Ocawe is a Crystal-first framework and runtime for Cawfile workflow bundles, agents, tools, skills, and OpenAI-compatible workflow execution.
Licenses: 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
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.
- 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
Run with Nix:
git clone https://github.com/lefinepro/ocawe.git
cd ocawe
nix run . -- --help
nix run . -- up caws/01-simpleInstall with Nix:
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")"The Nix package includes only the CLI/runtime dependencies used by the
distribution, including Git, OpenSSH, SQLite, and zstd. Provider CLIs and their
language runtimes are supplied separately when a workflow explicitly needs
them. The package 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:
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")"Install a specific release tag:
curl -fsSL https://github.com/lefinepro/ocawe/releases/download/2026.07.00/install.sh | OCAWE_VERSION=2026.07.00 bashThe 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:
mise use -g "github:lefinepro/ocawe[matching=ocawe-linux,bin_path=bin,filter_bins=ocawe]@latest"
ocawe --helpDeclarative mise.toml:
[tools]
"github:lefinepro/ocawe" = { version = "latest", matching = "ocawe-linux", bin_path = "bin", filter_bins = "ocawe" }Build the CLI from source with Nix:
nix build .#ocawe
./result/bin/ocawe --helpOptional shell install:
mkdir -p ~/.local/bin
ln -sf "$PWD/result/bin/ocawe" ~/.local/bin/ocawe
ocawe --helpIf ~/.local/bin is not in PATH, add it in your shell profile.
Nix provides the pinned Crystal toolchain, shards, and native build dependencies used by the flake package.
Run an example:
ocawe up caws/01-simplePull a remote Cawfile workflow:
ocawe pull git+https://github.com/lefinepro/ocawe/caws/10-acp-agent
ocawe pull git+ssh://github.com/lefinepro/ocawe/caws/10-acp-agentThe runtime reads Cawfile from the selected workflow directory or the current
directory. The port is configured there via settings do port = 4111 end.
Use Ocawe as a flake input:
{
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;
};
}
];
};
};
}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:
{
nixpkgs.overlays = [ inputs.ocawe.overlays.default ];
environment.systemPackages = [ pkgs.ocawe ];
}Use with Home Manager:
{
imports = [ inputs.ocawe.homeManagerModules.default ];
programs.ocawe.enable = true;
}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 modeBuild a Sireng pipeline on a remote host. Ocawe transfers only the selected
pipeline and the Ocawe source needed by its runtime, then uses the remote
host’s first working nerdctl, docker, or podman manager. The remote build
produces an immutable image and a matching <service>-runtime binary under the
host runtime store. Repeating the same command reuses the content-addressed
build.
ocawe build planner --remote build-host
ocawe build --remote build-host .
ocawe build planner --remote build-host --manager nerdctl
ocawe build planner --remote build-host --dry-runThe remote build never changes the active runtime until the image and binary have both completed successfully. Deploy the promoted version separately:
cd sireng
nix run .#deploy -- planner
# or, from the Sireng development environment:
sireng deploy planner
sireng deploy planner --version <source-hash>For the one-command remote workflow path, use up. It resolves the target
ActivityPub profile, packages the selected project as project.tar.zst, sends
it as a ForgeFed task, and lets that profile unpack and run the included
Cawfile on its server. The path defaults to the current directory:
nix run github:lefinepro/ocawe -- up --remote @handle@lefine.pro .The command returns the target profile and ActivityPub task id:
Profile: https://lefine.pro/actors/handle Task: https://lefine.pro/actors/ocawe-device/activities/ocawe-task-... Status: accepted (202)
The Cawfile and a local device code are saved with mode 0600 in
.ocawe/remote-task.json after delivery. Remote up uses WebFinger and
ActivityPub; it does not use SSH or create a Kubernetes namespace. The archive
excludes .git, .env*, private-key files, dependency caches, and build
output. Use --dry-run to inspect the task and archive size without sending it.
# 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_workflowWorkflow trigger CLI calls POST /v1/triggers/workflows/:id and sends:
input.<key>fromkey=valueargs (values parsed as JSON when possible)input.argsfrom positional args without ===
Trigger command mapping:
workflow→/v1/triggers/workflows/:idagent→/v1/triggers/agents/:idskill=/=support→/v1/triggers/skills/:idfunction=/=tool→/v1/triggers/functions/:id
Use OCAWE_TRIGGER_BASE_URL or --base-url to target a non-default runtime URL.
GET /v1/workflowsPOST /v1/workflows/:workflowId/runsGET /v1/toolsGET /v1/skillsGET /v1/agentsPOST /v1/agents/:agentId/generateGET /v1/mcp/serversPOST /v1/mcp/serversGET /v1/mcp/catalogPOST /mcp
POST /v1/chat/completionsGET /v1/chat/completions/:completion_idPOST /v1/chat/completions/tasksGET /v1/chat/completions/tasks/:taskId
POST /actors/{identifier}/inbox(S2S inbound activities, signature-verified)GET /actors/{identifier}/outboxPOST /actors/{identifier}/outboxGET /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
outboxforCreate(Ticket)activities - require HTTP Signatures for federation requests
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
See caws/ directory for complete examples with Cawfile format.
Run any example:
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-agentEach 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,putsteps withstep["name"]result access
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:
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
Key directives:
settings do ... end— runtime config (port, datasets adapter, federation, telemetry)@[Service]— workflow starts withocawe upand restarts on runtime reloadocawe start PATH— optimized Cawfile runtime with the minimal/run,/stop/:id, and/metricsAPIocawe stop PATH— stops the container or local server started byocawe start@[Model("provider")]— default model for the workflowcontainer do ... end— bundle-level container packaging;packagesaccepts Nix package names, flake refs, and path refs, while optionalfilesselects what to copy into/app@[Validate(Input, Output)]— input/output type validationexec "...", runtime: {acp: {...}}— run an external ACP-compatible agentexec "github.com/org/repo/path", runtime: {"git+https"}— clone or fast-forward pull a remote Cawfile reference over HTTPS, compile it into a cached singleocawecorebinary, then run the selected workflow through that binaryexec "git+https://github.com/org/repo/path", runtime: {"git+https"}— explicit HTTPS form for the same remote Cawfile behaviorexec "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 singleocawecorebinary, then run the selected workflow through that binary
API types:
"federation"— ForgeFed-only mode (enabled whenApi::Federation::InboxorApi::Federation::Outboxis 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.
Agents are defined via markdown files with frontmatter:
---
name: "My Coder"
description: "Custom agent"
model: "openai/gpt-4.1-mini"
---
You are a helpful assistant.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
cd packages/playground
pnpm install
pnpm run devcd packages/docs
pnpm install
pnpm run devmise install
mise run cli-build
mise run up
mise run playground-build
mise run docs-buildThe mise environment installs Crystal and Bun for local development.
crystal spec
cd packages/playground && pnpm run lint
cd packages/docs && pnpm run build