ocawe 2026.07.01

Workflow automation as simple as taking notes

#+TITLE: Ocawe #+AUTHOR: Konrad Geletey #+DATE: 2026-06-10

Ocawe is a Crystal-first framework and runtime for Cawfile workflow bundles, agents, tools, skills, and OpenAI-compatible workflow execution.

Licenses: [[https://spdx.org/licenses/0BSD.html][0BSD]] (=LICENSE=).

It includes:

  • A production-oriented HTTP runtime server
  • A Svelte playground for workflows, tools, skills, and agent chat
  • VitePress docs with a custom =/playground/= route
  • Workspace Ownership

Ocawe owns the workflow runtime framework and example Cawfiles. Deployment library code, CI workflows, host modules, and Lefinepro production pipeline packaging live outside this repository, primarily in =sireng/= and the root =lefinepro= workspace.

  • Why Ocawe
  • Cawfile-first workflow framework with root-level multi-workflow bundles
  • =@[Service]= workflows that start with the runtime for tunnels, daemons, and watchers
  • Agent + skill + tool discovery from workflow directories
  • Voice and RAG patterns through workflow DSL
  • ACP execution for external agents through =exec ..., runtime: {acp: {...}}=
  • Schema validation and guardrails in agent/workflow execution
  • OpenAI-compatible chat completions, retrieval, and async task endpoints
  • Quickstart

Run with Nix:

#+begin_src bash git clone https://github.com/lefinepro/ocawe.git cd ocawe

nix run . -- --help nix run . -- up caws/01-simple #+end_src

Install with Nix:

#+begin_src bash nix profile install github:lefinepro/ocawe ocawe --help

pkg="$(dirname "$(dirname "$(readlink -f "$(command -v ocawe)")")")" tmp="$(mktemp -d)" cp -R "$pkg/share/ocawe/caws/12-api-nodes" "$tmp/" chmod -R u+w "$tmp/12-api-nodes"

ocawe up -d "$tmp/12-api-nodes" --port 4119 curl -fsS http://127.0.0.1:4119/v1/workflows kill "$(cat "$tmp/12-api-nodes/.ocawe.pid")" #+end_src

The Nix package includes the CLI/runtime dependencies used by the distribution, including Crystal build inputs, Git, Node.js, Ruby, SQLite, and Lua 5.4. It also installs example Cawfiles and helper scripts under =share/ocawe=. Installed commands use the packaged source, examples, and runtime binary automatically.

Install from GitHub Releases:

#+begin_src bash curl -fsSL https://github.com/lefinepro/ocawe/releases/latest/download/install.sh | bash export PATH="$HOME/.local/bin:$PATH" ocawe --help

pkg="$(dirname "$(dirname "$(readlink -f "$(command -v ocawe)")")")" tmp="$(mktemp -d)" cp -R "$pkg/share/ocawe/caws/12-api-nodes" "$tmp/" chmod -R u+w "$tmp/12-api-nodes"

ocawe up -d "$tmp/12-api-nodes" --port 4119 curl -fsS http://127.0.0.1:4119/v1/workflows kill "$(cat "$tmp/12-api-nodes/.ocawe.pid")" #+end_src

Install a specific release tag:

#+begin_src bash curl -fsSL https://github.com/lefinepro/ocawe/releases/download/2026.07.00/install.sh | OCAWE_VERSION=2026.07.00 bash #+end_src

The installer downloads =ocawe-linux-x86_64.tar.gz= into =OCAWE_INSTALL_DIR= (default =~/.local=), verifies both =bin/ocawe= and =bin/ocawecore=, and runs =ocawe --help=.

Install with mise from GitHub Releases:

#+begin_src bash mise use -g "github:lefinepro/ocawe[matching=ocawe-linux,bin_path=bin,filter_bins=ocawe]@latest" ocawe --help #+end_src

Declarative =mise.toml=:

#+begin_src toml [tools] "github:lefinepro/ocawe" = { version = "latest", matching = "ocawe-linux", bin_path = "bin", filter_bins = "ocawe" } #+end_src

Install CLI from source without Nix:

#+begin_src bash

Uses system Crystal if present; otherwise downloads Crystal into ./.tools/crystal.

source scripts/bootstrap-crystal.sh crystal build src/cli/main.cr -o build/ocawe ./build/ocawe --help #+end_src

Optional shell install:

#+begin_src bash mkdir -p ~/.local/bin ln -sf "$PWD/build/ocawe" ~/.local/bin/ocawe ocawe --help #+end_src

If =~/.local/bin= is not in =PATH=, add it in your shell profile.

The bootstrap script downloads a platform Crystal archive into =./.tools/crystal= only when =crystal= is missing.

You can control bootstrap with env vars:

  • =OCAWE_CRYSTAL_VERSION= (default =1.13.3=)
  • =OCAWE_CRYSTAL_BASE_URL= (default Crystal GitHub releases URL)
  • =OCAWE_CRYSTAL_FORCE_BOOTSTRAP=1= (force local toolchain even if system =crystal= exists)
  • =OCAWE_CRYSTAL_VERBOSE=1= (print toolchain version during bootstrap)

Run an example:

#+begin_src bash ocawe up caws/01-simple #+end_src

Pull a remote Cawfile workflow:

#+begin_src bash ocawe pull git+https://github.com/lefinepro/ocawe/caws/10-acp-agent ocawe pull git+ssh://github.com/lefinepro/ocawe/caws/10-acp-agent #+end_src

The runtime reads =Cawfile= from the selected workflow directory or the current directory. The port is configured there via =settings do port = 4111 end=.

** Declarative Nix

Use Ocawe as a flake input:

#+begin_src nix { inputs.ocawe.url = "github:lefinepro/ocawe";

outputs = { nixpkgs, ocawe, ... }: { nixosConfigurations.host = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; modules = [ ocawe.nixosModules.default { services.ocawe = { enable = true; port = 4111; }; } ]; }; }; } #+end_src

By default the NixOS module declaratively installs packaged examples by linking =services.ocawe.workflowsRoot= to =${package}/share/ocawe/caws= when the path does not already exist. Disable that with =services.ocawe.installExamples = false= when you manage the workflows directory yourself.

Use the overlay:

#+begin_src nix { nixpkgs.overlays = [ inputs.ocawe.overlays.default ]; environment.systemPackages = [ pkgs.ocawe ]; } #+end_src

Use with Home Manager:

#+begin_src nix { imports = [ inputs.ocawe.homeManagerModules.default ]; programs.ocawe.enable = true; } #+end_src

  • CLI Commands

** Build and Run

#+begin_src bash ocawe build --release ocawe dev --port 4111 ocawe up # reads port from Cawfile ocawe up caws/01-simple # run specific workflow ocawe up -d caws/01-simple # background mode #+end_src

** Workflow Triggers

#+begin_src bash

explicit workflow trigger by id

ocawe workflow solver task=deploy env=prod

agent/function/tool/skill/support triggers

ocawe agent code-reviewer --prompt "review this patch" ocawe tool project_healthcheck ocawe support onboarding-check

alias executable style (workflow id from executable name)

ln -sf ./build/ocawe /usr/local/bin/ocawe_example_workflow ocawe_example_workflow #+end_src

Workflow trigger CLI calls =POST /v1/triggers/workflows/:id= and sends:

  • =input.= from =key=value= args (values parsed as JSON when possible)
  • =input.args= from positional args without ===

Trigger command mapping:

  • =workflow= → =/v1/triggers/workflows/:id=
  • =agent= → =/v1/triggers/agents/:id=
  • =skill=/=support= → =/v1/triggers/skills/:id=
  • =function=/=tool= → =/v1/triggers/functions/:id=

Use =OCAWE_TRIGGER_BASE_URL= or =--base-url= to target a non-default runtime URL.

  • Runtime APIs

** Primary APIs

  • =GET /v1/workflows=
  • =POST /v1/workflows/:workflowId/runs=
  • =GET /v1/tools=
  • =GET /v1/skills=
  • =GET /v1/agents=
  • =POST /v1/agents/:agentId/generate=
  • =GET /v1/mcp/servers=
  • =POST /v1/mcp/servers=
  • =GET /v1/mcp/catalog=
  • =POST /mcp=

** Compatibility

  • =POST /v1/chat/completions=
  • =GET /v1/chat/completions/:completion_id=
  • =POST /v1/chat/completions/tasks=
  • =GET /v1/chat/completions/tasks/:taskId=

** Federation APIs

  • =POST /actors/{identifier}/inbox= (S2S inbound activities, signature-verified)
  • =GET /actors/{identifier}/outbox=
  • =POST /actors/{identifier}/outbox=
  • =GET /federation/metadata= (reported capabilities + supported FEPs from FEDERATION.md)
  • =GET /FEDERATION.md= (repo interoperability manifest)

S2S ticket ingestion mode:

  • follow remote actor and poll remote =outbox= for =Create(Ticket)= activities
  • require HTTP Signatures for federation requests
  • Project Structure

#+begin_example src/ cli/ # ocawe CLI (build/dev/up) framework/ # runtime framework + HTTP endpoints packages/ playground/ # Svelte playground (Vite + pnpm/Node.js) docs/ # VitePress docs and static playground route caws/ # workflow bundle examples 01-simple/ # minimal single agent 02-multi-agent/ # sequential agent pipeline 03-control-flow/ # if/else, parallel, while, until 04-rag-assistant/ # retrieval-augmented generation 05-voice-pipeline/ # voice transcription + synthesis 06-full-suite/ # all features combined, including container and service workflows 09-custom-agent/ # how to write custom agents 10-acp-agent/ # ACP-compatible external agent execution 11-git-https-pull/ # pull remote Cawfile bundles with git+https 12-api-nodes/ # HTTP API steps and step["name"] result access spec/ # Crystal specs #+end_example

  • Examples

See =caws/= directory for complete examples with =Cawfile= format.

Run any example:

#+begin_src bash ocawe up caws/01-simple ocawe up -d caws/02-multi-agent # background mode ocawe pull git+https://github.com/lefinepro/ocawe/caws/10-acp-agent #+end_src

Each example demonstrates a specific feature:

  • 01-simple — minimal single agent setup
  • 02-multi-agent — sequential agent pipeline
  • 03-control-flow — =unless=, =if/else=, =parallel=, =while=, =until=
  • 04-rag-assistant — RAG with vector store
  • 05-voice-pipeline — voice transcription and synthesis
  • 06-full-suite — all features combined, including container packaging and multiple =@[Service]= workflows
  • 09-custom-agent — writing custom agents with external binaries
  • 10-acp-agent — ACP-compatible external agent execution
  • 11-git-https-pull — pulling remote Cawfile bundles with =git+https=
  • 12-api-nodes — HTTP =get=, =post=, =put= steps with =step["name"]= result access
  • Cawfile Framework

Framework configuration uses =Cawfile= in the workflow root. A root =Cawfile= can define runtime settings, Crystal structs, annotations, and multiple workflow blocks. Directory-local =Cawfile= files are preferred for bundles; =*.acd.cr= files are still supported as legacy/single-workflow fallback.

Example =Cawfile=:

#+begin_src rcl settings do port = 4111 datasets.adapter = "sqlite" telemetry do enabled = true service_name = "ocawe" endpoint = "http://127.0.0.1:4318" end end

container do packages = ["git", "curl"] files = ["agents", "skills", "tools"] end

Federation is enabled by including Api::Federation types

struct Input include Api::Federation::Inbox end

struct Output include Api::Federation::Outbox end

@[Model("openai/gpt-4.1-mini")] @[Validate(Input, Output)] workflow "solver-codex" do follow ["@coder@example.com"] agent "codex-agent" end

@[Service] workflow "agent-tunnel" do exec "localtunnel", runtime: {shell: "bash"} end

workflow "codex-acp" do exec "codex", runtime: {acp: {command: "codex", args: ["--server"]}} end

workflow "remote-acp" do exec "github.com/lefinepro/ocawe/caws/10-acp-agent", runtime: {"git+https"} end

workflow "remote-acp-ssh" do exec "git+ssh://github.com/lefinepro/ocawe/caws/10-acp-agent/10-acp-agent", runtime: {"git+ssh"} end #+end_src

Key directives:

  • =settings do ... end= — runtime config (port, datasets adapter, federation, telemetry)
  • =@[Service]= — workflow starts with =ocawe up= and restarts on runtime reload
  • =@[Model("provider")]= — default model for the workflow
  • =container do ... end= — bundle-level container packaging; =packages= accepts Nix package names, flake refs, and path refs, while optional =files= selects what to copy into =/app=
  • =@[Validate(Input, Output)]= — input/output type validation
  • =exec "...", runtime: {acp: {...}}= — run an external ACP-compatible agent
  • =exec "github.com/org/repo/path", runtime: {"git+https"}= — clone or fast-forward pull a remote Cawfile reference over HTTPS, compile it into a cached single =ocawecore= binary, then run the selected workflow through that binary
  • =exec "git+https://github.com/org/repo/path", runtime: {"git+https"}= — explicit HTTPS form for the same remote Cawfile behavior
  • =exec "git+ssh://github.com/org/repo/path/workflow", runtime: {"git+ssh"}= — clone or fast-forward pull a remote Cawfile reference over SSH, compile it into a cached single =ocawecore= binary, then run the selected workflow through that binary

API types:

  • ="federation"= — ForgeFed-only mode (enabled when =Api::Federation::Inbox= or =Api::Federation::Outbox= is included)
  • ="classic"= — default runtime APIs (=/v1/*=)

OpenTelemetry: Set =telemetry.enabled = true= or a nested =telemetry do ... end= block to emit HTTP server spans, workflow run spans, node spans, outbound API/exec spans, workflow/HTTP metrics, and OTEL log records mirrored from workflow logger events. =OTEL_EXPORTER_OTLP_ENDPOINT=, =OTEL_EXPORTER_OTLP_HEADERS=, =OTEL_SERVICE_NAME=, and signal exporter env vars override Cawfile settings.

  • Writing Custom Agents

Agents are defined via markdown files with frontmatter:

#+begin_src yaml

name: "My Coder" description: "Custom agent" model: "openai/gpt-4.1-mini"

You are a helpful assistant. #+end_src

See =caws/09-custom-agent= for a complete example.

Installing external agents:

  • OpenCode: =npm install -g @anthropic/opencode=
  • Claude Code: =npm install -g @anthropic-ai/claude-code=
  • Qwen Code: =pip install qwen-code=
  • Development

** Run Playground

#+begin_src bash cd packages/playground pnpm install pnpm run dev #+end_src

** Run Docs

#+begin_src bash cd packages/docs pnpm install pnpm run dev #+end_src

** Development Tasks (mise)

#+begin_src bash mise install mise run cli-build mise run up mise run playground-build mise run docs-build #+end_src

The mise environment installs Crystal, Bun, and Lua 5.4 for local development.

** Testing

#+begin_src bash crystal spec cd packages/playground && pnpm run lint cd packages/docs && pnpm run build #+end_src

Repository

ocawe

Owner
Statistic
  • 11
  • 3
  • 0
  • 1
  • 8
  • 1 day ago
  • February 15, 2026
License

BSD Zero Clause License

Links
Synced at

Fri, 17 Jul 2026 07:21:35 GMT

Languages