Using SubZeroClaw
Read the warning first. SubZeroClaw runs whatever the model emits through
popen() — no confirmation, no sandbox, rm -rf / included. There is nothing
between the model's output and your system. Run it only where you accept that,
ideally as an unprivileged service user (see As a service).
The whole runtime is: read a skill (markdown) → call an LLM → run shell tools →
loop until done. One tool: shell. The "adapter" for git/curl/email/ffmpeg is
that they're one popen() away — install the CLI, the model uses it.
Two senses of "skill" — don't conflate them. A SubZeroClaw skill is a plain
.mdfile in~/.subzeroclaw/skills/that becomes the agent's system prompt (what the agent knows). This file is a Claude Code skill (how to operate the tool). When the docs below say "write a skill", they mean the former.
Build & run
git clone https://github.com/genlayerlabs/subzeroclaw && cd subzeroclaw
make # ~0.5s → the 55KB binary (needs gcc; curl at runtime)
mkdir -p ~/.subzeroclaw/skills
# ...write ~/.subzeroclaw/config (below) and a skill .md...
./subzeroclaw "check disk usage and clean tmp if over 80%" # one-shot
./subzeroclaw # interactive
make install # → ~/.local/bin/
An agent skill is just prose the LLM reads — no format, no registry, no trigger
matching. Drop a .md in ~/.subzeroclaw/skills/ and it joins the system
prompt. The repo's skills/*.md are format examples tied to one setup; write
your own.
Configuration (~/.subzeroclaw/config)
These are the only keys the runtime parses (config_parse_line); config.example
is the canonical template.
| Key | Meaning |
|---|---|
api_key |
The unhardcoded router consumer key (llmr_…) — SubZeroClaw is designed to run on the router. A bare OpenRouter/provider key also works but is a degraded loop. Required. |
request_extra |
JSON merged into every request body. Carries the model ({"model":"..."}) — there is no dedicated model key. Against a router it also carries the routing policy_ir. Override wins on key collision. |
compact_extra |
JSON enabling async compaction: keep_recent + a cheap summariser policy_ir. Unset → no compaction. |
endpoint |
default OpenRouter; point at an unhardcoded router for routing/cache/compaction. |
skills_dir, log_dir |
defaults under ~/.subzeroclaw/. |
max_turns |
tool-call loops per input (default 200). |
Env vars SUBZEROCLAW_API_KEY, SUBZEROCLAW_ENDPOINT, SUBZEROCLAW_REQUEST_EXTRA,
SUBZEROCLAW_COMPACT_EXTRA override the file.
The unhardcoded substrate (routing + compaction)
Routing with prompt-cache affinity and context compaction are not part of
"skill + LLM + shell + loop" — they live in the substrate. Point endpoint at an
unhardcoded router and express them
as JSON:
- Routing:
request_extra = {"model":"policy:auto","policy_ir":[ … ]}. The runtime sends a per-runsessionid so the router keeps the conversation on its cache-hot peer. (Author thepolicy_irwith theunhardcoded-useskill.) - Compaction: on an
x_router.compactsignal, the runtime fires an append-only seal at the router's/v1/compactin the background and keeps taking turns, splicing the sealed block in ahead of the turns that arrived meanwhile — no pause, the cache prefix is never rewritten.compact_extracarrieskeep_recent+ the summariser policy.
Pointed at a bare provider the HTTP call still fires, but it's a degraded loop (no routing, cache, or compaction) — not a supported standalone mode.
As a service (and credential isolation)
SubZeroClaw doesn't supervise itself — bring your init system. A systemd unit with
Restart=on-failure and an EnvironmentFile (root-owned, chmod 600) holding
SUBZEROCLAW_API_KEY gets you restart + credential isolation. The runtime
scrubs the four provider secrets (SUBZEROCLAW_API_KEY, _ENDPOINT,
_REQUEST_EXTRA, _COMPACT_EXTRA) from its own environment right after
config_load — wiping the value in place, then unsetenv — so the shell it hands
the model never inherits the key (cat /proc/self/environ comes up empty).
Non-secret vars like SUBZEROCLAW_SKILLS are deliberately kept (the genswarms
wrapper sets it). The supervisor owns the secret; the shell stays unguarded by
design — the scrub only keeps the runtime's own secrets out of it.
Gotchas
- A top-level
model =line in the config is ignored — the runtime never parses it. The model rides inrequest_extra. (This is the most common misconfiguration.) - Use a tool-calling model. If the model can't call tools it will loop without
progress. Set it in
request_extra. curlis required at runtime (API calls shell out to it);gcconly to build.- Compaction is opt-in: unset
compact_extra→ context grows until the provider refuses it.
Where to read more
README.md— the full config reference, session logging, troubleshooting table.config.example— the canonical config template.- The
unhardcoded-use/sigma-policy-authorskills — author the routing and summariserpolicy_irthis runtime sends.