Configure · package · govern · run · observe · 13 agent CLIs
Build, govern, and run
your agent stack.
Define servers, skills, instructions, settings, hooks, plugins, profiles, and secrets once. AgentStack turns that source of truth into native config across 13 agent CLIs, installs and pins capabilities, activates profiles per task, enforces machine-owned policy, runs agents under optional lockdown, and records the evidence you need to audit and optimize the stack.
$ agentstack connect --all --write # once: the gateway, in every CLI
$ git clone some-repo && cd some-repo # declares servers you've never seen
agent session starts…
✗ untrusted — no servers spawned, no secrets resolved
$ agentstack trust . # you review, then consent
▶ demo: runs `python3 ./server.py`
✓ trusted at sha256:945b4b…
agent → demo.echo ✓ ok brokered, logged
agent → demo.delete_all ✗ denied rule "!delete_*" — YOUR machine policy
# both brokered outcomes are in the audit log
Why this exists
Your agents run whatever your repos tell them to.
Every skill, MCP server, and agent config you adopt is
unreviewed code plus instructions,
wired into a process that holds your credentials, your shell, and the
network. Installing one is npm install with an agent
attached — except with no lockfile, no review gate, and no record of
what it did. Three gaps follow from that:
One clone away
A repo can declare MCP servers, and the moment an agent session
starts, they want to run — commands you never read, with your
keychain in reach.
→ Here, a clone is
inert until you trust its exact bytes. Any edit
re-gates it.
One prompt away
An injected instruction can steer a well-behaved agent into using a
legitimate tool against you — deleting, leaking, phoning home.
→ Here, your
machine policy — which no repo can loosen — fences
tools, secrets, and egress; --lockdown removes the
network route entirely.
Invisible afterwards
When something does go wrong, most setups can't answer the first
question a reviewer asks: what did the agent actually
call?
→ Here, every brokered
call, denial, and secret touch lands in an
audit log you can read back per run.
Honestly scoped
No tool can make agent use risk-free: hosts you allow stay allowed,
and payloads aren't inspected.
→ That's why every mode
carries an honest posture label — and if
you run one agent with one hand-managed server, you may not need
this yet. The moment capabilities come from repos you didn't
write, you do.
Install
curl -fsSL https://raw.githubusercontent.com/Tarekkharsa/agentstack/main/install.sh | sh
One static binary, no runtime dependencies. From a checkout:
cargo build --release && target/release/agentstack self link.
The trust gate
Repo-declared servers wait for your consent.
Register the gateway once (agentstack connect --all --write)
and every repo you open brings its own servers — no files copied in. But
a repo you haven't reviewed is inert: none of its MCP
servers are spawned or contacted, no secrets resolved. You review what it
declares, trust it (pinned to a consent digest of the
manifest, local overlay, and lockfile, so any later edit re-gates it), and from
then on every brokered call passes two firewall layers —
the repo's [policy.tools], and your own machine-level rules
that no repo can see, shadow, or loosen — and lands in the call log.
$ git clone some-repo && cd some-repo # declares servers you've never seen
$ agentstack mcp --auto-project # an agent asks: which project servers can I use?
✗ untrusted — none of its servers are spawned or contacted
$ agentstack trust .
▶ demo: runs `python3 ./server.py` # you see exactly what it would run
✓ trusted at sha256:945b4b… # editing the manifest re-gates it
agent → demo.echo ✓ ok brokered through the gateway, logged
agent → demo.secret_read ✗ denied repo [policy.tools] firewall
agent → demo.delete_all ✗ denied YOUR machine policy — "*" = ["!delete_*"]
every call → ~/.agentstack/audit/calls.jsonl (tool · outcome · latency)
$ agentstack lib sync # your library syncs machine-to-machine over git
✗ refusing to sync — 'Authorization' looks like a literal secret
✓ make it a ${REF} and it travels safely — values never leave the keychain
Condensed from a real run — reproduce the whole gate:
docs/trust-gate-demo.sh.
The whole story, in one binary: an untrusted repo's agents can touch
nothing you didn't review, and every capability call is firewallable
and logged (the documented limits say exactly what trust does and doesn't cover). Prefer a drop-in proxy? --transparent
advertises every policy-filtered upstream tool straight in
tools/list, so any standard MCP client works with zero
agentstack knowledge — the default compact mode keeps them behind one
search tool so agent context stays small.
The sandbox
Don't trust the agent? Take away the network.
The trust gate decides what an agent may do.
agentstack run --sandbox decides what it can do:
the harness runs in a container, its traffic routes through an egress
proxy enforcing your machine [policy.egress], and every
allow and block lands in a per-run flight recorder you read back with
agentstack report. The workspace mounts read-only unless
your [policy.filesystem] says otherwise — the kernel
enforces the bind, not the harness.
- --sandbox proxied · direct route open
- The container gets an ordinary bridge network with its
HTTPS_PROXYpointed at the agentstack proxy: configured egress is filtered and recorded. A process that ignores the proxy env still has a direct route — that caveat is what the next flag removes. - --lockdown no route out
- The container's only network is an internal one whose single reachable peer is the egress-proxy sidecar. No host route, no internet, no DNS beyond it. An agent that ignores the proxy reaches nothing — the confinement is topology, not convention. Verified live on Docker through the real binary.
$ cat ~/.agentstack/agentstack.toml # YOUR machine firewall
[policy.egress]
"*" = ["!blocked.invalid"] # no repo can loosen this
$ agentstack run --lockdown shtest -- -c 'unset HTTPS_PROXY; curl example.com'
posture: LOCKDOWN / ENFORCED · NO DIRECT ROUTE
🔒 no host route, no internet — the only peer is the egress sidecar
BLOCKED # ignoring the proxy reaches nothing
$ agentstack run --lockdown shtest -- -c 'curl https://blocked.invalid/steal'
✗ refused at the sidecar — and recorded
$ agentstack report r-0859dcee73
Posture LOCKDOWN / ENFORCED · NO DIRECT ROUTE
Egress
✗ shtest → blocked.invalid denied by rule "!blocked.invalid" (machine policy)
Condensed from a real run. The full script is runnable (needs Docker):
agentstack-test/demo-lockdown.sh.
The proxy is picky on purpose: hostnames are normalized before matching, a tunnel's TLS SNI must equal the CONNECT host (no domain fronting), any name resolving to a loopback, private, link-local, or cloud-metadata address is refused (no SSRF into your own network), and a per-run credential means only its own sandbox can use it. This boundary was swept by a two-reviewer security review — all five High and all seven Medium findings are closed; the remaining accepted debt is tracked. Honest scope: hosts you allow stay allowed — the claim is unapproved egress is blocked, never "exfiltration is impossible."
Experimental execution primitive
Let generated code compose tools—not inherit your machine.
A sandbox-enabled build can expose tools_execute: one bounded
TypeScript program receives JSON input and only the exact MCP tools named
in its grant. It runs non-root in a read-only, resource-limited Docker
container with no workspace, credentials, package installation, or
direct network route. Calls return through the same policy-enforcing
gateway and appear beneath the execution in agentstack report.
# ~/.agentstack/agentstack.toml
[experimental]
tools_execute = true
[experimental.tools_execute_limits]
timeout_ms = 30000
max_calls = 40
max_output_bytes = 131072
allowTools = ["github__get_issue"] # exact names, no wildcards
✓ x-… → github__get_issue # gateway checked + recorded
Off by default, Docker-only, and never enabled by a repository manifest. A focused implementation review and regression-hardening pass completed on 2026-07-13. It remains experimental while executor-specific SBOM/attestation and longer-running soak evidence are outstanding. Read the exact contract and enforcement matrix.
What's enforced where
"Trusted" is not "confined." Here's the difference.
Trusting a project approves its current manifest, local overlay, and lockfile consent digest for automatic loading on this machine; lock verification separately catches pinned capability drift. What actually confines a run is its execution mode. Each mode carries a posture label, so a host run and a lockdown run never read the same:
--lockdown closes that route.| Dimension | hostADVISORY | gatewayBROKERED | --sandboxPROXIED | --lockdownNO ROUTE |
|---|---|---|---|---|
| Tools | unsupported | enforced | unsupported | unsupported |
| Egress | coarse | coarse | enforced * | enforced |
| Secrets | enforced | enforced | coarse | coarse |
| Filesystem — write | unsupported | unsupported | coarse | coarse |
| Filesystem — read | unsupported | unsupported | coarse | coarse |
| Audit / recording | unsupported | enforced | coarse | coarse |
enforced * — for proxied traffic only:
--sandbox's bridge network leaves a direct route a
proxy-ignoring process could use; --lockdown removes it.
Read every "enforced" with the ceiling in mind: it means the disallowed
action is prevented at runtime — never that the allowed action is
safe. An enforced egress allowlist blocks hosts you didn't approve; it
doesn't inspect payloads, and it permits traffic to every host you did
approve (the model API included). agentstack restricts destinations
and records decisions; it can't guarantee sensitive content never leaves
through an allowed destination. The full per-cell reasoning —
including where --sandbox's bridge network stops short of
--lockdown's topological confinement — is in the
enforcement matrix.
Portable config
The same reviewed file, rendered into every CLI
Everything above runs through the live gateway — zero files copied. When
you want native config files instead (or as well), the same manifest
renders them. And you don't start from a blank page: init
reads the agent config already on your machine and turns it into a
manifest you can review, commit, and share. (Nothing installed yet?
init writes a commented starter manifest instead — no dead
ends on a clean machine.)
Serves — and renders native config for —
-
agentstack init
Imports the servers and skills you already have; inline tokens become
${REF}s in your OS keychain. -
agentstack bootstrap
Preflight: which CLIs are installed, which skills and secrets are missing, what would change.
-
agentstack apply
Preview each CLI's config diff, confirm to write. Writes are atomic, backed up, and tracked —
restoreundoes them. -
agentstack doctor
Any time something feels off. Every warning names the exact fix command.
$ agentstack init
🔍 Detected 6 CLIs: Claude Code · Codex · Copilot · Gemini · OpenCode · Pi
📥 Imported 1 MCP server from existing configs
✅ Wrote .agentstack/agentstack.toml
$ agentstack bootstrap
✓ manifest validates · 6 adapters installed · no missing secrets
$ agentstack apply --write
Claude Code ✓ up to date # it already had the server
Codex CLI + [mcp_servers.filesystem] ✓ wrote 1 server
Gemini CLI + "filesystem": { … } ✓ wrote 1 server
…Copilot, OpenCode, Pi — the same server, each in its own syntax
$ agentstack apply
✓ nothing to write — every target in sync
Condensed from a real run. Reproduce it fenced (never touches your real
configs): agentstack-test/demo-firstrun.sh.
The manifest
One file, reviewed like code
version = 1
[servers.github]
type = "http"
url = "https://api.githubcopilot.com/mcp/"
headers = { Authorization = "Bearer ${GH_PAT}" } # resolved per machine, never stored
[profiles.backend]
servers = ["github", "kibana"]
skills = ["sql-review"] # resolves from your central library
[targets]
default = ["claude-code", "codex"]
Secrets resolve locally — process env, varlock,
OS keychain, or .env — and unresolved secrets block writes,
so placeholders never leak into live config. A lockfile pins every skill
and server by digest; install --locked reproduces the setup
in CI.
Rendered files
Pick where artifacts live
You always commit the intent (manifest + lock). The rendered artifacts
(.mcp.json, .claude/skills/) are a per-project choice:
- static default
- Artifacts (
.mcp.json,.claude/skills/, and the compiledCLAUDE.md/AGENTS.md) sit on disk, kept out of git by a managed.gitignoreblock that only ever covers files agentstack itself wrote — so a repo tracks only your.agentstack/intent and a hand-maintained file is never hidden. Works however you launch your tools. - clean-at-rest
- Nothing generated exists between sessions.
agentstack run <cli> --profile <p>injects on launch and reverts on exit;git statusstays silent. - zero files
- Nothing generated at all — the gateway serves each repo's servers on the fly, behind the trust gate above.
Teams & CI
Clone, bootstrap, done
git clone <repo>
agentstack bootstrap
agentstack secret set GH_PAT # local only; never committed
agentstack apply --write
In CI, the trust gate is two commands — or the one-line GitHub Action:
agentstack install --locked # fail if sources drifted from the pinned lock
agentstack doctor --ci # fail on errors, drift, policy, unsafe content
Provenance, too: agentstack sign writes a detached ed25519
signature over the lockfile, and agentstack verify checks
it — so CI can prove the pinned content is the content someone actually
reviewed.
Going further
When you want more than the loop
Choose the right boundary
Our recommendation for composing profiles, delivery, trust, policy, isolation, and evidence—plus the cases where the default should change.
Decision guideCentral library
One managed home for skills and servers; projects select by name and stay digest-pinned. Install from a local dir or a git subdirectory, sync across machines, and pull ready-made skills from the shipped catalog.
Visual walkthroughUsage insight
agentstack analyze reports what you actually call, from the
runtime audit log, and flags library skills and servers you installed but
never use — read-only and local, so pruning is data-driven.
Feature reference
The complete tested inventory: vendor packs, MCP firewall, call audit log,
optimize, plugin recipes, live runs, code mode, and the full
command list.
The no-terminal path
The dashboard lifecycle — discover, add, secrets, apply, verify, undo — from a local, token-gated dashboard, with read-only Proxy and Insights panels.
Dashboard guideVendor packs
agentstack add from git:github.com/acme/pack@v1.2.0 installs a
versioned MCP + skills + house-rules bundle — policy-gated and
content-scanned before anything is written.
Support any CLI, no rebuild
Each of the 13 agents is one data-driven YAML descriptor. Drop your own into
~/.agentstack/adapters/ to add or override a harness — validated
first, and a broken drop-in is skipped with a warning, never fatal.
$ agentstack add from git:acme/pack@v1.0.0 --write
✓ installed pack 'acme' — servers + skills, secrets stay ${REF}s
$ agentstack apply --write
✓ the whole pack spreads to every CLI on this machine
acme__search_docs ✓ ok
acme__delete_index ✗ refused [policy.tools] rule "!delete_*"
$ agentstack audit --calls
2 calls · 2 tools · 1 denied by policy — digests, never values
$ agentstack upgrade acme --yes --write # vendor ships v1.1.0
✓ upgraded pack 'acme' — previewed, re-pinned @v1.1.0
The closed loop in under a minute, condensed from a real run:
agentstack-test/demo-closed-loop.sh.