Docs
Lookup for the Nix options and the rig commands. The first session is on Getting started. Webhook HTTP is on Triggers. Coding agents should fetch /llms.txt then the matching /raw/ page. Retrieval rules are on For agents.
A workspace is a flake that calls rig.lib.workspace. rig new workspace writes one. Build, catalog lists, and rig add need it. Pulled images do not. The longer write path is Create an agent.
If you want
Pull and run
Install, pull agency-tester, open the session.
Write a layer
packages, files, skills, sandbox, hooks.
Write and compose
rig new, rig add, then compose, build, push.
Compose an agent
name and a compose list of layer modules.
Keep secrets out of the image
SecretSpec keys. Injected at start.
Default-deny network
Grant domains, CIDRs, write paths.
HTTP in
POST /v1/agents/:agent/:route.
Same image elsewhere
rig pull and rig push.
Write Nix from an agent
Fetch raw markdown. Do not invent fields.
Word meanings
layer, compose, session, attach, workspace.
Glossary
| Word | Meaning |
|---|---|
| add | rig add. Pull a published layer and add its flake input to the workspace. Does not edit agent files. |
| agent | A name and a compose list of layer modules. Composed into one image. |
| attach | rig attach. Stdin and stdout on the session agent process. Disconnect does not stop the agent. |
| compose | Layer modules in order. Later entries override on conflict. If a layer named base exists, you do not list it. |
| harness | OpenCode or Claude Code, generated from the same composed agent. |
| image | A published agent or layer at a host, name, and tag. |
| layer | One reusable slice. The file is layers/<name>/default.nix, or a published flake with a layer output. |
| sandbox | Default deny. Layers grant domains, CIDRs, and write paths. |
| secretspec | Secret keys declared on layers. The image holds the manifest, not the tokens. |
| session | A running agent instance with its own root and sandbox. |
| trigger | A webhook subscription. Provider plus kind, then exactly one of prompt or handler. |
| workspace | A flake that calls rig.lib.workspace. rig new workspace writes one. Build, catalog lists, and rig add need it. Pulled images do not. |
Raw: /raw/glossary.md.
agent
An agent is a name and a compose list of layer modules. The file lives at agents/<name>/default.nix. Do not list base. Eval prepends it.
{ layers, ... }:
{
name = "writer";
description = "What this agent is for.";
compose = [
layers.notes
layers.git
];
}
| Field | Type | Default | Meaning |
|---|---|---|---|
| name | string | default |
Agent identity. |
| description | string | empty | What this agent is for. |
| avatar | string | empty | Public HTTPS URL. Empty falls back to assets/avatars/avatar-<name>.png or a generated SVG. |
| compose | list of module | [] |
Layer modules in order. Later entries override on conflict. |
| dir | string | .agent |
Output directory for materialized artifacts. |
| triggers | attrs of trigger | {} |
Agent-level triggers. Applied after the layer compose. Later name wins. |
| disableNativeTools | bool | false |
Deny built-in harness tools and keep collected layer tools. ORed with any layer that sets it. |
If a layer named base exists, the resolved compose list is base plus compose. You do not list base.
Invariants
- If a layer named
baseexists, you do not list it incompose. disableNativeToolsis ORed with any layer that sets it.
Failure modes
- A cycle in layer
composefails eval. - A missing
layers.gitfails eval withlayer 'git' is not in this workspace. Add it withrig add git@latestorrig new layer git. - Two providers on one agent may not share a route path. See Triggers.
How to check
Needs a workspace whose flake calls rig.lib.workspace. rig inspect writer prints a block that starts with === agent: writer === and includes compose (declared) and compose (resolved).
Raw: /raw/agent.md. How-to: /raw/create-agent.md.
Layer
One reusable slice. The file is layers/<name>/default.nix. The name is the directory or the rig add input. The file is the module body.
{ pkgs, ... }:
{
description = "What this slice is for.";
packages = [ pkgs.jq ];
files."AGENTS.md" = {
source = ./AGENTS.md;
mergeable = true;
};
}
A layer that needs another layer lists modules, not strings:
{ layers, pkgs, ... }:
{
description = "GitLab CLI and forge conventions.";
compose = [ layers.git ];
packages = [ pkgs.glab ];
}
compose = [ layers.git ] fails eval unless git is a workspace input or a local layer. rig add does not add those dependencies for you.
| Field | Type | Default | Meaning |
|---|---|---|---|
| description | string | empty | Human-readable description. |
| image | null or string | null |
Canonical published image as host/name:tag. |
| compose | list of module | [] |
Layer modules composed before this one. A cycle fails eval. |
| packages | list of package | [] |
Nix packages on PATH in the session root. Unioned in compose order. |
| skills | list of skill | [] |
Native skills. Later compose entries win on the same skill name. First-seen order is kept. |
| models | submodule | empty | default and providers.<id>. Later compose entries override the same provider id and a non-null default. |
| mcp | attrs | {} |
stdio servers. Later compose entries win on the same server name. |
| services | attrs of service | {} |
Session processes. Later compose entries win on the same name. See Services. |
| skillLinks | list of string | [] |
Relative paths under the agent root. Each path becomes a symlink to skills/. |
| secrets | attrs of secret | {} |
SecretSpec keys. See Secrets. |
| privileged | list of string | [] |
Process ids such as harness:, service:, mcp: that get real secret env. Only agent.layers.secretGuard.privileged is consumed. Other layers append. lib.mkForce replaces. |
| sandbox.network.allow | list of attr | [] |
Egress allows. Each item has exactly one of domain, ip, or cidr. Default deny. |
| sandbox.network.deny | list of attr | [] |
Egress denies. Applied after allows. A deny removes a matching allow key. |
| sandbox.fs.writeAllow | list of string | [] |
Filesystem write allow prefixes. Default deny. |
| sandbox.fs.writeDeny | list of string | [] |
Filesystem write denies. A deny removes a matching allow path. |
| files | attrs | {} |
Files in the agent home. Each value has source path and mergeable bool, default false. Same path with different bytes fails the build unless the path is mergeable. AGENTS.md is mergeable even when unset. Mergeable files concatenate with a <!-- rig:layer:<name> --> marker. Paths under triggers/ are rejected. |
| hooks | list | [] |
id plus command. Concatenated in compose order. Each harness maps the ids it knows. |
| tools | attrs | {} |
Harness tools. Attr key is the tool name and must match [a-zA-Z0-9_-]+. Each value has description and a TypeScript source path. Later compose entries win on the same name. The opencode layer writes .opencode/tools/<name>.ts. |
| disableNativeTools | bool | false |
Deny built-in harness tools. ORed across the compose list and the agent. |
| triggerProvider | null or submodule | null |
Webhook ingress. See Triggers. |
| triggers | attrs of trigger | {} |
Triggers this layer contributes. Later compose entries win on the same name. |
| scripts | attrs | {} |
devenv scripts.<name>.exec when this layer is composed. |
| module | deferred module | empty | Extra devenv module. Not auto-imported. |
Sandbox
Default deny. Layers grant domains, CIDRs, and write paths. No grant, no access. Each network allow or deny item has exactly one of domain, ip, or cidr.
Invariants
AGENTS.mdis mergeable even whenmergeableis unset.- Paths under
triggers/infilesare rejected. - Tool names must match
[a-zA-Z0-9_-]+. revandhashare required whenrepois set.
Failure modes
- A cycle in
composefails eval. - Same
filespath with different bytes fails the build unless the path is mergeable. - A changed skill tree fails the build when
hashno longer matches. - Later compose entries win on the same skill name, MCP server name, service id, and trigger name.
- Only the last composed service that sets
workkeeps it.
How to check
Needs a workspace whose flake calls rig.lib.workspace. rig inspect <name> prints === layer: <name> === or === agent: <name> ===.
Raw: /raw/layer.md. How-to: /raw/create-agent.md.
Skill
| Field | Type | Default | Meaning |
|---|---|---|---|
| name | string | required | Directory and display name under skills/<name>/. |
| path | null or path | null |
Local SKILL.md or skill directory. |
| repo | null or string | null |
owner/repo or https://github.com/owner/repo. Built with fetchFromGitHub. |
| skill | null or string | null |
Subdirectory in the repo, skills/<name> or <name>. |
| rev | null or string | null |
Git revision. Required when repo is set. |
| hash | null or string | null |
SRI hash. Required when repo is set. A changed tree fails the build. |
Models
| Field | Type | Default | Meaning |
|---|---|---|---|
| default | null or string | null |
Default model id. |
| providers.<id>.name | string | the attr key | Display name. |
| providers.<id>.kind | enum | openai-compatible |
Only openai-compatible. |
| providers.<id>.baseURL | null or string | null |
Provider base URL. |
| providers.<id>.apiKeyEnv | null or string | null |
Environment variable that holds the API key. |
| providers.<id>.models.<id>.name | string | required | Display name for that model id. |
MCP
| Field | Type | Default | Meaning |
|---|---|---|---|
| command | string | required | Executable that speaks MCP over stdio. |
| args | list of string | [] |
Arguments. |
| secretsScope | null or string | null |
secretspec scope for the process wrapper. |
| env | attrs of string | {} |
Extra environment. |
| description | string | empty | Human-readable description. |
Services
The attr is services.<id> on a layer. Later compose entries win on the same <id>. Only the last composed service that sets work keeps it. Earlier work is dropped. A non-empty collected set writes services.json. A collected work also writes harness.json with mode set to service. The runtime starts each command inside the session root. The process id is service:<id>.
| Field | Type | Default | Meaning |
|---|---|---|---|
| command | list of string | required | argv started inside the session root. |
| env | attrs of string | {} |
Extra environment. |
| ready | null or submodule | null |
Optional TCP wait before the session is ready. |
| ready.tcp | string | required when ready is set | Listen address as host:port. |
| ready.timeoutMs | positive integer | 60000 |
Milliseconds to wait for ready.tcp. |
| work | null or submodule | null |
How session prompts reach this process. |
| work.kind | enum | required when work is set | acp or http. |
| work.url | null or string | null |
Required when kind is http. |
work.kind = "http" posts the prompt to work.url. work.kind = "acp" sends the prompt over the child stdin and stdout.
Triggers
A trigger subscribes to a provider kind. Exactly one of prompt or handler must be set. Eval throws otherwise. A trigger whose provider or kind is missing from the collected providers fails eval. Two providers on one agent may not share a route path. HTTP is on Triggers.
triggerProvider = {
id = "hooks";
auth = {
env = "WEBHOOK_SECRET";
header = "x-webhook-token";
};
routes = [{ path = "webhooks/hooks/:trigger"; }];
kinds.ping = {
match.headers."x-event" = "ping";
};
};
agent.triggers.hello = {
provider = "hooks";
kind = "ping";
prompt = ./prompts/hello.md;
};
triggerProvider
| Field | Type | Default | Meaning |
|---|---|---|---|
| id | string | required | Must match [a-z0-9][a-z0-9-]*. Triggers set provider to this id. |
| auth.env | string | required | SecretSpec key name for the token or HMAC secret. The value is not stored in the image. |
| auth.header | null or string | null |
Shared-token header name. At least one of header or hmac is required. |
| auth.hmac.header | string | required when hmac is set | Signature header name. |
| auth.hmac.algorithm | enum | sha256 |
Only sha256. |
| auth.hmac.prefix | string | empty | Prefix stripped from the header before compare. |
| auth.hmac.message | string | {{body}} |
Mustache over headers, query, payload, and body. |
| routes[].path | string | required | Suffix under /v1/agents/:agent/. |
| kinds.<id>.match.headers | attrs | {} |
Header name to string or list of strings. Names are case-insensitive. |
| kinds.<id>.match.payload | attrs | {} |
JSON path to string or list of strings. |
| kinds.<id>.dedup.header | null or string | null |
Dedup key from a request header. |
| kinds.<id>.dedup.payload | null or string | null |
Dedup key from a JSON path when the header is absent. |
| kinds.<id>.respond.contentType | string | text/plain |
Immediate response Content-Type. Stops trigger dispatch. |
| kinds.<id>.respond.body | string | required when respond is set | Mustache response body. |
trigger
| Field | Type | Default | Meaning |
|---|---|---|---|
| provider | string | required | Provider id from a triggerProvider on the stack. |
| kind | string | required | Kind id on that provider. |
| prompt | null or path | null |
Mustache prompt file. Exactly one of prompt or handler. |
| handler | null or path | null |
TypeScript file. The runtime runs bun <file> for 10 seconds. Stdin is JSON { headers, query, payload, body }. |
| session | null or string | null |
Mustache session name over payload, headers, query, and body. Empty or null uses the image name. |
Mustache does not HTML-escape. Missing paths render empty. Triggers that share a provider and kind must agree on a non-empty session template.
Invariants
- Exactly one of
promptorhandler. - At least one of
auth.headerorhmac. triggerProvider.idmatches[a-z0-9][a-z0-9-]*.- Two providers on one agent may not share a route path.
Failure modes
- Both
promptandhandlerset, or neither, throws at eval. - Missing provider or kind on the collected providers fails eval.
- Missing
auth.envat request time is 503. HTTP is on Triggers.
Raw: /raw/triggers.md.
Secrets
Layers declare keys. The image holds the manifest, not the tokens.
secrets.API_TOKEN = {
description = "What this key is for";
required = true;
secret = true;
};
secrets.API_HOST = {
description = "Non-secret default";
required = false;
default = "example.com";
};
| Field | Type | Default | Meaning |
|---|---|---|---|
| description | string | required | Human-readable purpose. |
| required | bool | false |
Required on the layer default profile. |
| default | null or string | null |
Optional default. Use for non-secret config such as a host. |
| secret | bool | false |
When the secretGuard layer is composed, unprivileged processes see a fake. Set true only when that layer is in the compose list. |
rig secrets <agent> runs secretspec check against the catalog agent. Catalog session create refuses when required keys are empty. Session processes start with a cleared environment. RIG_BWRAP_KEEP_ENV=1 keeps the host environment. rig shell --raw-secrets skips the secret wrap.
Invariants
- The image holds the manifest, not the tokens.
- Session processes start with a cleared environment.
Failure modes
- Catalog
session createrefuses when required keys are empty. - Pulled images skip that CLI check.
How to check
Needs a workspace. rig secrets <agent> runs secretspec check.
Raw: /raw/secrets.md.
rig pull
Download a published agent or layer into the local dest store.
rig pull <image|ref>
image|ref is one of:
https://host/agents/name@tagorhttps://host/layers/name@tags3://bucket[/prefix]/agents/name@taghost/name:tagname@tag, which walkshttps://repo.slopkit.devthenrepositoriesin user config
Needs no workspace. Progress lines go to stderr when stderr is not a TTY as rig: …. The last stdout line is:
pulled https://repo.slopkit.dev/agents/agency-tester@latest storePath=/nix/store/7j5lhfm26sg9vkp2vdbcxvmh9l1h09sw-kit-agent-agency-tester-root cache=https://repo.slopkit.dev/cache
A layer with no session root prints pulled … (no session root) cache=…. Missing argument exits 1 with error: pull requires an image or layer ref.
rig push
Publish a local agent or layer to an image repo.
rig push <image> [path]
image is required. path is an optional agent or layer directory. If you omit it, Rig searches the current working directory. Needs a workspace or that path, and secretspec credentials for scopes.s3.
rig publish
Build NARs and upload to S3.
rig publish <image> [--system SYSTEM] rig publish --all <repo> [--tag TAG]… [--system SYSTEM]
Needs a workspace. --system selects a pkgsCross output such as aarch64-linux. It does not run a foreign ELF. --all publishes every catalog layer, then every agent, once. Extra --tag values are written only when that image's store path changed versus @latest.
Stdout is one line per image:
published <website> storePath=<path> cache=<url> uploaded=<n> layers=<names>
rig agent
rig agent list rig agent remove <name|image> [--force] [--runtime URL]
list prints pulled agents from $RIG_DATA_DIR/rigdb.sqlite. Empty inventory prints No pulled agents. A pulled row looks like:
Pulled agents: agency-tester latest compose: base flue git unslop agency chromium agent-browser osmani-browser
remove deletes a pulled agent and unused layer store paths. It refuses if a session uses the image, or if the runtime is down, unless --force.
rig layer
rig layer list rig layer remove <name|image> [--force] [--runtime URL]
Same inventory and remove rules for pulled layers. Empty inventory prints No pulled layers.
rig new
Create an empty workspace, or scaffold a layer or agent module.
rig new workspace [directory] rig new layer <name> rig new agent <name>
rig new workspace writes flake.nix that calls rig.lib.workspace. Default directory is rig-workspace.
created workspace → rig-workspace
A destination that exists and is not empty exits 1 with exists and is not empty. Creating a layer or agent outside a workspace initializes the current empty directory first.
created layer notes → layers/notes created agent writer → agents/writer
The layer file is the module body. The agent scaffold lists every local layers/*/default.nix name. It does not list flake inputs.
rig add
Pull a published layer and add its flake input to the workspace. Does not edit agent files. Does not add layers named in that layer's own compose list.
rig add <layer|image>
Needs a workspace. Match:
added layer git → https://repo.slopkit.dev/layers/git@latest/flake.tar.gz
The card must include a flake archive. If it does not, add exits with layer '<name>' has no flake archive; rig pull still works for run. Missing argument exits 1 with error: rig add requires a published layer ref.
rig build
Materialize a catalog agent to .agent.
rig build <agent> [--harness opencode|claude] [--system SYSTEM]
Needs a workspace. That is nix build .#agents-<agent> -o .agent. Cross is .#cross.<system>.agents-<agent>. --harness checks that harness's files exist. It does not build a different derivation. Unknown harness exits 1. Missing name exits 1 with error: build requires an agent name.
Stdout starts with:
build notes: base → notes (flake .#agents-notes)
Then a materialized line with outDir, storePath, and counts for files, skills, and MCP servers.
rig list-agents
List agents from the local flake catalog. Alias: rig agents. Needs a workspace. Pulled inventory is rig agent list.
Agents: <name> compose: <layer names>
rig list-layers
List layers from the local flake catalog. Alias: rig layers. Needs a workspace. Pulled inventory is rig layer list.
Layers: <name> <description>
rig inspect
rig inspect <name|path>
Print evaluated metadata for a local or pulled layer or agent. A workspace name looks up the catalog. A path such as ./layers/notes works without a catalog name. An agent block starts with === agent: <name> === and includes compose (declared) and compose (resolved). A layer block starts with === layer: <name> ===. Missing name exits 1 with error: inspect requires a layer or agent name.
rig secrets
rig secrets <agent>
Run secretspec check for a catalog agent manifest. Needs a workspace.
rig card serve
rig card serve [--port N]
Preview repo, agent, and layer cards from a workspace. Default port is 43191. Cards are written under .rig/cards and served at 127.0.0.1.
rig session
rig session create --agent <name|image> [options] rig session list [--runtime URL] rig session get <name|id> [--runtime URL] rig session suspend <name|id> [--runtime URL] rig session resume <name|id> [--runtime URL]
--agent is a pulled name from rig agent list, an image URL, or a local catalog name. Catalog create runs a required-secret check first. Pulled images skip that CLI check. Create starts a local runtime when the target URL is this machine. --runtime targets a remote runtime and skips the local start.
| Option | Meaning |
|---|---|
| --agent | Required on create. Pulled name, image URL, or catalog name. |
| --name | Session name. Default is the image leaf, then -alpha through -zulu, then -alpha-alpha. |
| --prompt | Initial prompt. Delivered only when the image wrote harness.json. Prompts on one session run one at a time, in order. |
| --workdir-host | Bind a host path. The path must exist. Exactly one workdir kind is allowed. |
| --workdir-clone | Git URL. Schemes are http, https, ssh, git, git+ssh, file, or user@host:path. A URL that starts with - is rejected. |
| --workdir-ref | Clone ref. Requires --workdir-clone. A ref that starts with - is rejected. |
| --workdir-persistent | Named directory under $RIG_DATA_DIR/workdirs/<name>. |
| --backend | nix or firecracker. |
| --runtime | Runtime base URL for this command. |
If you omit a workdir, the runtime uses a persistent directory named after the session id. Create prints the session name, status, page URL, agent, backend, and id. The URL may include a one-time login query. list with no sessions prints (no sessions). get, suspend, and resume print JSON. Stopped, failed, or suspended sessions refuse shell. Resume first.
On disk the runtime keeps $RIG_DATA_DIR/sessions/<id>/ with home/, workdir/, root-path, and meta.json. Inside the root, $HOME is /home/agent and cwd is /home/agent/workdir.
rig attach
rig attach <name|id> [--runtime URL]
Attach stdin and stdout to the session agent process over WSS. Disconnect does not stop the agent. Missing name exits 1. A non-TTY prints attach info as JSON and a hint to run in a TTY. No harness.json means the runtime skips the harness process.
rig shell
rig shell <name|id> [--script 'cmd;…'] [--runtime URL] [--raw-secrets]
Bash in the session root. The runtime runs bwrap. --script splits on ;, runs those commands, then exit, and writes the output to stdout. Timeout for script mode is 15 seconds. --raw-secrets skips the secret wrap. A non-TTY without --script prints shell info as JSON.
rig runtime
rig runtime start [--port N] [--foreground|-f] rig runtime restart|stop|status rig runtime login <url> [--name <n>] [--token <t>] rig runtime switch [name] rig runtime list
Start the local runtime, or save and switch remotes. Does not need a workspace. TLS plus token dev. Default URL is https://127.0.0.1:7443. Start in the background prints runtime started (background) → <url> or that it was already up. --foreground keeps it on this terminal. Local listen details land in ~/.config/rig/runtime.json.
login saves the URL and makes it current. It needs --token or RIG_RUNTIME_TOKEN. --name labels it. Default name is the URL hostname. Saved remotes land in ~/.config/rig/runtimes.json. Login prints rig: runtime <name> → <url> (current) on stderr. switch with no name opens a picker. A named switch prints rig: current runtime: <name> (<url>). list marks the current entry with *. Empty list prints (no runtimes). rig supervisor exits 1 with unknown command: supervisor. --supervisor exits 1 with Unknown option '--supervisor'.
config.toml
User config is ~/.config/rig/config.toml, or $XDG_CONFIG_HOME/rig/config.toml. Override the file with RIG_USER_CONFIG.
repositories = [ "https://repo.slopkit.dev", "s3://my-rig-cache", ]
If the file is missing, or repositories is empty, Rig uses https://repo.slopkit.dev. Project rig.toml is named flake registries only. It does not replace this list.
Environment
| Name | Meaning |
|---|---|
| RIG_USER_CONFIG | Exact path to the user TOML file. |
| RIG_CONFIG_DIR | Directory that holds config.toml, runtime.json, and runtimes.json. |
| RIG_ROOT | Workspace root for build, inspect, and catalog lists. |
| RIG_RUNTIME_URL | Runtime base URL. When unset, the current runtimes.json entry, then runtime.json, then https://127.0.0.1:7443. |
| RIG_RUNTIME_TOKEN | Bearer token. Default dev. |
| RIG_RUNTIME_INSECURE_TLS | 1 or true accepts a self-signed cert. 0 or false turns that off. Localhost is insecure unless turned off. |
| RIG_DATA_DIR | Store, sessions, SQLite. Default ~/.local/share/rig. |
| RIG_BWRAP | bubblewrap binary for session enter. |
| RIG_BWRAP_KEEP_ENV | 1 keeps the host environment inside bwrap. |
| RIG_VERSION | Installer package version. Default latest. |
Each RIG_* name above that has a KIT_* twin also reads the KIT_* name as a fallback.
rig version
rig version rig --version rig -V
Prints three lines: version:, built:, and commit:. Getting started records 0.4.36.
rig help prints the full usage. rig <cmd> --help prints one command.
rig update
rig update
Downloads and runs the latest install.sh. Same installer as Getting started.
rig config analytics
rig config analytics [on|off]
Anonymous telemetry is on by default. It records sanitized command, pull, runtime, and session aggregates. It does not record prompts, responses, raw argv, URLs, tokens, paths, or environment variables. Local data is $XDG_DATA_HOME/rig/analytics.duckdb, default ~/.local/share/rig/analytics.duckdb. Dashboard: analytics.slopkit.dev.