chancery

module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 20, 2026 License: Apache-2.0

README

Chancery

The identity provider for AI agents — the neutral, self-hosted system of record for what every agent is, who it acts for, what it can do, and what it has done.

Agents get their identities from Chancery, their credentials through it (never holding real secrets), and every action attributed by it — by construction, not log forensics. In-path enforcement: register, scope, delegate, revoke — instantly, at the identity or instance level. Audit is metadata-only as a structural invariant: prompts and payloads are never stored.

Single Go binary. Apache-2.0. MCP-first, then HTTP, shell, browser. Try the 60-second story: make demo.

What you get today:

  • Identity — agent → immutable version → revocable instance; SPIFFE-compatible URIs; registry-born, owner-attributed (RFC-001)
  • Writs — delegated authority that can only narrow: the block format has no field for widening (RFC-002)
  • In-path enforcementmcp wrap checks every tool call against fresh state; revocation lands on the next call, not the next token expiry (RFC-005/007)
  • Sealed credentials — injected into the tool server's env, never the agent's context; prompt injection can't leak what was never there (RFC-003)
  • Runtime spawn — orchestrators mint governed workers without the admin token, bounded by human-locked templates (RFC-012)
  • Browser sessions — custodied cookies + per-URL navigation scoping (RFC-013)
  • Callee trust — server pinning with drift refusal, frozen tree-pinned installs, and OS-level confinement (RFC-016/018)
  • Per-call semantics — task-bound grants, a pluggable intent checker, capability leases, admitted-vs-committed audit (RFC-015/017)
  • Tamper-evident audit — hash-chained, metadata-only by schema; plus a read-only dashboard at /ui (RFC-006/014)

MCP-first, not MCP-only. The registry, writs/delegation, policy, sealed credentials, and audit govern any agent in any language today (LangGraph, CrewAI, a cron job, a shell script) via the decision API — see Governing any agent. What's MCP- specific today is the unbypassable, in-path enforcement (mcp wrap); for other runtimes you use advisory checks now and switch to in-path enforcement as those PEPs land (HTTP → shell → browser), with the same writs.

Two promises (RFC-011): what ships open source stays Apache-2.0 — no license flip, ever; and security is never paywalled — every gap in SECURITY.md closes in the open core. The boundary test: whatever makes a single trust domain secure and operable is open source; value that exists only at organizational scale (SSO/SCIM, multi-tenancy, SIEM exporters, compliance packs, HA orchestration) is enterprise.

Status

Beta. All 19 design RFCs are locked and implemented; 105 tests across 11 packages gate every commit; releases are cosign-signed and ship an SBOM. The security model is settled, and every known gap is published in SECURITY.md with an owner and a phase — all of them close in the open-source core.

What beta still means: the CLI and REST surfaces may take breaking changes before 1.0, always called out in release notes. Run it, build on it, and tell me what breaks.

Install

brew install chanceryhq/tap/chancery          # macOS / Linux
# or: docker run --rm -v chancery:/data ghcr.io/chanceryhq/chancery --help
# or: download a signed binary from the Releases page
# or from source:
go build -o chancery ./cmd/chancery

Release binaries and checksums are cosign-signed (keyless, via GitHub OIDC) and ship with an SBOM; verification instructions are in each release's notes.

Try it

./chancery init --trust-domain acme.com
./chancery agent register deploy-bot --owner user:you@acme.com \
    --purpose "deploys services" --prompt ./prompt.md --model claude-fable-5
./chancery writ grant --for user:you@acme.com --to deploy-bot --cap "call:github/*"
./chancery writ delegate <writ-id> --to test-runner --caveat "call:github/get_*"
./chancery writ check <writ-id> --resource github/get_pull_request   # ALLOW + lineage
./chancery writ revoke <writ-id>
./chancery writ check <writ-id> --resource github/get_pull_request   # DENY: revoked
./chancery audit                                                     # the timeline

Every action is attributed to a specific agent, version, and delegation chain — and a delegated writ can only ever narrow: the block format has no field for widening.

Spawn agents at runtime — governed, no admin token

Orchestrators that create agents at runtime don't need the admin token: spawning is itself writ-governed (RFC-012). A human locks a template (capability ceiling + max lifetime) once; the orchestrator's writ carries admin:spawn/<template>; every spawned worker is registered, delegated a narrowed block, owner-attributed, and expires on its own:

./chancery template create researcher --purpose "reads github" \
    --max-cap "call:github/get_*" --max-ttl 30m
./chancery writ grant --for user:you@acme.com --to orchestrator \
    --cap "call:github/*" --cap "admin:spawn/researcher"
./chancery agent spawn worker-1 --writ <writ-id> --agent orchestrator \
    --template researcher --ttl 10m      # or POST /v1/spawn — no admin token
Enforce it live on any stdio MCP server

Per-call policy, sealed secrets injected server-side only, revocation on the next call:

./chancery secret put github-token --from-file ./token
./chancery mcp wrap --agent deploy-bot --writ <writ-id> \
    --secret GITHUB_TOKEN=github-token -- npx @yourorg/some-mcp-server
Browser agents: custodied sessions, scoped navigation

The human's session is sealed and custodied — the agent never holds a cookie — and granting net:… capabilities scopes every navigation per-URL, in-path, fail-closed (RFC-013):

./chancery secret put github-session --from-file storage-state.json
./chancery writ grant --for user:you@acme.com --to web-bot \
    --cap "call:browser/*" --cap "net:github.com/*"
./chancery mcp wrap --agent web-bot --writ <writ-id> \
    --secret-file STATE=github-session \
    -- npx @playwright/mcp@latest --isolated --storage-state=chancery-file:STATE
# github.com/* navigations pass; mail.google.com is a -32001 denial;
# instance revoke is the session kill switch. See examples/browser-agent.
Trust the server, not just the agent

Permission is about the caller; the gate also verifies the callee. The first wrap pins the server's identity and every later wrap refuses on drift (RFC-016) — three tiers, strongest wins: an image@sha256:… digest in the command, a whole directory tree via --pin-tree, or the binary's hash by default. Better: skip npx entirely (RFC-018) —

./chancery mcp install @yourorg/some-mcp-server@1.4.2 \
    --egress api.github.com --writable /tmp/agent-scratch
# frozen install, lifecycle scripts disabled, whole tree Merkle-pinned;
# mutable specs (latest, ^, ~) refused — a mutable reference is not an identity.
# Poison ONE file in it and the next wrap refuses to start.

and turn the pin's manifest into an OS boundary with --confine: outbound network goes loopback-only through an auditing egress allow-list proxy (off-manifest hosts are a 403 + mcp.server_egress_denied — host recorded, never paths), and the filesystem is read-only outside the declared writable paths. Where the sandbox layer is missing, the spawn refuses — never silently unconfined. Upgrades and manifest changes go through chancery mcp repin: explicit, audited. And mcp wrap --dry-run preflights all of it — effective authority, pin status, manifest — spawning nothing, pinning nothing.

Beyond capabilities: the task, the moment, the commit

Capabilities say what's allowed; three per-call mechanisms narrow that to what's intended and record what happened:

./chancery writ grant --for user:you@acme.com --to db-bot \
    --cap "call:db/*" --task "read this week's metrics"
./chancery mcp wrap --agent db-bot --writ <writ-id> \
    --intent-check ./checker.sh --lease -- <db-mcp-server>
  • Task-bound grants (RFC-017): --task writes the grant's purpose onto the writ — audited, and handed to intent checkers as the one thing they can't infer.
  • The intent socket (RFC-017): plug any external detector into the per-call decision. It sees {agent, task, tool, args} and votes — veto-only (it can never widen), fail-closed in enforce, log-only in advise, arguments never stored. Chancery ships no semantic judgment; it makes yours enforceable.
  • Capability leases (RFC-015): --lease stamps each admitted call with a 30-second signed lease a cooperating server verifies (POST /v1/leases/verify) right before committing — a revocation landing mid-flight fails at the server instead of landing. Either way the trail records mcp.call_result: "allowed" and "happened" are different facts.
The control plane: API + read-only dashboard

Run the control plane as an HTTP API with ./chancery serve (REST/JSON under /v1; the admin token is printed once at init) — and open http://127.0.0.1:7423/ui for the embedded read-only dashboard (RFC-014): the live audit timeline with a permanent integrity badge, the agent roster with spawn provenance, and the delegation tree rendered as a tree. Writes (grant, revoke, seal) deliberately stay in the CLI/API. The audit timeline is hash-chained — ./chancery audit verify detects any edit, deletion, or reorder. Known MVP gaps are published in RFC-009 §5.

Guides

The SDK is advisory — a client-side convenience. The enforcement boundary is always the out-of-process proxy (chancery mcp wrap), which a prompt-injected agent cannot talk its way around.

Build & test from source

git clone https://github.com/chanceryhq/chancery && cd chancery
make build      # -> ./chancery  (Go 1.26+, no CGO, single static binary)
make test       # go vet + 105 tests across 11 packages, in seconds
make demo       # the 60-second enforcement + audit arc, end to end

See CONTRIBUTING.md for the repo layout, how the tests map to each RFC, and how to propose changes.

Design RFCs

Design happens as a series of locked decisions, one RFC at a time (template).

RFC Title Status
000 Vision and plan Locked
001 Agent identity model Locked
002 Lineage and delegation Locked
003 Credential broker Locked
004 Policy and authorization Locked
005 Runtime enforcement (MCP → HTTP → shell → browser) Locked
006 Audit and attribution Locked
007 Lifecycle and revocation Locked
008 Data model and APIs Locked
009 Threat model Locked
010 MVP scope (the 90-day build) Locked
011 Open-core boundary Locked
012 Dynamic agent creation (writ-gated runtime spawn) Locked
013 Browser sessions and tokens as governed credentials Locked
014 Read-only dashboard (/ui) Locked
015 Call lifecycle and capability leases Locked
016 Server pinning (callee identity: binary, tree, digest) Locked
017 Task-bound grants and the intent socket Locked
018 Frozen installs and manifest-bounded confinement Locked

Directories

Path Synopsis
cmd
chancery command
chancery is the CLI for the Chancery control plane: the registry of agent identities (RFC-001) and writs (RFC-002).
chancery is the CLI for the Chancery control plane: the registry of agent identities (RFC-001) and writs (RFC-002).
examples
go-agent command
A minimal agent using the Chancery Go SDK: it starts a runtime instance, then advisory-checks each action against its writ before attempting it.
A minimal agent using the Chancery Go SDK: it starts a runtime instance, then advisory-checks each action against its writ before attempting it.
internal
api
Package api is Chancery's HTTP control-plane surface (RFC-008): REST/JSON under /v1, Vault-style.
Package api is Chancery's HTTP control-plane surface (RFC-008): REST/JSON under /v1, Vault-style.
confine
Package confine implements manifest-bounded runtime confinement for wrapped MCP servers (RFC-018): the pin's manifest declares the hosts a server process may reach (egress) and the paths it may write (writable); the spawn applies it as an OS boundary.
Package confine implements manifest-bounded runtime confinement for wrapped MCP servers (RFC-018): the pin's manifest declares the hosts a server process may reach (egress) and the paths it may write (writable); the spawn applies it as an OS boundary.
identity
Package identity issues and verifies Chancery identity documents (RFC-001 §4): short-lived ES256 JWTs naming the acting principal — agent (durable SPIFFE-compatible URI), version (content digest), and instance — wire-compatible with WIMSE WIT conventions.
Package identity issues and verifies Chancery identity documents (RFC-001 §4): short-lived ES256 JWTs naming the acting principal — agent (durable SPIFFE-compatible URI), version (content digest), and instance — wire-compatible with WIMSE WIT conventions.
mcp
Intent socket (RFC-017): an optional sixth decision layer consulted AFTER the deterministic layers pass.
Intent socket (RFC-017): an optional sixth decision layer consulted AFTER the deterministic layers pass.
policy
Package policy owns the capability grammar and the layered PDP (RFC-004).
Package policy owns the capability grammar and the layered PDP (RFC-004).
seal
Package seal is the broker's sealed credential store (RFC-003): AES-256-GCM per entry, key material in a 0600 file, values in plaintext only in memory during injection.
Package seal is the broker's sealed credential store (RFC-003): AES-256-GCM per entry, key material in a 0600 file, values in plaintext only in memory during injection.
service
Package service is the single implementation of Chancery's operations (RFC-008): the HTTP API and the CLI are thin clients over it.
Package service is the single implementation of Chancery's operations (RFC-008): the HTTP API and the CLI are thin clients over it.
store
Package store is Chancery's registry: the durable record of agents, versions, instances, and writs (RFC-001, RFC-002).
Package store is Chancery's registry: the durable record of agents, versions, instances, and writs (RFC-001, RFC-002).
writ
Package writ implements RFC-002: the writ, a chain of signed blocks in which block 0 grants capabilities and every later block may only add caveats.
Package writ implements RFC-002: the writ, a chain of signed blocks in which block 0 grants capabilities and every later block may only add caveats.
Package sdk is the Go ergonomics layer over a Chancery control plane (RFC-010 item 7).
Package sdk is the Go ergonomics layer over a Chancery control plane (RFC-010 item 7).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL