Trust agents with action, not access. An agent can ask; the broker decides; tools execute; secrets stay with the tools. The agent can reach the broker, and nothing else.
The security comes from physical boundaries (a single ingress, loopback binding, and secrets resolved at the workload), not from rules on paper. The agent carries only a low-power token to the broker; everything sensitive lives behind it.
- Broker: the authority boundary and the only address the agent has: auth, policy, request lifecycle, approval orchestration, and audit. One process, one SQLite file. (Internally split into module seams; see the architecture doc.)
- Toolyard: container lifecycle + per-tool secret resolution at container start.
- Tool template: what a new tool needs (drop a
toolyard.toml, runtoolyard up). - Approval adapter: a broker module that talks to nod; pluggable via docs/approval-surface-adapter.md.
- Agent client: the generic
toolstackCLI + skill (and an MCP adapter) an agent uses to discover and call tools through the broker; see client/SKILL.md. - Admin web app: the operator's control panel: run the broker, manage
callers/tokens/policies, author/edit tools (form →
toolyard.toml), and watch requests + audit. Local/homelab, loopback-only; see admin/README.md. - Operator apps: a cross-platform desktop shell (desktop/, an OS-WebKit window around the admin) and a native macOS app (macapp/, SwiftUI over the JSON API).
- nod: self-hosted approval surface for human-in-the-loop decisions.
- Secret backend: Infisical or SOPS.
- Tailnet: Tailscale Serve (or any VPN); the ingress boundary.
The fastest way to bring up the whole stack (broker + admin panel) is the one-box Docker setup:
cd deploy/docker
cp .env.example .env # set an admin password + vault passphrase
docker compose upOpen the admin panel at http://127.0.0.1:8780 and drive everything from there. For a native
single-host install under systemd, run sudo deploy/install.sh (it creates the service user,
the virtualenv, the admin password, and the unit) or follow deploy/README.md.
The broker, toolyard, and client are stdlib-only Python (3.11+, for tomllib). pip install -e .
puts the commands on your PATH:
pip install -e .
toolstack --helpThat installs toolstack (agent client), brokerctl (operator), toolyard (tool runner), and
toolstack-mcp (the client as an MCP stdio server); each also runs uninstalled as
python3 -m client.toolstack, and so on. Installing the CLIs does not start anything: they
talk to a broker you run via one of the paths above. Only the admin app carries runtime deps
(FastAPI/uvicorn), in its own venv; see admin/README.md.
The full path runs end to end (agent → broker → human approval → tool execution, with the broker never holding a secret) and it's hardened for a single-host deployment: a systemd state directory and sandbox, login throttling, bind and SSRF guards, runtime timeouts with partial-failure cleanup, pinned dependencies, and database backups. It binds loopback by default; reach it over a tunnel that terminates TLS. A few things are deliberately deferred: tmpfs secret injection, just-in-time grants, and a background approval-expiry sweeper.
- docs/walkthrough.md, the system end to end: how it runs, the security properties, and the evidence.
- docs/component-decomposition.md: architecture, broker internals, and trust boundaries.
- admin/README.md · broker/README.md · toolyard/README.md: per-component detail.