Agent-box & Portal documentation
Welcome. This site documents two related projects:
- Agent-box: workspace + container orchestration for autonomous coding agents
- Portal: host capability mediation service (Unix socket + MessagePack), usable standalone
If you are new, start with Choose your path.
Demo
(Click images to open in lightbox. Diagrams use mdbook-excalidraw with built-in zoom/pan.)
Diátaxis layout
- Tutorials: guided learning paths
- How-to guides: practical task playbooks
- Reference: CLI/config contracts
- Explanation: architecture and rationale
Current status
Portal is still evolving. Where behavior is policy-sensitive, reference pages document defaults and configurable alternatives.
Choose your path
Use this quick routing guide to pick the right docs entry point.
I want sandboxed local agent sessions
Use Agent-box.
Start with: Tutorial: Agent-box first run
I want host capability mediation, but no container orchestrator
Use Portal standalone.
Start with: Tutorial: Portal standalone first run
I want Agent-box with Portal-enabled wrappers
Use both.
Start with: Tutorial: Connect Portal to Agent-box
What’s the difference?
- Agent-box manages repositories, workspaces, and containers.
- Portal brokers selected host operations over a Unix socket with policy controls.
- Wrappers (for example
wl-paste,gh) provide transparent, tool-compatible access to Portal methods.
Tutorials
Tutorials are learning-oriented and linear. Pick one path and follow it end-to-end.
- New to Agent-box: Agent-box first run
- New to Portal only: Portal standalone first run
- Combining both: Connect Portal to Agent-box
Tutorial: Agent-box first run
Outcome
You create a workspace and start a containerized session with ab.
Prerequisites
abinstalled- Docker or Podman available
~/.agent-box.tomlcreated withworkspace_dir,base_repo_dir, andruntime.image
Steps
-
Check basic CLI access:
ab info -
Create a workspace (JJ by default):
ab new myrepo -s first-session -
Spawn the container:
ab spawn -r myrepo -s first-session -
Inside the container, verify where you are:
pwdYou should be in the workspace path managed by Agent-box.
What you learned
- How to create a named session workspace
- How to spawn a container bound to that workspace
Next: see How-to guides for custom runtime and CI usage.
Tutorial: Portal standalone first run
Outcome
You run agent-portal-host and send successful requests with agent-portal-cli.
Prerequisites
agent-portal-hostandagent-portal-cliinstalled~/.agent-box.tomlwith[portal]enabledwl-pasteavailable on host if testing clipboard method
Minimal config
[portal]
enabled = true
socket_path = "/run/user/1000/agent-portal/portal.sock"
[portal.policy.defaults]
clipboard_read_image = "allow"
gh_exec = "ask_for_writes"
Steps
-
Start the host service in a terminal:
RUST_LOG=info agent-portal-host -
In another terminal, verify connectivity:
agent-portal-cli ping agent-portal-cli whoami -
Optionally test image clipboard request:
agent-portal-cli clipboard-read-image --out /tmp/clip.bin
What you learned
- Portal works independently of Agent-box
- Portal methods are reachable over a Unix socket via official CLI
Next: Portal wrapper contract.
Tutorial: Connect Portal to Agent-box
Outcome
You run an Agent-box container where tools can use Portal through wrapper binaries.
Prerequisites
- Agent-box setup working (
ab spawnsucceeds) - Portal host running (
agent-portal-host) - Wrappers installed in container image or mounted into container PATH
Steps
-
Enable portal in config:
[portal] enabled = true socket_path = "/run/user/1000/agent-portal/portal.sock" [portal.policy.defaults] clipboard_read_image = "allow" gh_exec = "ask_for_writes" -
Start portal host on the machine running containers:
agent-portal-host -
Spawn an Agent-box session:
ab spawn -r myrepo -s portal-sessionAgent-box mounts the configured socket and sets
AGENT_PORTAL_SOCKETin the container. -
In the container, validate wrapper-backed flow:
wl-paste --list-typesIf wrappers are in PATH and policy allows, this returns an image MIME type when present.
What you learned
- How Agent-box and Portal integrate
- How wrappers keep calling conventions tool-compatible
How-to guides
How-to guides are task-oriented. Use these when you already understand the basics and need to get work done.
- Agent-box profiles: use profiles effectively
- Agent-box runtime tuning: customize container runtime
- GPG integration: forward GPG agent
- Nix integration: share host Nix store
- Portal wrappers: run with a custom wrapper
- Portal troubleshooting: debug wrapper failures
- pi image paste: enable pi image paste
How to run Portal with a custom wrapper
Goal
Add a wrapper binary that forwards a host-capability request through Portal.
Prerequisites
- Running
agent-portal-host - Access to socket path (
AGENT_PORTAL_SOCKETor config default) - Wrapper implemented in Rust (
wrappers/convention)
Steps
- Define wrapper CLI shape that matches the tool you want to emulate.
- In wrapper, create a
PortalClientusing env/config resolution. - Translate wrapper input into a Portal method request.
- Forward host response bytes/stdout/stderr to wrapper stdout/stderr.
- Exit with mapped exit code from portal response.
- Place wrapper ahead of native utility on PATH in target environment.
Validation
- Wrapper command succeeds against live
agent-portal-host. - Expected output format matches calling tool expectations.
- Failure path returns useful stderr and non-zero code.
Related
How to debug Portal wrapper failures
Goal
Find and fix common failures for wl-paste/gh wrappers and other Portal clients.
Quick checklist
- Is host service running?
pgrep -a agent-portal-host - Can you ping the socket directly?
agent-portal-cli ping - Is the wrapper using the expected socket path?
echo "$AGENT_PORTAL_SOCKET" - Enable host logs:
RUST_LOG=debug agent-portal-host
Common failures
- failed to connect to socket
- socket path mismatch or host service not running
- denied
- policy mode blocks method/container
- prompt_failed
prompt_commandmissing or exits non-zero in ask-mode
- clipboard_failed
- no allowed image MIME currently in clipboard or host
wl-pasteissue
- no allowed image MIME currently in clipboard or host
- gh_exec_failed
- host
ghunavailable or command failure
- host
Next actions
- Confirm
[portal.policy]defaults and overrides. - Confirm wrapper is first on PATH in container.
- Re-run request via
agent-portal-clito isolate wrapper-specific parsing issues.
How to enable pi image paste via portal wrapper
Goal
Make image paste work transparently in pi sessions using portal-backed wl-paste wrapper.
Requirements
agent-portal-hostrunning on host- Portal enabled in
~/.agent-box.toml - Wrapper binary (
wl-paste) available in container PATH before systemwl-paste
Steps
- Start portal host:
agent-portal-host
- Spawn with Agent-box (portal enabled):
ab spawn -s my-session
- In container/session env:
export WAYLAND_DISPLAY=wayland-1
# AGENT_PORTAL_SOCKET is injected by ab when portal is enabled
- Validate wrapper flow:
wl-paste --list-types
wl-paste --type image/png --no-newline
Notes
- Policy is enforced host-side by
agent-portal-host. - Wrapper should not prompt in-container.
Agent-box: use profiles effectively
Goal
Use profiles to keep ~/.agent-box.toml modular and compose task-specific runtime settings.
When to use this
Use profiles when you want to:
- avoid one giant
[runtime]block - share reusable config chunks (for example
nix,gpg,rust) - apply extra settings only for specific sessions
Step 1: Define reusable profiles
Create named profiles under [profiles.*]:
[profiles.nix]
env = ["NIX_REMOTE=daemon"]
mounts.ro.absolute = ["/nix/store"]
mounts.rw.absolute = ["/nix/var/nix/daemon-socket/"]
[profiles.rust]
mounts.rw.home_relative = ["~/.cargo"]
[profiles.gpg]
mounts.rw.absolute = [
"/run/user/1000/gnupg/S.gpg-agent:~/.gnupg/S.gpg-agent",
]
Step 2: Create a baseline profile
Compose a default profile with extends:
default_profile = "base"
[profiles.base]
extends = ["nix", "rust"]
This profile is automatically applied to ab spawn.
Step 3: Add extra profiles per command
Apply additional profiles in CLI order:
ab spawn -r myrepo -s mysession -p gpg
You can stack multiple flags:
ab spawn -r myrepo -s mysession -p rust -p gpg
Step 4: Verify the resolved config
Inspect what Agent-box will actually use:
ab dbg resolve
ab dbg resolve -p rust -p gpg
Validate that your config is accepted:
ab dbg validate
Merge behavior checklist
- Scalars: later layers override earlier layers
- Arrays: values append
- Objects: merge recursively
- Profile order matters for scalar conflicts
Related reference
How to customize Agent-box container runtime
Goal
Adjust runtime backend, image, entrypoint, mounts, ports, hosts, and network mode.
Steps
-
Edit
~/.agent-box.toml:[runtime] backend = "podman" # or "docker" image = "agent-box:latest" entrypoint = "/bin/bash" ports = ["8080:8080"] hosts = ["host.docker.internal:host-gateway"] [runtime.mounts.ro] absolute = ["/nix/store"] [runtime.mounts.rw] home_relative = ["~/.local/share"] -
Validate configuration:
ab dbg validate -
Preview resolved profile/runtime:
ab dbg resolve -p your-profile -
Override per spawn when needed:
ab spawn -s demo -p rust -P 3000:3000 -H myhost:10.0.0.1 --network=bridge
Notes
--network=hostand port/host mappings may conflict on Docker runtime.- Overlay mount mode (
o) is Podman-only.
How to forward GPG agent to Agent-box containers
Goal
Use host GPG keys/signing from inside container sessions.
Steps
- Discover host socket locations:
gpgconf --list-dirs
- Configure overlay mount for
~/.gnupg(Podman):
[runtime.mounts.o]
home_relative = ["~/.gnupg"]
[runtime.mounts.rw]
home_relative = [
"/run/user/1000/gnupg/S.gpg-agent:~/.gnupg/S.gpg-agent",
"/run/user/1000/gnupg/S.keyboxd:~/.gnupg/S.keyboxd",
]
- Spawn session and test:
ab spawn -s my-session
gpg --list-secret-keys
Notes
- Replace
1000with your UID. - For smartcards, add
S.scdaemonsocket mapping. - Docker does not support overlay mounts; Podman is preferred here.
Troubleshooting
- Lock conflicts: remove stale locks (
find ~/.gnupg -name '.#lk*' -delete) - IPC issues: verify socket path with
gpg-connect-agent 'getinfo socket_name' /bye
How to share host Nix store with containers
Goal
Use host Nix binaries/store paths from inside Agent-box containers.
Configuration
[runtime.env]
# alternatively set in runtime.env as KEY=VALUE string entries
[runtime]
env = ["NIX_REMOTE=daemon"]
[runtime.mounts.ro]
absolute = ["/nix/store"]
[runtime.mounts.rw]
absolute = ["/nix/var/nix/daemon-socket/"]
Verify
ab spawn -s my-session
nix --version
Notes
- Store is mounted read-only.
- Daemon socket mount enables requesting builds/fetches through host daemon.
Explanation
Explanation pages provide architecture, rationale, and trade-offs.
Use this section to build mental models, then jump to tutorials/how-to/reference for action-oriented details.
Portal architecture overview
Portal is a host-side mediation layer for selected capabilities that should not be exposed directly to containers.
Core model
agent-portal-hostlistens on a Unix socket.- Clients send MessagePack requests with method payloads.
- Host resolves caller identity from peer credentials.
- Host applies policy (default + per-container overrides).
- Host performs allowed operation and returns structured response.
Why this model
Direct host socket passthrough is broad and hard to audit. Portal centralizes control, policy, and observability behind method-level requests.
Implemented methods
pingwhoamiclipboard.read_imagegh.exec
Important constraints
- Prompting happens host-side, never in container wrappers.
- Timeouts and rate limiting are explicit configuration concerns.
- Host binary resolution avoids recursive wrapper execution.
Agent-box architecture overview
Agent-box orchestrates safe, disposable execution environments for autonomous coding agents.
Core responsibilities
- Discover and resolve repositories under
base_repo_dir - Create workspaces (JJ or Git)
- Build container runtime config from layered settings + profiles + CLI overrides
- Spawn containers with deterministic mounts, env, networking, and entrypoint
Layered configuration model
- Global config defines defaults.
- Repo-local config refines project-specific behavior.
- Profiles provide composable bundles (mounts/env/ports/hosts/context).
Runtime abstraction
Agent-box supports Docker and Podman backends through runtime-specific implementation while preserving one CLI surface.
Relationship to Portal
Agent-box can mount Portal socket and export AGENT_PORTAL_SOCKET, but Portal remains optional and independently operable.
Agent-box workflow internals
This page explains the runtime flow behind ab new and ab spawn.
Repository/workspace model
- Source repositories are discovered under
base_repo_dir. - Workspaces are created under
workspace_dir. - Workspace mode is either JJ workspace or Git worktree.
ab new flow
- Resolve repository ID (explicit or from current directory).
- Choose workspace type (JJ default, or Git).
- Create workspace for selected session name.
ab spawn flow
- Resolve workspace path (
--sessionmode) or current dir (--local). - Load and validate layered configuration.
- Resolve profile graph (
default_profile+ CLI profiles). - Build runtime-specific container configuration.
- Apply mounts/env/ports/hosts/network options.
- Mount portal socket and set
AGENT_PORTAL_SOCKETwhen portal is enabled. - Execute selected runtime backend (Podman or Docker).
Path resolution notes
- Home-relative paths are translated for host/container user homes.
- Relative mount source paths are resolved from current working directory.
- Symlinked paths are expanded to preserve resolution behavior inside container.
Portal and Agent-box relationship
Portal and Agent-box are complementary, not coupled.
Independent concerns
- Agent-box: workspace + container lifecycle orchestration
- Portal: host capability mediation with policy/prompt/audit boundary
Combined flow
When used together:
- Agent-box starts container and mounts portal socket.
- Agent-box sets
AGENT_PORTAL_SOCKETin container env. - Wrapper/tool in container calls portal client.
- Portal host enforces policy and executes host operation.
Request flow diagram
Use scroll/pinch to zoom, drag to pan (Excalidraw interactive viewer).
flowchart LR
A[Tool in container] --> B[Wrapper binary in container]
B -->|MessagePack over Unix socket| C[agent-portal-host on host]
C --> D{Policy decision}
D -->|allow| E[Execute host capability]
D -->|ask| F[Prompt command]
F -->|approved| E
F -->|denied or timeout| G[Return denial]
E --> H[Return stdout stderr bytes and exit code]
G --> H
H --> B
B --> A
Why keep them separable
- Portal can serve non-Agent-box environments.
- Agent-box remains useful without Portal methods.
- Integration boundary stays explicit (socket + env), reducing hidden coupling.
ADRs and rationale
The accepted Portal ADRs are now part of this book.
ADR list
- ADR 0001: Host Portal Service for Container-to-Host Capabilities
- ADR 0002: Transparent Portal Access via Wrapper Binaries
Read them in order.
ADR 0001: Host Portal Service for Container-to-Host Capabilities
- Status: Accepted
- Date: 2026-02-27
Context
We need a secure way for containers to access selected host capabilities (starting with host clipboard image read), with optional user approval similar to xdg-desktop-portal behavior.
Directly exposing host Wayland/X11 sockets to containers is too broad and does not provide granular per-action approval/policy controls.
Decision
Implement a separate host binary (agent-portal-host) that runs as a systemd user service and listens on a Unix socket for container requests.
An official CLI client (agent-portal-cli) will be provided.
1. Identity and container attribution
- Use Unix socket peer credentials (
SO_PEERCRED) to identify the caller process. - Resolve peer PID + namespace/cgroup metadata back to a Podman container ID.
- Display resolved Podman container identity to the user in approval prompts.
2. Protocol and transport
- Use Unix domain sockets.
- Use MessagePack as the on-wire protocol.
- Design protocol to be method-based and extensible for future host capabilities.
3. User prompt integration
- Prompting is done via configurable dmenu-style command.
- User can configure a command such as
rofi -dmenu ....
4. Policy model
- Policy is configurable.
- It must support allowing
clipboard.read_imagewithout prompting. - Per-method/per-container policy overrides are supported.
5. Initial clipboard scope
- First capability:
clipboard.read_imageonly. - Restrict to image MIME types (allowlist).
- Log request decisions and outcomes.
6. Operational safeguards
- Handle multiple containers concurrently.
- Apply concurrency limits.
- Apply per-container and/or global rate limiting.
- Timeouts are configurable;
0means no timeout.
7. Configuration source
- Reuse
~/.agent-box.tomlfor portal configuration. - Use a dedicated namespaced section (e.g.
[portal]).
8. Data transfer strategy
- MVP may return image bytes inline in MessagePack with size limits.
- File descriptor passing (
SCM_RIGHTS) is acknowledged and can be added later for efficient large payload transfer across the Unix socket boundary.
Consequences
Positive
- Fine-grained host capability mediation for containers.
- Better security posture than mounting host display/session sockets.
- User-visible approval flow with container attribution.
- Extensible foundation for future portal methods.
Trade-offs
- Host daemon becomes a sensitive trust boundary and must be hardened.
- Identity resolution from PID/ns/cgroup to Podman ID must be robust.
- Prompt UX and policy defaults affect safety and usability.
Follow-up implementation tasks
- Add
agent-portal-hostbinary skeleton and Unix socket server. - Implement peer credential extraction and Podman container ID resolution.
- Define MessagePack request/response schema and versioning.
- Implement
clipboard.read_imagemethod with MIME allowlist and size limits. - Add prompt adapter with configurable dmenu-style command.
- Add policy/rate-limit/timeout plumbing sourced from
~/.agent-box.toml. - Add structured audit logging.
- Add
agent-portal-cliofficial CLI client (ping, request method, dump output).
ADR 0002: Transparent Portal Access via Wrapper Binaries
- Status: Accepted
- Date: 2026-02-27
Context
Portal-based host capability access is safer than direct host socket passthrough, but requires callers to know and integrate a custom API.
Many agent tools (e.g. pi) already call standard host utilities directly (such as wl-paste). Requiring every agent/tool to learn portal APIs is not transparent and hurts adoption.
Decision
Provide transparent wrapper binaries that mimic standard utility behavior while internally using the portal socket API.
Initial wrapper:
wl-pastewrapper using portalclipboard.read_image.
Implementation structure:
- New top-level
wrappers/crate for compatibility binaries. - Shared Rust portal client API in
common(portal_client) for direct Rust consumers.
Scope (MVP)
The wrapper supports the Wayland usage pattern required by pi image paste flow:
wl-paste --list-typeswl-paste --type <selected-mime-type> --no-newline
Behavior:
--list-types: returns the currently available clipboard image MIME type exposed by portal.--type ... --no-newline: returns raw image bytes from portal, enforcing MIME match when requested.
Consequences
Positive
- Agent/tools remain unaware of portal internals.
- Drop-in compatibility improves usability.
- Single mediation point (portal) keeps policy/security controls centralized.
Trade-offs
- Wrapper compatibility must track behavior of real utilities.
- Some utility flags may not be supported initially.
- Extra binary indirection adds maintenance surface.
Follow-up
- Expand wrapper flag compatibility as needed.
- Add more wrappers for other host capability entry points.
- Consider packaging wrappers on PATH ahead of native tools inside container images.
Security model
This section explains the security posture of using Portal and Agent-box together or separately.
Threat reduction goals
- Avoid broad host socket exposure to containers.
- Add policy gates to sensitive host operations.
- Provide audit-friendly mediation point.
Trust boundaries
- Container: untrusted or semi-trusted agent execution context.
- Portal host: trusted broker enforcing method policy.
- Host binaries (
gh,wl-paste): executed only by host broker.
Control mechanisms
- Unix socket peer credentials for caller identity.
- Method-level policy modes (
allow/ask/deny,gh_execpolicy modes). - Optional prompt-based approval command.
- Concurrency limits, rate limits, and request/prompt timeouts.
- Clipboard MIME allowlist and payload size limit.
Residual risks
- Misconfigured policy can allow broader access than intended.
- Prompt UX failures can degrade safety or usability.
- Host broker is a sensitive component and must be monitored and updated.
Operational guidance
- Keep policies explicit and minimal.
- Use deny-by-default for high-risk methods where appropriate.
- Enable debug logs during rollout and review outcomes.
Reference
Reference pages are factual and scannable.
Use these when you need exact command flags, config keys, and behavior contracts.
Do not edit by hand. Regenerate with:
nu docs/scripts/generate-cli-reference.nu
Portal CLI reference
agent-portal-host
Command:
cargo run -q -p agent-portal --bin agent-portal-host -- --help
Output:
2026-03-01T16:49:06.778024Z INFO agent_portal_host: PATH path=["/nix/store/3vs2fr2mazafcdwyza15bfhpmccx1k7z-patchelf-0.15.2/bin", "/nix/store/r9wbjib6xxjkyb9yvjvrkl4sq61i2lyn-gcc-wrapper-15.2.0/bin", "/nix/store/qarrb8yfby1yyypm32vabzgxgq3w41ma-gcc-15.2.0/bin", "/nix/store/7ri1mm5y99nkr7657r77wycrvhg7z9x0-glibc-2.40-66-bin/bin", "/nix/store/d75200gb22v7p0703h5jrkgg8bqydk5q-coreutils-9.8/bin", "/nix/store/dwiyp91lmxq864plaas14jm14m87sg3f-binutils-wrapper-2.44/bin", "/nix/store/cl88v2m1y5q3k6jlkq5jjf73nmfgl1px-binutils-2.44/bin", "/nix/store/9db7xb1axdgqp4lakdxbp27yh7kfvm0b-cargo-1.91.1/bin", "/nix/store/lvdr3nws8gs2qcgm3rnwvjprcx55kx7l-rustc-wrapper-1.91.1/bin", "/nix/store/i1zl6150nc77vryj6n9razki0l5bzxv5-rustfmt-1.91.1/bin", "/nix/store/bqgnqxrjkhmfw1pwy4ir7yn3mdxs4sva-clippy-1.91.1/bin", "/nix/store/hxn2qrz1zmk5q01wsb7n3d58brzrsizb-pkg-config-wrapper-0.29.2/bin", "/nix/store/827b5xq1ghbbdrp1faa4fw32xj1m2p51-openssl-3.6.0-bin/bin", "/nix/store/5dslbfaryrm3lsjr0d6bdbp7f2g40dwp-mdbook-0.5.1/bin", "/nix/store/fwq388qahsyy8pngn05zvrkdfywy90sc-mdbook-excalidraw-0.1.0/bin", "/nix/store/fryiyyhhilpqhx2wc4b6l5gnxc8w18rg-git-2.52.0/bin", "/nix/store/d75200gb22v7p0703h5jrkgg8bqydk5q-coreutils-9.8/bin", "/nix/store/wd99g2j010fdkry0ws1bhxzm52w82ssx-findutils-4.10.0/bin", "/nix/store/20zvyjvxq9x2mkp7rbnvrwjjzq2n76hh-diffutils-3.12/bin", "/nix/store/k06ssckzrzn9jjvvs4n62m6567zmbx6x-gnused-4.9/bin", "/nix/store/qfmq7p42ak5yn389qvx7zpxkan5i4xiy-gnugrep-3.12/bin", "/nix/store/nmxm04dhkaqg1q6hai70n9zmzb0q49a5-gawk-5.3.2/bin", "/nix/store/k1lcfin159706lihwx5hhvl80dbij4jw-gnutar-1.35/bin", "/nix/store/0hv4z5s3r1h4lmvn0427mlxjxgvg34nr-gzip-1.14/bin", "/nix/store/p61ba9fdgx3358bpp18hv4rslf6n5bq6-bzip2-1.0.8-bin/bin", "/nix/store/mkm3my2067305hdh7rzmi10npwr7y17f-gnumake-4.4.1/bin", "/nix/store/lw117lsr8d585xs63kx5k233impyrq7q-bash-5.3p3/bin", "/nix/store/clrf4mjwr8xcfpvway6w34wzvqc1hry4-patch-2.8/bin", "/nix/store/j1zc5jh0vi9sbxj09ldj4xklgm6kpf8n-xz-5.8.1-bin/bin", "/nix/store/s2k48fw3y698j4kcvmw0520m06ihv2z4-file-5.45/bin", "/nix/store/x12lw455sq6qy2wcya85d7rb88ybc3df-bash-interactive-5.3p9/bin", "/nix/store/c2p7haf4zzkbrir9zs662r68c5dmylbq-patchelf-0.15.2/bin", "/nix/store/a245z3cvf9x9sn0xlk6k8j9xhxbhda1z-gcc-wrapper-15.2.0/bin", "/nix/store/mjf8jlq9grydcdvyw6hb063x5c34g5gf-gcc-15.2.0/bin", "/nix/store/0bdqq2z98kg2hfn3k60if6pb5fd5p10h-glibc-2.42-47-bin/bin", "/nix/store/i2vmgx46q9hd3z6rigaiman3wl3i2gc4-coreutils-9.9/bin", "/nix/store/i6ppbrlpp6yki8qvka7nyv091xa8dchx-binutils-wrapper-2.44/bin", "/nix/store/47mn80zqpygykqailwzw8zlag4cgl75q-binutils-2.44/bin", "/nix/store/4q96zm895m64s3011i20gph9anxkpk0a-nushell-0.109.1/bin", "/nix/store/i2vmgx46q9hd3z6rigaiman3wl3i2gc4-coreutils-9.9/bin", "/nix/store/16wfacfgap3chf7mcjnd8dwi85dj4qqi-findutils-4.10.0/bin", "/nix/store/3p87h6dn5i87i3iq9364imzbqgwvkg2p-diffutils-3.12/bin", "/nix/store/ryz8kcrm2bxpccllfqlb7qldsfnqp5c2-gnused-4.9/bin", "/nix/store/02vv0r262agf9j5n2y1gmbjvdf12zkl0-gnugrep-3.12/bin", "/nix/store/2xq9rayckw8zq26k274xxlikn77jn60j-gawk-5.3.2/bin", "/nix/store/qyg62bc2xnpwz0fa9prqxvvk00zj4g9q-gnutar-1.35/bin", "/nix/store/84yyzmxs7mb8nhkvcfv9n1l9irpb6mnq-gzip-1.14/bin", "/nix/store/90yw24gqmwph4xjp4mqhpx1y1gcrvqla-bzip2-1.0.8-bin/bin", "/nix/store/vbah5c4rzy1q1hbqhginyxjhj8d4dj8j-gnumake-4.4.1/bin", "/nix/store/f15k3dpilmiyv6zgpib289rnjykgr1r4-bash-5.3p9/bin", "/nix/store/wwij6563c6wbg4kzgjhng7vlhf7api19-patch-2.8/bin", "/nix/store/zys6d102zp171wpwcs08g632886w2qxs-xz-5.8.2-bin/bin", "/nix/store/nyy0bvgjwd98x7ih8pl6pr79qjljgsf7-file-5.45/bin", "/home/dmnt/.pi/agent/bin", "/nix/store/yazhl9gb5lpdgm220nm4202imc2k2cz1-fd-10.3.0/bin", "/nix/store/5ij40vzd46x6psvdv5yg5gr6igrpwyd4-ripgrep-15.1.0/bin", "/home/agent/.nix-profile/bin", "/nix/var/nix/profiles/default/bin", "/nix/var/nix/profiles/default/sbin"]
Host portal service for container capability requests
Usage: agent-portal-host [OPTIONS]
Options:
--socket <SOCKET> Override socket path
-h, --help Print help
agent-portal-cli
Command:
cargo run -q -p agent-portal --bin agent-portal-cli -- --help
Output:
Official CLI client for agent portal host service
Usage: agent-portal-cli [OPTIONS] <COMMAND>
Commands:
ping
whoami
clipboard-read-image
gh-exec
exec
help Print this message or the help of the given subcommand(s)
Options:
--socket <SOCKET> Override socket path
-h, --help Print help
wl-paste wrapper
Command:
cargo run -q -p agent-wrappers --bin wl-paste -- --help
Output:
Usage: wl-paste [--list-types] [--type <mime>] [--no-newline]
Portal MessagePack type reference
Portal uses MessagePack encoding over a Unix domain socket.
This page documents request/response pairs by method.
Envelopes
Request envelope
PortalRequest {
version: u16,
id: u64,
method: RequestMethod (flattened)
}
version: protocol version (currently1)id: client-generated request IDmethod: serde-tagged enum (method+ optionalparams)
Response envelope
PortalResponse {
version: u16,
id: u64,
ok: bool,
result: ResponseResult | null,
error: PortalError | null
}
idis echoed from request- if
ok = true,resultis set - if
ok = false,erroris set
Error object
PortalError {
code: string,
message: string
}
Common code values: denied, prompt_failed, rate_limited, clipboard_failed, gh_exec_failed, too_busy.
Method pairs
ping
Request:
{ "method": "ping" }
Success response (Pong):
{
"type": "Pong",
"data": {
"now_unix_ms": u128
}
}
whoami
Request:
{ "method": "whoami" }
Success response (WhoAmI):
{
"type": "WhoAmI",
"data": {
"pid": i32,
"uid": u32,
"gid": u32,
"container_id": string | null
}
}
clipboard.read_image
Request:
{
"method": "clipboard.read_image",
"params": {
"reason": string | null
}
}
Success response (ClipboardImage):
{
"type": "ClipboardImage",
"data": {
"mime": string,
"bytes": binary
}
}
Common error codes for this method:
deniedprompt_failedclipboard_failedrate_limitedtoo_busy
gh.exec
Request:
{
"method": "gh.exec",
"params": {
"argv": [string, ...],
"reason": string | null,
"require_approval": bool
}
}
Success response (GhExec):
{
"type": "GhExec",
"data": {
"exit_code": i32,
"stdout": binary,
"stderr": binary
}
}
Common error codes for this method:
deniedprompt_failedgh_exec_failedrate_limitedtoo_busy
exec
Request:
{
"method": "exec",
"params": {
"argv": [string, ...],
"reason": string | null,
"cwd": string | null,
"env": { string: string, ... } | null
}
}
Success response (Exec):
{
"type": "Exec",
"data": {
"exit_code": i32,
"stdout": binary,
"stderr": binary
}
}
Common error codes for this method:
deniedprompt_failedexec_failedrate_limitedtoo_busy
Serialization notes
- Request enum tags:
method+params - Response enum tags:
type+data PortalRequest.methodis flattened into request envelope- Rust source of truth:
common/src/portal.rs
Portal configuration reference
Portal config lives under [portal] in ~/.agent-box.toml.
Top-level keys
enabled(bool, default:true)socket_path(string, default:/run/user/<uid>/agent-portal/portal.sock)prompt_command(string|null, default: unset)timeouts.request_ms(u64, default:0= no timeout)timeouts.prompt_ms(u64, default:0= no timeout)limits.max_inflight(usize, default:32)limits.prompt_queue(usize, default:64)limits.rate_per_minute(u32, default:60)limits.rate_burst(u32, default:10)limits.max_clipboard_bytes(usize, default:20971520)clipboard.allowed_mime(array of strings, default:image/png,image/jpeg,image/webp)
Policy defaults
[portal.policy.defaults]
clipboard_read_image:allow | ask | deny(default:allow)gh_exec:ask_for_writes | ask_for_all | ask_for_none | deny_all(default:ask_for_writes)- aliases accepted by config parser:
allow->ask_for_noneask->ask_for_writesdeny->deny_all
- aliases accepted by config parser:
Per-container policy override
[portal.policy.containers."<container-id>"]
Container ID is resolved from peer process cgroup metadata.
Example:
[portal.policy.containers."3f7a1d5c2b8e"]
clipboard_read_image = "deny"
gh_exec = "ask_for_all"
Example
[portal]
enabled = true
socket_path = "/run/user/1000/agent-portal/portal.sock"
prompt_command = "rofi -dmenu -p 'agent-portal'"
[portal.timeouts]
request_ms = 5000
prompt_ms = 15000
[portal.limits]
max_inflight = 32
prompt_queue = 64
rate_per_minute = 60
rate_burst = 10
max_clipboard_bytes = 20971520
[portal.clipboard]
allowed_mime = ["image/png", "image/jpeg", "image/webp"]
[portal.policy.defaults]
clipboard_read_image = "allow"
gh_exec = "ask_for_writes"
JSON Schema
Portal configuration is part of the overall ~/.agent-box.toml schema. The full JSON Schema can be used for validation and IDE autocompletion.
Portal wrapper contract reference
This page describes behavior expected from compatibility wrappers in wrappers/.
General contract
- Wrappers expose familiar command names/flags.
- Wrappers do not call host capability directly inside container.
- Wrappers call Portal methods via Unix socket.
- Wrappers forward output as tool-compatible stdout/stderr.
- Wrapper process exit code matches operation result semantics.
Socket resolution order
AGENT_PORTAL_SOCKETenvironment variable~/.agent-box.toml->[portal].socket_path- Built-in default
/run/user/<uid>/agent-portal/portal.sock
wl-paste wrapper contract
--list-typesreturns a single available image MIME type selected by portal policy.--type <mime> --no-newlinewrites raw image bytes.- If requested MIME does not match available MIME, wrapper errors.
gh wrapper contract
- Forwards argv as
gh.execrequest payload. - Includes a human-readable
reasonstring for prompt/audit context. - Does not prompt in-container.
- Prints portal-returned stdout/stderr and exits with portal-returned exit code.
Host-side execution model
- Policy decisions and prompts are enforced by
agent-portal-host. - Host service resolves host-native binaries (
gh,wl-paste) to avoid wrapper recursion.
Versioning
Current request/response protocol version field is 1.
Do not edit by hand. Regenerate with:
nu docs/scripts/generate-cli-reference.nu
Agent-box CLI reference
ab
Command:
cargo run -q -p ab -- --help
Output:
Agent Box - Git repository management tool
Usage: ab <COMMAND>
Commands:
info Show repository information and list workspaces
new Create a new workspace (jj or git worktree)
spawn Spawn a new container for a workspace
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
ab new
Command:
cargo run -q -p ab -- new --help
Output:
Create a new workspace (jj or git worktree)
Usage: ab new [OPTIONS] [REPO_NAME]
Arguments:
[REPO_NAME] Repository name (defaults to current directory's git repo)
Options:
-s, --session <SESSION> Session/workspace name
--git Create a git worktree
--jj Create a jj workspace
-h, --help Print help
ab spawn
Command:
cargo run -q -p ab -- spawn --help
Output:
Spawn a new container for a workspace
Usage: ab spawn [OPTIONS]
Options:
-s, --session <SESSION> Session name (mutually exclusive with --local)
-l, --local Use current directory as both source and workspace (mutually exclusive with --session)
-r, --repo <REPO> Repository identifier (defaults to current directory's git repo)
-e, --entrypoint <ENTRYPOINT> Override entrypoint from config
-c, --command <COMMAND> Command to run in the container (passed to entrypoint)
--git
--jj
-n, --new Create workspace if it doesn't exist (equivalent to running `ab new` first)
--ro Mount source directory as read-only
-m, --mount <MOUNT> Additional mount (home-relative). Format: [MODE:]PATH or [MODE:]SRC:DST MODE is ro, rw, or o (default: rw). Paths use ~ for home directory. Relative host source paths are resolved against the current working directory. Example: -m ~/.config/git -m ro:~/secrets -m rw:~/data:/app/data -m ../pierre
-M, --Mount <MOUNT> Additional mount (absolute). Format: [MODE:]PATH or [MODE:]SRC:DST MODE is ro, rw, or o (default: rw). Same path used on host and container. Relative host source paths are resolved against the current working directory. Example: -M /nix/store -M ro:/etc/hosts -M ../shared
-p, --profile <PROFILE> Additional profiles to apply (can be specified multiple times). Profiles are applied after the default_profile (if set) and in order specified. Example: -p git -p rust
-P, --port <PORT> Port mapping to expose (can be specified multiple times). Format: [HOST_IP:]HOST_PORT:CONTAINER_PORT or just CONTAINER_PORT. Example: -P 8080:8080 -P 3000 -P 127.0.0.1:9090:9090
-H, --add-host <HOST:IP> Custom host-to-IP mapping added to /etc/hosts in the container (can be specified multiple times). Format: HOST:IP (use `host-gateway` as IP to resolve to the host machine). Example: -H myhost:192.168.1.1 -H host.docker.internal:host-gateway
--no-skip Don't skip mounts that are already covered by parent mounts
--network <MODE> Network mode to use (e.g. host, bridge, none, or a container name). Passed directly as --network=<MODE> to the container runtime
-h, --help Print help
ab info
Command:
cargo run -q -p ab -- info --help
Output:
Show repository information and list workspaces
Usage: ab info
Options:
-h, --help Print help
Agent-box configuration reference
Main configuration file: ~/.agent-box.toml
Layered configuration
Load order:
~/.agent-box.toml(required){git-root}/.agent-box.toml(optional)
Merge behavior:
- Scalars: repo-local overrides global
- Arrays: values are appended
- Objects: merged recursively
Root keys
workspace_dir(path): base directory for generated workspacesbase_repo_dir(path): base directory for source repositoriesdefault_profile(string|null): profile automatically applied toab spawnprofiles(table): named profile definitionsruntime(table): runtime/backend settingscontext(string): root context contentcontext_path(string, default/tmp/context): in-container path for context fileportal(table): portal host integration settings
All paths support ~ expansion.
[runtime]
backend(string, defaultpodman):podmanordockerimage(string): container imageentrypoint(shell-style string): parsed to argvenv(array ofKEY=VALUE)env_passthrough(array of variable names)ports(array of-pcompatible port mappings)hosts(array ofHOST:IPentries)skip_mounts(array of glob patterns)mounts(table):ro,rw, andomount categories
Mount table shape
Each of runtime.mounts.ro, runtime.mounts.rw, and runtime.mounts.o has:
absolute(array of strings)home_relative(array of strings)
Mount modes:
ro: read-onlyrw: read-writeo: overlay (Podman only)
CLI additional mount syntax (ab spawn)
[MODE:]PATH[MODE:]SRC:DST
MODE values: ro, rw, o (default: rw).
Examples:
-m ~/data-m ro:~/.config/git-m rw:~/src:/app/src-M /nix/store-M o:/tmp/cache
Environment passthrough
env_passthrough copies host env values into the container at spawn time.
Example:
[runtime]
env_passthrough = ["PATH", "SSH_AUTH_SOCK", "TERM"]
Context composition
Context is built in this order:
- Root
context - Each resolved profile
contextin profile resolution order
Values are joined with newlines and written to a temp file mounted at context_path.
Port mappings
Port values follow container runtime -p syntax:
HOST_PORT:CONTAINER_PORTHOST_IP:HOST_PORT:CONTAINER_PORTCONTAINER_PORT
Example:
[runtime]
ports = ["8080:8080", "127.0.0.1:9090:9090", "3000"]
Host entries
hosts entries are passed as runtime --add-host values.
Example:
[runtime]
hosts = ["host.docker.internal:host-gateway", "myhost:10.0.0.1"]
Network mode
CLI flag: ab spawn --network=MODE
Typical values:
hostbridgenone- runtime-specific named network
On Docker, --network=host conflicts with published ports and add-host options.
Runtime backend differences
- Podman: supports overlay mount mode (
o) and keep-id user namespace behavior - Docker: no overlay mounts; uses direct user mapping
Profiles
Profiles are reusable config fragments you can layer on top of runtime defaults.
Profile table: [profiles.NAME]
Supported keys:
extends(array of profile names)mounts(same shape as runtime mounts)env(array ofKEY=VALUE)env_passthrough(array of variable names)ports(array of port mapping strings)hosts(array ofHOST:IPentries)context(string)
Profile inheritance (extends)
A profile can inherit from one or more profiles using extends.
Inherited values are merged using the same layered rules described above:
- Scalars override
- Arrays append
- Objects merge recursively
Example:
[profiles.base]
env = ["RUST_BACKTRACE=1"]
mounts.rw.home_relative = ["~/.cargo"]
[profiles.gpg]
mounts.rw.absolute = ["/run/user/1000/gnupg/S.gpg-agent:~/.gnupg/S.gpg-agent"]
[profiles.dev]
extends = ["base", "gpg"]
ports = ["8080:8080"]
Activation order
Final runtime config is resolved in this order:
- root runtime config (
[runtime]) default_profile(if configured)- each CLI profile flag in order (
ab spawn -p one -p two)
That means later profiles can override scalar values from earlier layers, while arrays continue to append.
Typical usage
Set a baseline profile for daily use:
default_profile = "base"
Then add task-specific profiles per spawn:
ab spawn -r myrepo -s mysession -p rust -p gpg
Validation and inspection
Validate config:
ab dbg validate
Preview merged config/profile resolution:
ab dbg resolve
ab dbg resolve -p rust -p gpg
Portal integration
Portal config is defined under [portal] in the same file.
See Portal config and Portal wrapper contract.
JSON Schema
A machine-readable JSON Schema for the configuration is available for validation, IDE autocompletion, and tool integration.
Agent-box requirements
Runtime requirements
- Rust toolchain (workspace uses Rust edition 2024)
- Git
- Jujutsu (
jj) for JJ workspace flows - Docker or Podman for container execution
Portal-related requirements
wl-pasteavailable on host when using portal clipboard methodsagent-portal-hostrunning for wrapper/API operations
Optional tooling
- Nix / flakes for reproducible development shell workflows
- Nushell to run CLI reference generation script:
nu docs/scripts/generate-cli-reference.nu
Environment variables reference
Portal-related
-
AGENT_PORTAL_SOCKET- Used by portal clients/wrappers to select socket path.
- Resolution priority is env var first, then config/default.
-
AGENT_PORTAL_HOST_WL_PASTE- Used by
agent-portal-hostto override hostwl-pastebinary path.
- Used by
-
AGENT_PORTAL_HOST_GH- Used by
agent-portal-hostto override hostghbinary path.
- Used by
Logging
RUST_LOG- Controls tracing filter for
agent-portal-hostand other Rust binaries using tracing subscriber.
- Controls tracing filter for
Runtime passthrough
Variables listed in [runtime].env_passthrough are copied from host into container at spawn time.
Glossary
- Agent-box: CLI (
ab) that manages repository workspaces and container sessions. - Portal: host daemon + API/CLI for mediated host capability access.
- Wrapper: compatibility binary that presents familiar command behavior while forwarding to Portal.
- Session: named workspace instance used by Agent-box (
ab new/ab spawn). - Workspace type: JJ workspace or Git worktree.
- Policy decision: allow/ask/deny behavior for portal methods.
- Policy mode (
gh_exec): ask_for_writes, ask_for_all, ask_for_none, deny_all. - Prompt command: configurable dmenu-style command invoked by portal host for ask-mode approvals.
- Container override policy: per-container policy table keyed by container ID.