Skip to content

Architecture ​

How agent-custody is put together, what runs on the customer's side, what runs on ours, and what changes in the agent. The diagrams are the maps; Deployment says what each setup proves, and What each piece is for says when to turn each one on.

The split in one picture ​

Almost everything runs in the customer's environment. The one thing a customer cannot run for themselves is a log that is not theirs, so that is the only thing hosted. The hosted log holds hashes, not receipts.

runs on the customer's sideruns on ours
the gateway, in front of the toolsthe log server for each tenant, at /t/<tenant>/
the gateway key, the principal key, the grant, the Cedar policy, the revocation listthe signer that holds the log's key, apart from the process facing the internet
the tool credentials (Stripe key and the like), held by the gateway, not the agentsigned checkpoints on a second host, and the outside monitor
the full receipts, with arguments and results, under the customer's capture rulesthe tenant portal: registration, tokens, usage against the plan, billing, export
the optional memory server and its ledgerthe witness software, which must run on a machine the log's operator does not control; no independent operator runs one for our log yet

A customer who does not want a hosted log runs the same server from the container, the compose file, or the Kubernetes manifests in deploy/.

The shared gateway service ​

For one agent session the host spawns the gateway on stdio and there is nothing to operate. For every agent on a host or cluster, run one shared gateway as a service (ghcr.io/svayatta/agent-custody-gateway, amd64 and arm64): each agent connects over HTTP and presents its own grant, and the tool credentials live in the gateway.

An admin console runs from the same image as a separate service, with a token of its own: it reads the gateway's config, receipts and revocation file, edits the policy file, and revokes grants. It holds no principal key and signs nothing, and the gateway does not serve any admin page. See the console.

Running the gateway as a service has the commands, the Kubernetes and systemd recipes, and what goes where. Its limits are part of the design:

  • A grant is a bearer credential: whoever holds it can connect as that agent until it expires or is revoked. Short lifetimes, a private network or mutual TLS, and revocation are the controls.
  • The transport is plain HTTP, so TLS comes from a proxy or mesh in front.
  • One replica: sessions are held in memory and the local log has one writer.
  • One policy and one gateway key apply to every agent on it; one gateway per team where that is not acceptable.
  • One process holds every agent's tool credentials, which is the point and also the blast radius.

What the agent has to change ​

agent typechangewhat you get
uses MCP tool serversname the gateway where the host named the tool server, and remove the direct routeenforcement: a denied call never reaches the tool, and the record is observed
calls a REST APIdescribe the endpoints as tools in the gateway config; the credential moves to the gatewaythe same, for REST
plain function toolswrap them with the framework adapter, or send them through the sidecara record only: every field is claimed, because the process describes itself
any agenttake tool credentials out of the agent's environmentthe gateway cannot be bypassed

The agent never calls agent-custody. If it holds a credential of its own, nothing here sees what it does with it.

The pieces and how they depend on each other ​

The data model ​

Every field of the predicate carries a provenance label: attested (signed by a key other than the issuer's), observed (the gateway obtained it itself), or claimed (the agent said so). The receipts overview has the full table.

One tool call ​

The order is fixed. A denial at any step still produces a signed receipt, and a tool in precommit is authorized in the log before it is forwarded.

The status the receipt records is one of five:

Onboarding a new customer ​

Revoking a grant mid-session ​

Revocation is per grant and takes effect on the next call, with no restart and no key rotation. The Cedar policy is re-read the same way: a changed policy.cedar applies from the next call, a policy that does not parse is refused and the one in force stays, and every change is recorded with both digests.

Verifying a receipt without trusting the operator ​

What this does and does not cover ​

  • Only calls through the gateway. A credential in the agent's environment, a second direct route in the host config, or code that calls an API on its own is outside it. Taking those away is a deployment decision.
  • The SDK and sidecar record, they do not prove. They run in the process they describe, so their receipts are claimed.
  • The hosted log does not see grants or policy. It sees leaf hashes. A receipt names the grant and carries the policy digest, and a verifier checks both against public keys.
  • The operator can rewrite a local log. A remote log, published checkpoints, and a witness are what detect that. The witness ships in the package, but a second, independent operator for our log is still being sought, so today the checkpoints are published and the witness step is not yet independent.
  • There is no hosted control plane yet. Grants, revocation and Cedar policy are managed on the customer's gateway; the portal manages the log, tokens, usage and billing.
  • Failure mode: a precommit tool is withheld while the log is unreachable. That is deliberate and it is a denial of service on the agent.
  • No transparent egress proxy. Arbitrary HTTP from agent code is not intercepted.

See the threat model for who could make a receipt false and what stops them, and the hosted log reference for the log's routes.