A capability graph for agent stacks.
Live demo · Quick start · CLI · MCP · Roadmap · Why Ambit · Theory
Ambit is a capability-accounting system for agentic infrastructure. It gives agents and their users a persistent model of what their combined environment can actually do: which capabilities exist, what they depend on, what is one step away, what is decaying, and what would break if something disappeared.
The premise is that the meaningful capabilities of an AI system do not reside entirely in the model. They arise from composition — models, tools, credentials, machines, networks, memory, schedulers, humans, policies, persistent processes — so the question worth answering is not what the model knows or what appears in a config file, but:
What can this human-machine system actually cause to happen right now?
It is built for the point where an agent setup stops fitting comfortably in your head.
You have multiple models. MCP servers. Skills. Subagents. Local machines. Hosted services. Credentials. Scheduled jobs. Maybe a homelab.
The individual pieces are visible in configuration files. Their combined action space is not.
Ambit makes that action space explicit.
Agent systems are becoming capable through composition.
A shell is one capability. Tailscale is another. Docker is another. Monitoring is another. Together, under the right conditions, they may amount to:
safely diagnose and recover a failed service without human intervention
No individual config entry says that.
Likewise, the presence of a tool does not prove an agent can reliably use it. A model may technically support tool calling and fail in practice. An agent may hold credentials for an action it is not authorised to take. A machine may have idle compute that no agent can actually reach.
As agent environments grow, several different questions collapse into one:
What can this system actually do?
Ambit treats that as a graph problem. It models capabilities, dependencies, costs, maturity, and composition so that both humans and agents can reason over the environment they share.
Configuration tells you what is declared. Ambit tries to tell you what those declarations amount to.
A capability in a tool registry looks like "GitHub access: yes." The useful form is closer to:
Can diagnose a failing service, modify its repository, deploy a fix, verify recovery, and report the intervention — because the system currently has repository write access, shell execution, deployment credentials, monitoring visibility, network reachability, persistent execution, and the required human authorisation.
That second description is effective capability, and it is the object Ambit is built around. Getting there means keeping apart seven things that ordinary registries collapse into one:
| today | ||
|---|---|---|
| Available | something appears to exist | ✅ |
| Reachable | all necessary dependencies are currently accessible | ✅ |
| Composed | several lower-level capabilities together make a higher-order action possible | ✅ |
| Verified | the capability has actually succeeded | ✅ where a check is declared |
| Authorized | the system has permission to use it | ✅ per action, declared, not enforced |
| Delegated | a human or another agent supplies a missing step | ✅ people are nodes; who to ask is roadmap |
| Persistent | it can operate beyond the current interaction | roadmap |
Capability change is now recorded over time — see the ledger — which is the accounting half of that table rather than a seventh state.
Six of seven, with the caveats stated in the table rather than hidden: checks exist for eight capabilities, and authority is described rather than mediated. The roadmap is the rest.
$ tt verify # run the declared checks, record what happened
checked: 8 · verified: 8 · failed: 0
Local Runtime verified 23ms reliability 4/4
$ tt authority # reached is not the same as permitted
autonomous File Editing · Parallel Execution
needs approval Shell Execution · Version Control · Continuous Delivery
forbidden Secret Management
$ tt actions version-control # and permission is finer than a capability
exercisable read_repository · commit_changes
needs approval push_branch · merge_to_default
$ tt plan offline-capable
goal: Offline Capable · steps: 2 · estimated setup: 25m
1. Embeddings 2. Local EmbeddingsThe compressed form of the same point:
installed ≠ callable ≠ working ≠ reliable ≠ authorized ≠ appropriate
Ambit reads your agent configuration and maps discovered components onto a capability graph. It ships with a curated seven-era capability model:
1 Foundation shell · files · code intelligence · version control
2 Model Access hosted inference · local runtime · local tool calling
extended context · model routing
3 Tool Use tool protocol · browser automation · web research
secret management · data access
4 Memory embeddings · vector store · retrieval
persistent memory · context compaction
5 Autonomy subagents · skills · parallel execution
scheduled work · notifications
6 Assurance tests · review loop · evaluation
observability · continuous delivery
7 Sovereignty local embeddings · offline operation
private data · self-hosted infrastructure
Each node is in one of three states:
- Reached — your environment provides evidence for it, and the graph records which capability provided that evidence
- Next — its prerequisites are satisfied and nothing is configured yet
- Blocked — an implementation exists but a prerequisite is missing
Ambit also records explicit dependency edges from your configuration: provider → model, and model → agent for agents pinned to one.
Every node says what kind of thing it is, and every edge says what the relation means:
capability an action the system can bring about — the curated model's nodes
action one concrete thing a capability confers, or that a person supplies
provider what supplies a capability — an MCP server, a skill, a tool
resource what a provider needs — a model, an inference endpoint, a machine
actor a person: authority, money, judgement, physical access
runtime an agent runtime, which contributes providers rather than owning them
provides · contributes · requires · optional · authorizes · runs_on
Ten capabilities declare a contract.can — the actions they confer — and each becomes a node with its own authority. That is what lets the model say may read the repository, may not merge to its default branch, which the coarse node cannot. tt actions reports them; the visualizer leaves them out of the era columns on purpose, because legibility is the point of that view.
Alongside state, each capability carries a lifecycle derived from its providers and its recorded evidence:
unknown → detected → configured → verified → reliable
↓
degraded → broken
The two are separate columns because reachable and working are different claims. A capability whose check has started failing reads as broken and stays in the frontier.
CONFIG — your configuration as a graph, with diagnostics. |
DOCS — nine terms carry all the meaning; the same definitions back tt explain.
|
Any view is linkable: ?view=tree, ?layout=constellation, ?docs=open.
The view updates itself when the graph changes underneath it. /api/events streams AG-UI StateSnapshot events over SSE — the state subset of that protocol, chosen so the transport an agent would use to propose a change and a human to approve it is a standard one rather than invented. Runs, messages and tool calls are not implemented.
git clone https://github.com/zz-plant/ambit.git
cd ambit
./bootstrap.shBootstrap installs dependencies, discovers your environment, and seeds the graph:
Installing...
✓ 168 capabilities
┌─ Toolchain ───────────────────────────────────────────┐
│ 156/168 capabilities, 8 domains, 33 combos
│ █████████░ ai-ml 22/26
│ ████████░░ backend 6/8
│ █████████░ devops 7/8
│ ██████████ frontend 1/1
│ █████████░ infra 26/28
│ ██████████ meta 88/89
│ ████████░░ quality 5/6
│ █████░░░░░ security 1/2
└───────────────────────────────────────────────────────┘
To inspect what it would find without changing anything:
./bootstrap.sh --dry-runTo launch the visualizer:
./bootstrap.sh webBootstrap links the tt command into ~/.local/bin when that directory is on your PATH. If it isn't, bootstrap prints the one-line ln -s to run instead; until then the CLI works in place as ./cli.js.
Homebrew installs the CLI on its own, as tt. There is no clone, so build the graph with tt seed:
brew install zz-plant/tap/ambit
tt seedThe graph is a local SQLite file — ~/.local/share/ambit/graph.db for an installed copy, or the checkout itself when you cloned. tt where prints the path, and TOOLCHAIN_DB overrides it. The engine, the MCP server and the visualizer all read the same one. Nothing is uploaded.
Requires Bun for the visualizer and server, Node 22+ for the engine and CLI. Bootstrap checks for both before doing anything. The visualizer needs a checkout; an installed copy carries the engine, CLI and MCP server.
Without an agent config, bootstrap still seeds the curated capability model and says so — you get the graph with nothing of yours in it yet, rather than an error. Point it at your own config with OPENCODE_CONFIG, or map a different format with CONFIG_MAPPING (see Other configurations).
Run tt with no arguments and it shows where you are, what is one step away, and what to do next. The full set:
Explore stats · context · health · profile · export · explain
Verify verify [id] · evidence <id> · authority · actions [id]
Maintain decay · diff · trend · prune · prune <id> · ledger · since · failed · deficits
Plan plan <id> · simulate <id> · propose <id> [n] · proposals · proposal <id>
Act approve <id> <who> · apply <id> · rollback <id>
Plan near · combos · fork · insight
Analyze bottlenecks · impact <id> · spof · budget <setup> <tokens>
| asks | |
|---|---|
tt near |
What am I one or two dependencies away from being able to do? |
tt bottlenecks |
Which capability would unlock the largest part of the graph? |
tt impact <id> |
What becomes unavailable if this disappears — and what survives on another provider? |
tt spof |
Which capabilities have only one provider — and which actions has only one person? |
tt actions <id> |
Which concrete actions does this confer, and which of them may run unattended? |
tt fork |
Which nearby path has the best trade-off between setup cost, regret, and downstream leverage? |
tt decay |
Which parts of the system appear to be rusting? |
tt since |
What became reachable since a past date — and what emerged rather than being added? |
Real output — one dependency away, and the dependency it names gates four further capabilities:
$ tt near
Local Embeddings
missing: 1
met count: 1
total required: 2
met maturity: 70
investment: Add EmbeddingsMore useful is where composition fails. Capabilities you have already half-built carry the reason:
Retrieval configured, but Vector Store is not in place yet
Offline Capable configured, but Local Embeddings is not in place yet
Self-Hosted Stack configured, but Observability is not in place yet
Nothing declared those. They fall out of the dependency structure, and they are invisible in every file you own.
Humans supply what machines cannot — legal authority, money, physical access, judgement — so they are nodes rather than users of the graph. An actors block declares them:
{ "actors": { "kanav": {
"provides": ["physical-access", "approve-purchases"],
"authorizes": ["combo:continuous-delivery"] } } }provides becomes a capability only that person supplies. authorizes becomes a hard prerequisite, so a plan says whose step it is:
$ tt plan continuous-delivery
goal: Continuous Delivery
requires a person: Kanav
steps: 1 · estimated setup: 30mA plan that hides the human step reads as autonomous when it is not. A capability chain can therefore run:
diagnose hardware failure → request replacement → human approves expenditure
→ vendor ships component → human installs it → agent configures it
→ monitoring verifies recovery
The capability belongs to the human-machine system, not to either half — which lets partial, structured autonomy be described as it actually is, rather than forced into "fully autonomous" or "human controlled".
tt simulate computes the frontier as it would be, without touching anything. What makes it worth reading is the second line:
$ tt simulate vector-store
frontier: 21 → 23
acquired: Vector Store
unblocked: Retrieval # already provided, waiting on the prerequisitett propose turns that into a reviewable draft — ordered steps, the alternative chosen, and what it costs beyond time:
$ tt propose retrieval
Retrieval · 25m
Embeddings nomic-embed via local runtime none / local
Vector Store pgvector on existing Postgres none / local
simulated frontier: 21 → 24
executable: falseChoosing the hosted alternatives (tt propose retrieval 1) takes it to 13 minutes, at a per-token bill and a data boundary.
Where an acquisition genuinely is a config change, the step carries a declarative patch and Ambit derives its undo — removing what it adds, or restoring what it overwrites. Anything needing an installer gets no inverse, and a proposal is applicable only when every step has one.
$ tt approve prop-msrrv9c2 kanav
approved by: Kanav · applicable: true
Approved. Applying is not implemented — this records permission, not action.$ tt apply prop-msrsqzij
applied: true · keys: mcp.fetch
backup: opencode.json.ambit-prop-msrsqzij.bak
$ tt rollback prop-msrsqzij
removed: mcp.fetch # git survives — the inverse reverses only thisApply only edits configuration, and cannot do otherwise. A step carries a declarative patch or nothing; there is no field that holds a command. It refuses a proposal no person approved, and any step without an inverse. It backs up first, and if verification fails afterwards it rolls back automatically and says the change was reversed.
applicable and executable are separate claims: the first says a proposal could be applied safely, the second says apply does not exist. Approval is CLI-only and not exposed over MCP — an agent may draft and preview, but approval is the human's act and should not be reachable by the thing being approved.
capabilities holds the present state and is overwritten on every seed, so on its own the graph can only say what the system can do now. Every seed also records the whole frontier, which lets it answer what was reachable at a past date:
$ tt since
frontier then: 13
frontier now: 19
gained: Embeddings · Local Embeddings · nomic-embed-text
emergent: Model Routing · Offline Capable · SubagentsOne embedding model was added. Six capabilities moved. The three under emergent became reachable although nothing providing them was added — their prerequisites were satisfied by something else entirely. Offline Capable was already provided by an agent that did not change.
That is the entry a per-component changelog structurally cannot produce, because no single change explains it. Accumulated capacity to act is a graph property, and this is where it shows up.
A fourth class, vocabulary, exists to keep the first three honest. When Ambit starts modelling a part of your system it did not model before — a new action on a contract, or a capability added to the curated tree that your existing tools already provide — the node is new and nothing about the machine changed. Those are described and not counted, so frontier_now stays comparable with frontier_then:
$ tt since
frontier then: 21
frontier now: 21
vocabulary: 12 act:shell-execution/run_command · act:file-editing/write_file · …Without it, upgrading Ambit would read as a dozen capabilities acquired on a machine where nothing happened — which is exactly what this table exists not to do.
Every command prints for a person by default and takes --json for scripts.
Ambit ships an MCP server exposing the graph directly to an agent:
tt_stats tt_context tt_recs tt_cap tt_decay tt_combos
tt_diff tt_health tt_impact tt_budget tt_trend tt_near
tt_insight tt_profile tt_prune tt_fork tt_bottlenecks
tt_verify tt_evidence tt_authority tt_plan tt_since tt_ledger
tt_blocked tt_deficits tt_spof
tt_simulate tt_propose tt_proposals tt_proposal
The second group is the capability lifecycle: is this real, may I act, what is missing, and — when the answer is nothing here can do that — recording it so a deficit hit repeatedly becomes visible as infrastructure that should exist rather than a wall to work around again.
Register it with Claude Code:
claude mcp add ambit -- node --experimental-sqlite /path/to/ambit/src/mcp/server.tsOr in opencode.json — and in any other runtime that takes a stdio command:
{ "mcp": { "ambit": {
"type": "local",
"command": ["node", "--experimental-sqlite", "/path/to/ambit/src/mcp/server.ts"],
"enabled": true } } }It reads the same database bootstrap.sh writes. Set TOOLCHAIN_DB if you keep the graph somewhere else; the engine, the MCP server and the visualizer API all resolve that one variable.
This matters because Ambit is not only a dashboard for the user. An agent should be able to ask:
- What infrastructure am I operating inside?
- Which capabilities are already available?
- Why is this task outside the current action space?
- Is this limitation local to the task, or are we repeatedly missing the same primitive?
- Which existing component is a single point of failure?
The agent no longer has to reconstruct the environment from conversational context every session.
Ambit represents agent runtimes rather than being one. A runtime becomes a node, and everything it contributes hangs off it — so two runtimes configuring the same MCP server produce one capability with two providers, not two capabilities.
bun run scripts/adapters/claude-code.ts # what Claude Code provides
bun run scripts/adapters/claude-code.ts --seed # add it to the graph
bun run scripts/adapters/hermes.ts # the same for HermesThe Claude Code adapter reads ~/.claude.json and ~/.claude/ — MCP servers global and per project, skills, subagents, a pinned model — plus the authority the runtime states outright: permission mode, and how many allow, deny and ask rules are in force. Rule names only; an allow rule can name a path, and those are not Ambit's to copy into a graph you may export.
Against a real install, that yields:
runtime:opencode — contributes 127 capabilities
runtime:hermes — contributes 32 capabilities
shared by both — mcp:fetch · mcp:filesystem · mcp:git · mcp:sequential-thinking
tt impact runtime:hermes then answers what would be lost if that runtime went away — and the answer is smaller than its capability count, because the shared four survive.
The adapter also reads what a config file cannot infer but the runtime states outright: Hermes reports approvals: manual, cron_mode: deny, eight messaging surfaces, a policy engine, and zero scheduled jobs — which is the difference between a capability that persists and one that lasts a session.
Hermes has no machine-readable config export today, so the adapter reads its documented paths. That is a stopgap: the durable contract is for runtimes to publish their capability surface and for Ambit to consume it.
Four views: ERAS (capability model by era), CONSTELLATION (3D), ORBITAL (concentric by type), FLAT (force-directed).
The visualizer is a view over the model, not the product's ultimate abstraction. The underlying graph is meant to stay useful when no human is looking at it.
Agent capabilities do not stop at the model boundary. A local GPU, NAS, browser worker, Proxmox host, database, or cloud account can all contribute to what the system can accomplish.
Ambit scans infrastructure from an explicit local manifest (INFRA_MANIFEST, default ~/.config/opencode/infrastructure.json). With no manifest it returns an empty scan rather than an error — no host addresses are baked in.
The manifest is not specific to servers. A device is anything that can act — a Pi, a GPU host, a robot arm, a sensor, a decoder — and they seed as first-class nodes in a physical domain. Whether that generalisation is the right one is argued in the affordance frontier; what is implemented is that the model does not assume software.
The goal is not another homelab inventory. It is to treat infrastructure as capability-bearing:
GPU node
├─ local inference
├─ embeddings
├─ batch evaluation
└─ private processing
A machine matters because of the actions it makes reachable.
A growing agent environment produces capabilities that no individual component declares:
monitoring + shell + service manager + network reachability
+ notifications + appropriate authority
→ diagnose and recover a failed service
Ambit's direction is to represent and verify those compositions explicitly — moving from which tools are installed? toward which goals are reachable?
Detection is a first approximation. Today Ambit infers capability from configuration and naming patterns. The deeper model is empirical:
unknown → detected → configured → demonstrated
→ repeatedly verified → degraded → unavailable
A capability should mean more than "something with the right name exists". It should mean the system has evidence the action can be performed under known conditions. That distinction matters more as agents gain authority.
That lifecycle is now a stored column rather than an aspiration — see what a node is. What it does not yet do is gate anything: tt plan will route a path through a broken capability without complaint.
Being technically capable of an action should not imply permission to perform it.
CAN OBSERVE autonomous
CAN PLAN autonomous
CAN SIMULATE autonomous
CAN EXECUTE approval required
CAN VERIFY autonomous
CAN ESCALATE autonomous
This lets technical capability accumulate without silently broadening delegated authority. It is not a restriction on the capability model — it is what makes a larger capability surface governable.
Authority is recorded per action, and from two sources. The curated model says what an action is like in general; the runtime that would execute it says what it permits here — Hermes publishes approvals.mode and approvals.cron_mode, Claude Code publishes permissions.defaultMode, and both adapters pass them through. Where the two disagree the narrower wins, and tt authority names which source narrowed it.
Two limits worth stating plainly. Nothing is enforced — Ambit describes authority, it does not mediate action. And scope is declared, not checked: a grant can say repo:owner/name, and nothing verifies that the scope is the one an action would touch.
The interesting case is not "you should install another developer tool." It is an agent recognising:
I keep encountering environmental limitations that neither of us can represent or fix systematically.
That happens when every session rediscovers the same machines and permissions, when unrelated tasks fail on the same missing primitive, when capabilities exist but their composition is unclear, or when the distinction between possible and permitted lives only in conversation.
The point is not to make the agent indiscriminately more powerful. It is to give the joint system a durable representation of the environment it is already building.
As AI systems acquire more persistent means of action, capability increasingly becomes a systems property rather than a model property. Effective agency can grow through thousands of small integrations without any dramatic increase in model intelligence — credentials, persistence, schedulers, tools, machines, memory, delegation, and network reach all contribute.
A useful design norm follows:
No increase in effective capability without a corresponding increase in legibility, verification, and governability.
Ambit does not attempt to solve alignment. It addresses a narrower problem: the growth of effective agency outpacing our ability to represent, bound, verify, and revoke it.
A mature capability graph could make several invariants explicit — no autonomous acquisition of new authority; no delegation beyond the delegator's; no persistent worker without an owner and a kill path; no capability promotion without verification; no irreversible action without a recovery path.
The larger idea is simple: make capability accumulation explicit rather than accidental.
There is a longer version of this argument in why-ambit.md, and the theory it rests on — affordances as relational, robotics and BCIs as the cases that test the abstraction, and the intellectual genealogy — in affordance-frontier.md.
agent config
infrastructure manifest
│
▼
Ambit engine
│
▼
SQLite graph
│
├──► CLI tt
├──► MCP server 31 tools, JSON-RPC over stdio
├──► visualizer API Bun.serve, port 3001
└──► tracking plugin config-change events
TypeScript · Node 22 with node:sqlite · Bun server · React + Vite · Three.js · JSON-RPC MCP · local-first state.
The server reads and writes opencode.json, so two invariants hold:
- Loopback only. It binds
127.0.0.1and rejects non-local origins before routing — CORS headers alone are insufficient, because a simple request skips preflight and reaches the handler regardless. - It cannot create configuration entries. An MCP entry carries a command your agent runtime executes, so creating one over HTTP would be remote code execution. The API can toggle an MCP and edit an existing agent's description or model; adding a server generates a snippet you paste yourself.
Ambit should become more capable without making its own control surface casually dangerous. There is no telemetry.
OpenCode is the default input format, but the engine is not tied to it. Map another JSON configuration with CONFIG_MAPPING:
CONFIG_MAPPING='{
"config_keys": {
"tools": { "type": "tool", "domain": "devops", "desc_field": "description" }
}
}' \
OPENCODE_CONFIG=my-project.json \
./bootstrap.shThe default mapping covers MCP servers, agents, providers, commands, and skills.
The eventual goal is broader: different agent runtimes should attach to the same capability model rather than each maintaining a private and incompatible understanding of the environment.
Ambit is early. Today it is strongest as capability discovery, dependency mapping, maturity and decay analysis, failure-cascade analysis, near-miss discovery, budget-aware planning, and an MCP-readable external model of an agent environment.
Verified capability and explicit authority are built: the graph distinguishes capability from provider from resource from actor, records what each action confers, and holds authority from both the model and the runtime that would execute the step. What remains of the larger direction — goal-to-capability planning from a free-form goal, comparing acquisition paths by risk and lock-in, enforcement rather than description, and scope that is checked rather than declared — is the architecture the current graph is meant to grow toward. See ROADMAP.md.
The point is not to pretend those pieces exist. It is to build toward them without changing the fundamental object. That object is the system's ambit: the set of actions presently reachable by the combined capabilities of its human, agents, tools, and machines.
Fork → branch → commit → PR. See AGENTS.md for conventions, the typechecking setup, and the security invariants the server must preserve.
MIT — see LICENSE.


