A deterministic agent registry — verify that an AI agent is real, was delegated by a verified human, and that the delegation is still valid right now.
Trusty answers one question with a yes/no verdict:
Is this agent who it claims to be, acting under authority a real person granted, and is that authority live?
It is a trust registry in the ToIP TRQP sense ("DNS for trust") for the agent era: agents register a key, a verified human binds an assurance level to them, humans issue scoped delegation credentials, and anyone can verify a delegation — with no account, over a single stateless call.
Agents are multiplying, and "who authorized this action?" is becoming unanswerable. Trusty makes the answer verifiable and auditable rather than asserted:
- Real — every principal proves key ownership at registration; re-registering the same key is rejected.
- Authorized — a delegation credential carries the human's binding, the granted scope, and an issuance ceiling; it is signed, not claimed.
- Live — verification checks revocation and status in real time, so a pulled delegation stops verifying.
The verify path is open and auditable on purpose — a trust product whose checking logic is a black box isn't trustworthy. The engine is source-available; the answer is reproducible.
The core use case. Instead of an agent acting with ambient, unaccountable access:
- A verified human issues a delegation credential — scoped (
cart.create), time-boxed, optionally capped, bound to their assurance level. - Your app or agent runtime verifies before every consequential action — one stateless call (HTTP, or the MCP tool) — and gates on the result:
v = verify(delegation_id, required_scope="cart.create") # POST /v0/delegations/verify
if v["verdict"] != "valid" or v["human_binding"]["assurance_level"] < "IAL2":
refuse() # not authorized, revoked, or not enough identity proofing
proceed() # authorized · attributable to a real person · live- Revoke, and the very next verify returns
invalid— an instant kill switch.
This works standalone: any model or agent environment (Claude/MCP, an LLM tool-loop, your backend) can gate its own actions on Trusty's verdict. No account, no SDK, no other service.
Trusty answers identity + delegation validity and stops there (enforcement.decided: false). It reports "this delegation is valid and its ceiling is X" — it does not track how much has been spent, nor move money. The separation is intentional: a neutral verifier composes with any enforcer.
- Action / access autonomy — "the agent may do things within its scope": gate on
verifyalone (above). ✅ standalone, today. - Economic autonomy — "the agent may spend up to a ceiling, then settle and distribute": needs a stateful mandate authority that atomically tracks consumption and moves value. That is the Interust core — its mandate + settlement engine, the roadmap beyond this registry, not part of standalone Trusty.
So: Trusty is the verify-first authorization primitive you can adopt today; spend-bounded, settled agent autonomy is powered by the Interust core.
Requires Docker. No external services — the server runs as a single container over SQLite.
cd docker
docker compose up -d # starts the registry server on :8080
./smoke.sh # end-to-end smoke test (register → bind → issue → verify)
curl -s localhost:8080/v0/healthzOr drive it with the CLI (talks to the same contract):
trusty key generate --out agent.json # create a keypair
trusty registry register --key agent.json --kind agent # prove key ownership
trusty registry bind --did <human-did> --ci <id-proof> --ial IAL2 # bind a human's assurance level
trusty delegation issue --key owner.json --subject <agent-did> \
--scope cart.create --nbf <ts> --exp <ts> # human grants scoped authority
trusty delegation verify --dc-id <delegation-id> --scope cart.create # yes/no verdict
trusty delegation revoke --key owner.json --dc-id <delegation-id> # kill switchgit clone https://github.com/Interust/trusty.git
cd trusty
cargo build --release
cargo install --path crates/trusty-cli # installs the `trusty` CLI
cargo install --path crates/trusty-registry-mcp # installs the `trusty-registry-mcp` serverA single container over SQLite — no external services.
cd docker
docker compose up -d # builds the image + starts the registry on :8080
curl -s localhost:8080/v0/healthz # → {"status":"ok"}
./smoke.sh # optional: end-to-end register → bind → issue → verify
docker compose downcompose.yml runs in dev mode with an env-injected salt and a ./data SQLite volume. Production requires a KMS-backed salt — set TRUSTY_SALT_SOURCE=kms:vault:<mount>/<path> (a Vault/OpenBao KV v2 secret, fetched once at boot via VAULT_ADDR/VAULT_TOKEN) and a Postgres DATABASE_URL; the server refuses to boot with a dev salt under TRUSTY_ENV=production, by design (fail-fast).
| Env var | Purpose |
|---|---|
TRUSTY_ENV |
dev | production (production hardens salt/DB/secret requirements) |
DATABASE_URL |
sqlite:///data/registry.db (dev) or a Postgres URL (postgres://…) |
TRUSTY_SALT_SOURCE |
env:TRUSTY_SALT_V1 (dev) or kms:vault:<mount>/<path> (production; needs VAULT_ADDR + VAULT_TOKEN) |
TRUSTY_PLANE |
tenancy plane, e.g. shared |
TRUSTY_POP_WEBHOOK_URL |
identity-proofing webhook (bind authority for {ci, assurance_level}); https-only in production |
TRUSTY_BADGE_SEED_FILE |
badge signing seed as a mounted secret file (production; the env form is dev-only) |
See .env.example for the full knob list.
You don't have to run the full stack to use verify — pick by how much you want to operate:
Sidecar (recommended self-host). Run the registry server co-located with your app (same Compose network / Kubernetes pod) and call it over the internal network — low latency, your delegation data stays in your infra, and your app takes on no AGPL obligation (you call an unmodified OSS server over HTTP; you don't link it).
services:
registry:
image: trusty-registry # built from docker/Dockerfile
volumes: ["./data:/data"] # SQLite, or point DATABASE_URL at your Postgres
myapp:
depends_on: [registry]
environment: ["TRUSTY_URL=http://registry:8080"]Your app then calls POST http://registry:8080/v0/delegations/verify and gates on the verdict.
HTTP client (any language). Verify is a single stateless call returning a stable JSON contract — no SDK required. Works against a sidecar or any reachable server.
Hosted (roadmap). A free, rate-limited hosted verify endpoint is planned as a zero-ops on-ramp — call it, run nothing.
Library (Rust, advanced). trusty-registry is a crate you can embed in-process — but that makes your application an AGPL derivative. Unless your app is AGPL-compatible, prefer the HTTP path above.
Verify itself is open and free to run — it's the authorization primitive. Spend-bounded, settled agent autonomy is powered by the Interust core (see Use it for).
Four ideas cover everything Trusty does:
- Principal — an agent or a human, named by a
did:key. Registering proves you hold the private key; the same key can't be registered twice. - Binding — a real human is bound to an assurance level (
IAL0–IAL3; higher = stronger identity proofing). This is what makes a delegation accountable to a person. - Delegation credential (DC) — a signed, scoped grant: "this human lets this agent do these things, until this time, up to this ceiling." A verifiable credential, not a claim.
- Verify — anyone asks "is this DC valid right now?" and gets a deterministic yes/no — statelessly, with no account.
1 — keys + register (the agent, and the human who will authorize it)
trusty key generate --out owner.json
trusty key generate --out agent.json
trusty registry register --key owner.json --kind human
trusty registry register --key agent.json --kind agent2 — bind the human's assurance level
trusty registry bind --did <owner-did> --ci <identity-proof-ref> --ial IAL23 — issue a scoped delegation (optionally with a spend ceiling)
trusty delegation issue \
--key owner.json --subject <agent-did> \
--scope cart.create \
--nbf 2026-07-03T00:00:00Z --exp 2026-10-01T00:00:00Z \
--ceiling-amount 500000 --ceiling-currency KRW --ceiling-period P30D
# → prints the delegation id + the credential document4 — verify (CLI, or one stateless HTTP call)
trusty delegation verify --dc-id <delegation-id> --scope cart.createcurl -s -X POST localhost:8080/v0/delegations/verify \
-H 'content-type: application/json' \
-d '{"dc_id":"<delegation-id>","at":"2026-08-01T00:00:00Z","required_scope":"cart.create"}'A valid answer:
{
"verdict": "valid",
"human_binding": { "present": true, "assurance_level": "IAL2" },
"chain": { "depth": 1, "root_type": "human" },
"enforcement": { "decided": false, "note": "limit/state enforcement = external mandate authority" },
"checks": { "v1": "pass", "v2": "pass", "v3": "pass", "v4": "pass", "v5": "pass", "v6": "absent", "v7": "pass" },
"snapshot": { "evaluated_at": "2026-08-01T00:00:00Z" },
"schema_version": "1.1.0"
}It's a stable, versioned JSON contract (schema_version, semver) that is byte-identical for identical input — cache it, sign over it, or diff it. verdict is "valid" or "invalid"; a failure is a verdict, never an error. Passing a DPoP proof in the request wires check v6 — since 1.1.0 it is subject-bound proof of possession: the proof must be signed by the delegated agent's own key, so a stolen delegation id plus a foreign key verifies invalid. Trusty verifies identity and delegation validity and reports them — it does not enforce spend limits or business state itself (enforcement.decided: false); that is left to an external mandate authority.
5 — revoke (the kill switch)
trusty delegation revoke --key owner.json --dc-id <delegation-id>The very next verify returns "invalid" — revocation is checked live, so a pulled delegation stops verifying immediately.
Most integrations only ever call one endpoint: POST /v0/delegations/verify. Before your agent takes a consequential action, verify its delegation and gate on verdict == "valid" (and, if you need it, on human_binding.assurance_level). No SDK, no account — one stateless call returning a stable JSON contract. For agent toolchains, the same check is a native tool via the MCP server (delegation_verify). See Deploy and embed options for sidecar / HTTP / hosted ways to run it.
The trusty CLI drives the same contract as the HTTP API.
| Command | What it does |
|---|---|
trusty key generate --out <file> |
Create a did:key keypair |
trusty registry register --key <file> --kind <human|agent> |
Register a principal (proves key ownership) |
trusty registry bind --did <did> --ci <proof> --ial <IAL0..3> |
Bind a human's assurance level |
trusty delegation issue --key <file> --subject <did> --scope <s> --nbf <ts> --exp <ts> |
Issue a scoped delegation (repeat --scope; add --ceiling-amount/-currency/-period P30D for a spend cap, --parent <dc-id> to sub-delegate) |
trusty delegation verify --dc-id <id> [--scope <s>] [--at <ts>] [--remote <url>] |
Verify — yes/no verdict (or --dc-file <f> for a local credential) |
trusty delegation revoke --key <file> --dc-id <id> |
Revoke (kill switch) |
trusty admin rotate-key --did <did> --new-key <file> · trusty admin salt-status |
Ops |
--remote <url> verifies against a running server; without it the CLI uses a local store.
Public routes are unauthenticated + rate-limited — that is the single-player, cold-start surface.
| Method & path | Auth | Purpose |
|---|---|---|
POST /v0/delegations/verify |
none (rate-limited) | Verify a delegation — deterministic verdict |
GET /v0/delegations/{id}/document |
none (rate-limited) | The signed credential as issued — re-verify it yourself |
GET /v0/badges/{did} |
none (rate-limited) | Signed verified-agent badge snapshot (24h expiry) |
GET /v0/status-lists/{id} |
none | Revocation status list |
GET /v0/metrics |
none | Aggregate KPI counters (counts only) |
GET /v0/healthz |
none | Liveness |
POST /v0/principals |
submit | Register an agent principal |
POST /v0/principals/{did}/bindings |
submit | Bind a verified human's assurance level |
POST /v0/delegations/prepare · POST /v0/delegations |
submit | Prepare / issue a delegation credential |
POST /v0/delegations/{id}/revoke |
submit | Revoke (kill switch) |
GET /v0/delegations/{id}/mandate |
submit | Mandate view |
The verify response is a stable, versioned JSON contract (semver) — integrators build against it independently of the server. It is currently 1.2.0; checks gained v8 (re-delegation consent) additively, so a consumer reading the keys it knows is unaffected.
GET /v0/delegations/{id}/document returns the credential's canonical signed bytes
(application/octet-stream), never re-serialized. Everything else the server reports —
the verify response, the mandate view — is derived from stored columns, so an integrator that
must not take the server's word for a credential's constraints verifies these bytes locally
with trusty-vc (Apache-2.0) and reads the constraints from the document
itself.
GET /v0/badges/{did} returns a signed snapshot of an agent's current standing, valid for 24 hours — a "Verified by Trusty" mark that surfaces (real + authorized + live) without exposing the underlying identity.
Use Trusty from Claude & ChatGPT. trusty-registry-mcp exposes the registry to agent toolchains over stdio (MCP) as five tools whose schemas mirror the HTTP contract exactly:
| Tool | Does |
|---|---|
registry_register |
register a principal |
registry_bind |
bind a human's assurance level |
delegation_issue |
issue a scoped delegation |
delegation_verify |
verify — yes/no verdict |
delegation_revoke |
revoke (kill switch) |
Add the server to claude_desktop_config.json (Settings → Developer → Edit config), then restart Claude:
{
"mcpServers": {
"trusty": {
"command": "trusty-registry-mcp",
"env": {
"TRUSTY_ENV": "dev",
"DATABASE_URL": "sqlite:///absolute/path/to/registry.db",
"TRUSTY_SALT_SOURCE": "env:TRUSTY_SALT_V1",
"TRUSTY_SALT_V1": "change-me-dev-only-salt-32bytes"
}
}
}
}The five tools appear in Claude — now you can ask "verify delegation <id>" and Claude calls Trusty directly. (command assumes trusty-registry-mcp is on PATH via cargo install; otherwise use its absolute path.)
ChatGPT calls an HTTP API, not a local stdio server. Run the registry server (Docker, above) behind a reachable URL, then add a custom GPT Action whose OpenAPI schema points at POST /v0/delegations/verify — an unauthenticated, rate-limited, read-only check the GPT runs before acting.
Public directory / store listing (a one-click Claude connector or a GPT-store app) needs a hosted, publicly reachable endpoint — that is the managed tier on the roadmap. Local integration (Claude Desktop, or a self-hosted URL for ChatGPT) works today.
| Crate | Role |
|---|---|
trusty-registry |
Core registry — principals, bindings, delegation credentials, revocation, stateless verify |
trusty-registry-server |
HTTP API (Axum) |
trusty-registry-mcp |
MCP server (5 tools, HTTP-contract-identical) |
trusty-cli |
Command-line client |
trusty-vc |
Verifiable-credential primitives |
trusty-common |
Shared primitives — DPoP verify/build, token-bucket, JTI cache |
trusty-gateway |
Sidecar gateway — Keycloak reverse proxy, DPoP/cross-plane enforcement, rate limit, audit |
trusty-crypto |
Envelope 3-Tier encryption (MK→DEK→FEK), crypto-shredding |
trusty-consent |
Consent policy/store engine (deny-by-default, RFC 7807) |
trusty-vault |
PII vault + transactional outbox (tokenized-only) |
Deployment lives in docker/ (Dockerfile · compose · smoke test).
Agent identity uses DID (did:key by default); delegations are verifiable credentials; the registry aligns with ToIP TRQP trust-registry semantics. Proof-of-possession and DPoP-style binding guard against token replay.
v0.1 — early. The contract surface (verify JSON, HTTP routes, MCP tools) is stabilizing under semver. APIs may still change ahead of a tagged release.
SECURITY.md states what the project guarantees, which apparent gaps are deliberate design decisions, and the scope limits to know before deploying the sidecar gateway. Read the known limitations before putting the gateway in front of personal data.
Report a suspected vulnerability privately through Security → Report a vulnerability on this repository, not as a public issue.
- Client, SDK, CLI, MCP, and shared primitives — Apache-2.0 (friction-free integration):
trusty-vc,trusty-common,trusty-cli,trusty-registry-mcp. - Registry, server, sidecar gateway, and server-side libraries — AGPL-3.0-or-later:
trusty-registry,trusty-registry-server,trusty-gateway,trusty-crypto,trusty-consent,trusty-vault. A commercial license is available for deployments that cannot meet AGPL terms.
Full license texts: LICENSE-APACHE and LICENSE-AGPL. Each crate declares its own SPDX license in Cargo.toml.
Issues and pull requests are welcome — see CONTRIBUTING.md for the checks to run, what makes a change easy to accept, and the contribution terms.
Two requirements before a pull request can be merged: every commit is signed off (git commit -s, certifying the DCO), and you accept the license grant that keeps commercial licensing possible alongside the AGPL. Reporting a bug or discussing a design requires neither.
