Skip to content

Capability tokens

A capability token is how you hand an agent bounded authority: a signed object that says who the agent is, what paths it may touch, which operations it may perform, and when that all expires. This page is a how-to — issue, scope, renew, revoke, and delegate tokens. For the model behind them, see agent sessions; for the spec, see Policy & Tokens §6.

Built across the CLI and agent edge

The token primitive and the tovio agent new / show / renew / revoke / list lifecycle are implemented in the Rust core. Delegation is end-to-end: the MCP issue_sub_token tool mints a narrower leaf, and that leaf opens its own later session with the full root-to-leaf chain re-verified and recorded in provenance. Relay-side write enforcement is implemented in the Forge. The commands below show the human/CLI surface; the CLI issues the §10 default scope, and narrower scopes come from delegation.

Issue a token

One command issues a token signed by your human identity, with safe defaults. The agent's name, the model it runs, and a short task description are required; the expiry is given in hours:

$ tovio agent new cgm-sync --model anthropic:claude-sonnet --task "stabilize the CGM sync" --expires-in 8

--json for the machine-readable result an orchestrator would parse:

$ tovio agent new cgm-sync --model anthropic:claude-sonnet --task "stabilize the CGM sync" --expires-in 8 --json
{
  "token_id": "cap_7r4qy9m2x8k3v6bd0n1p5h",
  "object": "3f9c…",
  "agent": "did:tovio:agent/cgm-sync",
  "model": "anthropic:claude-sonnet",
  "authorized_by": "did:tovio:dustin",
  "path_scope": ["**"],
  "denied_path_scope": ["config/production/**", "secrets/**"],
  "branch_scope": ["agent/cgm-sync/**"],
  "secret_clearance": false,
  "issued_at": 1782482400,
  "expires_at": 1782511200,
  "revoked": false,
  "signature_valid": null
}

Timestamps are Unix seconds. The operation allow/deny lists and capability flags are set by the safe defaults below and are part of the signed token; the CLI's JSON does not echo them. An MCP session's effective_scope tool echoes the allowed operations alongside the scope globs --- today in the engine's internal spelling (ConflictResolve, not conflict:resolve), so normalize before you compare.

The safe defaults

tovio agent new is designed so the easy thing is the safe thing. Out of the box a token gets:

Default Value Why
path_scope ["**"] Broad access to ordinary code.
denied_path_scope every policy-protected path Everything except secrets. Protected paths are excluded at the scope stage — refused before policy is read, with no leak.
secret_clearance false The agent cannot obtain keys for clearance-gated objects. Cleared agents are the rare, explicit case.
allowed_ops read, commit, amend, branch:create, relay:fetch, relay:push, conflict:resolve Everyday coding operations.
denied_ops obliterate, policy:modify, tag:create, tag:force, sub-token:issue No self-escalation, no history destruction, no delegation.
branch_scope ["agent/<name>/**"] Keeps agent work on namespaced lanes.
expires_at now + --expires-in hours (default 24) A token is never indefinite.

The summary, in one line: an agent made with tovio agent new can touch ordinary code immediately, cannot read or write any policy-gated path, and cannot escalate. Promotion to broader authority is a separate, explicit, audited action.

Every token expires by design

There is no "forever" token: --expires-in defaults to 24 hours when you omit it. Pick a window that matches the task — a few hours for an interactive session, a workday for a long migration. The deadline is the cryptographic backstop that makes a leaked token self-limiting.

What the scope fields mean

A token bounds an agent on independent dimensions. Each is checked in a fixed order on every operation (see enforcement).

path_scope and denied_path_scope

path_scope is the set of globs the token may read and write within. denied_path_scope is a set of globs excluded from it — and exclusion always wins. A path matching a denied glob is out of scope even if it also matches an allowed one. This is how a token expresses "everything except these paths", which a positive list alone cannot, and it is how the default carves secrets out of **.

Globs use the standard TOVIO path matching: * within a segment, ** across whole segments.

Narrowing happens through delegation today

tovio agent new always issues the §10 default scope (** minus every policy-protected path); it has no --scope or --deny-scope flag yet. To hand an agent a narrower slice — say docs/** only — issue the root token with --can-delegate and mint the narrower leaf with the MCP issue_sub_token tool (or the Node SDK's issueSubToken), as described under delegation.

A read or write outside scope is rejected before any policy is consulted, so the agent is told only that the path is out of scope — never what policy guards it. That ordering is a security property, not an accident: the early stage is not allowed to learn what the later stage gates.

secret_clearance

Read access in TOVIO is enforced by cryptography, not a server check. A protected file is a policy object — ciphertext — and you read it only by obtaining its content key. secret_clearance is the switch that governs whether a token can ever obtain a key for an object whose policy references clearance:

  • secret_clearance: false (the default) — the agent cannot obtain a clearance-gated key through the TOVIO read surface. Even if the path were in scope and the policy otherwise satisfied, the read fails. This is not an operating-system sandbox for unrelated tools or plaintext supplied outside TOVIO.
  • secret_clearance: true — the rare, explicit case: the token may obtain clearance-gated keys, if the agent identity's attributes also satisfy the policy.

Allowed and denied operations

allowed_ops is an allow-list: an operation not listed is denied by default. denied_ops is an explicit deny-list, and deny overrides allow. The operation set is closed; the ones agents get by default cover everyday coding. Several are withheld by default and never offered to agents over MCP:

Operation Default for agents
read, commit, amend, conflict:resolve, relay:fetch Allowed
branch:create, relay:push Allowed (each gated by a capability flag)
obliterate Denied — permanent history removal is a human, fully-audited act
policy:modify Denied — changing a policy is privilege-defining; agents must never self-escalate
tag:create, tag:force Denied — creating or repointing a tag is withheld; tag:force requires a write proof
sub-token:issue Denied by default — an agent delegates only if the token was issued with --can-delegate

Capability flags gate specific operations further: can_create_branches, can_push_to_relay, and can_issue_sub_tokens must each be true for the corresponding operation to succeed.

Inspect a token

$ tovio agent show cap_7r4qy9m2x8k3v6bd0n1p5h

show reports the token's scope — its agent, model, authorizer, path and lane globs, clearance, and deadline — so you (or an agent debugging a denial) can see what it permits, and verifies the signature against the issuing identity. Add --json for the machine shape: the same fields agent new --json prints --- signature_valid is on both, null from agent new (nothing to compare against yet) and a boolean from show. tovio agent list --json reports every issued token with an active / expired / revoked status, and tovio agent token <id> prints the hex-serialized token material an MCP client can present directly.

Renew a long-running token

A job that outlives its token must renew. Renewal is a deliberate re-issue — never a silent extension — of a fresh token with equal-or-narrower scope from the same human authorizer, linked to the prior token through provenance.

$ tovio agent renew cap_7r4qy9m2x8k3v6bd0n1p5h --expires-in 8 --json

The result is a fresh token with a new token_id and a new issued_at / expires_at window, printed in the same shape as agent new --json; the human output names the lineage (Renewed cap_7r4qy9m2… → cap_2b8h4k0m…), and the signed audit chain records the renewal against the prior token id.

Renew proactively, around 80% of the token's life

An orchestrator should renew before the deadline — a common threshold is 80% of the token's TTL — so the active operation always runs under a valid token. Track the deadline yourself from expires_at (tovio agent show --json, or the effective_scope echoed at connect): the MCP server does not yet attach an approaching-expiry signal to tool responses. A renewal chain is treated as one session, not many (see agent sessions).

An operation under an already-expired token fails closed with TVO-TOKEN-002 — there is no grace period and no implicit extension. A revoked token, or a delegated sub-token, cannot be renewed (TVO-TOKEN-007); renew the root and re-delegate.

Revoke a token

Revocation is immediate and audited. Revoke a token before its deadline when a task is done early, a session looks wrong, or you simply want to pull access:

$ tovio agent revoke cap_7r4qy9m2x8k3v6bd0n1p5h --json

The token is re-printed in the agent new --json shape with "revoked": true, the agent's registration is removed from the access registry, and the revocation is appended to the signed audit chain. Revoking a token that is already revoked is a safe no-op, but it prints no JSON result --- so treat exit code 0, not the presence of an object, as the confirmation when you retry.

A revoked token's next operation fails with TVO-TOKEN-003 and the session closes. Revocation cascades: any chain containing the revoked ancestor is rejected, and a live delegated session observes it on its next call — every call re-verifies the whole persisted chain.

Delegate to sub-agents

An orchestrator agent can hand bounded slices of its own authority to sub-agents by minting sub-tokens — but only if its token has can_issue_sub_tokens: true and sub-token:issue in its allowed ops. Neither is a default; both are granted together by issuing the root with tovio agent new … --can-delegate. The MCP tool is issue_sub_token. expires_at is required (unix seconds, at or before your own expiry); paths, branches, and ops inherit your token's value when left empty; can_delegate does not inherit --- it is false unless you ask for it, so a leaf never gains delegation by accident. The core enforces monotonic narrowing: a sub-token must be provably narrower than its parent on every dimension.

did:tovio:dustin (human)
└── cap_root   path_scope=["src/frontend/**"], can_issue_sub_tokens=true, expires 22:00Z
    ├── cap_dash    path_scope=["src/frontend/Dashboard.tsx"],    expires 20:00Z
    └── cap_panel   path_scope=["src/frontend/MetricsPanel.tsx"], expires 20:00Z

Every dimension must shrink or stay equal: path_scope ⊆ parent, allowed_ops ⊆ parent, clearance only dropped, each flag only dropped, expires_at <= parent. Anything the core cannot prove is contained is conservatively rejected with TVO-TOKEN-005. The chain's root always remains the original human — a sub-token's authorized_by is the delegating agent, but following parent_token_id upward always terminates at a human-authorized token.

This is what makes hierarchical agents safe: a sub-agent can never reach beyond what its orchestrator could reach, which can never reach beyond what the human granted.

A leaf opens its own session

issue_sub_token verifies the whole chain before returning the signed leaf as hex. Hand that hex to the sub-agent as its own TOVIO_TOKEN: at connect the core reconstructs the ancestor chain from the persisted parent links and re-verifies every hop (human-rooted, unexpired, unrevoked, monotonically narrower) before any session exists, and the sub-agent's commits record the complete delegation_chain. Re-delegation needs an explicit can_delegate grant at every hop — rights never propagate silently.

Provenance: every action traces to a human

You never have to trust an agent's word about what it did. On every successful write — commit, amend, conflict resolution, or pushed commit — the MCP server records an AgentProvenance block on the produced object. The agent cannot supply, omit, or override these fields; they are derived from the bound token and the server's own state.

Provenance field What it records
token_id The leaf token the action ran under.
model / model_hash The model powering the agent and its pin — today the BLAKE3 digest of the model identifier; a content hash of the weights or manifest replaces it once a model registry exists.
task_id / task_description The bounded task the token authorized.
prompt_hash A BLAKE3 hash of the initiating prompt, computed by the server.
session_id A server-generated correlation ID shared by commits from one write session.
tool_manifest_hash BLAKE3 over the canonical tovio-tool-manifest-v3 manifest — the exact tool catalog and allowed ops the session negotiated — computed at session start.
mcp_server_version The build of the MCP server.
authorized_by The human at the root of the delegation chain.
delegation_chain The ordered token IDs, root → leaf.

This makes every successful write performed through the MCP surface traceable to the recorded model, task, token chain, and human root authorizer. Provenance attaches to produced objects and operations; it is not a permanent line-by-line authorship claim after later merges or rewrites.

How a call is checked

Whichever surface the agent uses, the core runs the same four checks in this exact order on every operation. The order is the contract — earlier checks must not leak what later checks gate.

  1. Token validity — signed by a human, unexpired, not revoked, chain valid. → TVO-TOKEN-002/003
  2. Path scope — target within path_scope, not in denied_path_scope. Checked before any policy is read. → TVO-TOKEN-001
  3. Operation allow/deny — op in allowed_ops, not in denied_ops, flags satisfied. → TVO-TOKEN-004
  4. Policy / clearance / key availability — for protected paths: policy evaluation and key availability (reads), or a write proof (writes). → TVO-PERM-001 / TVO-PERM-002

A denial returns the structured error contract an agent parses — see the machine contract.

Where this connects

Last reviewed September 9, 2026

Suggest an improvement to this page Not for security reports — see disclosure