mandatum

module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 18, 2026 License: Apache-2.0

README

Mandatum

ci security licenses OpenSSF Scorecard Go Reference License

English | 日本語

Verifiable delegation for AI agents. An agent's authority to act becomes a signed chain rooted in a named human — attenuating at every hop, revocable at any link, and evaluated across whole action sequences.

Status: early. The format, the verifier, the signing layer and the issuer work end to end and are tested against real signatures. The AuthZEN binding and sequence evaluation work too, as do key resolution from an issuer's JWKS or SPIFFE bundle, local revocation from a published compact set, and an MCP tool-call enforcement middleware that runs the four checks in the order the specification requires. The audit log is not built, the sequence store is in-process only, nothing publishes a revocation set on a schedule, and nothing here has had a third-party security review. See ROADMAP.md and the threat model for what that means in practice.

The specification is the thing to read and argue with: docs/spec/delegation-assertion.md.


The problem

An AI agent acting for a person almost always does so by inheriting a credential — a service account, an API key, a shared token, the human's own session. Three failures follow from that.

You cannot say who is responsible. 28% of organizations can reliably trace agent actions to a human or system across all environments (1), and 68% cannot clearly distinguish AI agent activity from human activity (2).

You cannot revoke one agent. Agents share the credential they inherited, so cutting off a misbehaving agent cuts off everything else using it. Nobody pulls that lever, so the agent keeps its access.

You cannot constrain a sequence. Authorization is decided one call at a time. An agent reads untrusted external content, then writes to an internal system. Both calls are legitimately authorized. The pair is an exfiltration, and no per-call check can see it.

The Model Context Protocol's authorization specification covers the transport — how a client gets a token for a server. It says nothing about which tool that token may call, which agent holds it, or who delegated to whom, and MCP's authorization interest group has open work on per-tool scopes and on consent across chains of agents. The Enterprise-Managed Authorization extension, stable since June 2026, gets an access token from an enterprise identity assertion, so what it produces is scoped to a server, not to a call.

That gap is what Mandatum fills. Nothing else.

How it works

sequenceDiagram
    autonumber
    actor Human as 👤 Alice
    participant IdP as Identity Provider
    participant A as 🤖 planner
    participant B as 🤖 retriever
    participant PEP as MCP server · PEP
    participant PDP as AuthZEN PDP

    rect rgba(130,170,255,0.12)
    Note over Human,A: delegation · authority only ever narrows
    Human->>IdP: authenticate (password + hardware key)
    IdP-->>A: MDA₀ · search.* · 1h · 2 hops left
    A-->>B: MDA₁ · search.query · 40m · 1 hop left
    Note right of B: cannot widen: the verifier<br/>rejects a link that tries
    end

    rect rgba(120,220,170,0.12)
    Note over B,PDP: enforcement · before the tool runs, every call
    B->>PEP: tools/call search.query + chain [MDA₀, MDA₁]
    PEP->>PEP: verify chain, offline (V1–V9)
    PEP->>PEP: check the grant covers this call
    PEP->>PDP: subject = Alice · agent = retriever
    PDP-->>PEP: allow
    PEP->>PEP: admit against the chain's history
    PEP-->>B: result
    end

    rect rgba(255,150,150,0.12)
    Note over B,PEP: the pair no per-call check can see
    B->>PEP: tools/call fetch(external URL)
    PEP-->>B: allowed
    B->>PEP: tools/call write(internal record)
    PEP--xB: denied · no-write-after-external-read
    end

Step 3 is the property that makes the rest work: every link commits to its parent by hash and may only narrow, so authority cannot grow on the way down. Revoke MDA₁ and agent B loses everything, including whatever it delegated onward. Agent A and every sibling chain are untouched.

Step 6 is the one an enforcement point is most likely to skip, since the PDP is about to answer anyway: it compares the call against what the sponsor actually granted, so a PDP that says yes to everything still cannot authorize a tool nobody delegated. Step 9 is last for a reason — admitting an action is also recording it, and a call the PDP refused should not spend the chain's invocation budget.

Step 13 is the one nothing else does. Both calls are individually authorized. The pair is the exfiltration, and a check that sees one call at a time cannot tell.

Three places this fits

A coding agent that can open pull requests. It reads a dependency's README, an issue comment, a diff from a fork: all attacker-writable. Then it pushes. Tag the reads external-content and the push mutating, and the second one stops after the first.

{ "id": "no-push-after-third-party-read",
  "forbid": { "resource.tags": ["mutating"] },
  "after":  { "resource.tags": ["external-content"] } }

A support agent that can issue credits. The customer's message is attacker-controlled text and the refund tool moves money. Same shape, and the sponsor is the engineer who approved the session, so the audit record names a person rather than svc-support-bot.

A research agent someone left running. max_invocations on the sponsor's grant caps the whole chain. Because state is keyed by the chain root, spawning ten sub-agents spends the same budget rather than ten of them.

None of these need a new policy engine. They need the tool call to know what happened earlier under the same authority.

What this is not

The failure mode for a project in this space is drifting into categories that are already occupied. Mandatum stays out of them by design:

  • Not an authorization engine. Mandatum never decides. It establishes facts and hands them to a Policy Decision Point that already exists — OPA, Cedar, OpenFGA, SpiceDB, Cerbos, or anything conformant with the OpenID AuthZEN Authorization API. Engines are not the gap.
  • Not a new protocol. JOSE for the assertions, RFC 8693 for issuance, SPIFFE for workload identity, AuthZEN for decisions, RFC 6962 for the log. Where a standard fits, Mandatum uses it unchanged.
  • Not a gateway, registry, sandbox, or agent runtime. Those categories are crowded. Mandatum is a library meant to run inside agentgateway, ToolHive, or an MCP server. pkg/mcp is the net/http middleware that puts it there: it enforces tools/call and forwards everything else to the server it fronts.
  • Not a blockchain. The audit log is a Merkle tree. No consensus, no network, no token.

Prior art, honestly

Attenuated capabilities that can be delegated without contacting the issuer are the contribution of macaroons and, in modern form, Biscuit. SPIFFE established workload identity. RFC 8693 established delegation semantics for token exchange, and drew a line this project sits outside of: its §4.1 tells a resource server to authorize the current actor and treat prior actors as informational. Mandatum authorizes on the history instead, with its own verifiable credential rather than by reinterpreting act. That is an extension of the RFC 8693 model, not a reading of it, and docs/alternatives.md says so at length.

What is new here is narrow: a chain rooted in an authenticated human that survives arbitrary sub-delegation, constraints evaluated over a sequence of actions rather than one call, and a binding to AuthZEN so the chain is input to any conformant engine instead of one vendor's.

If something already does those three things, that is worth knowing before more gets built. Please open an issue — see docs/alternatives.md.

Documentation

Document What it covers
Delegation Assertion specification Format, attenuation rules, verification, AuthZEN binding, sequence evaluation, threat model
docs/alternatives.md What else exists and why it does not close this gap
ROADMAP.md Both tracks, with gates
GOVERNANCE.md Roles, voting, organizational balance
VERSIONING.md Semantic versioning, wire-format versioning, deprecation
CHANGELOG.md What changed, and the known limitations of each release
CONTRIBUTING.md How to get a change merged
SECURITY.md Reporting a vulnerability
Threat model What is defended, from whom, what is assumed, and what is not yet covered
Supply chain How to verify a release, what CI enforces, and the known gaps

Trying it

The whole flow — a human sponsors an agent, that agent sub-delegates something narrower, a resource server verifies what arrives, and an attempt to widen is refused — is a runnable example:

go test -run Example ./pkg/issue/ -v

The source is pkg/issue/example_test.go, and it is the shortest honest description of what the library does.

The enforcement side is a second one. A Policy Decision Point that permits everything asks to wipe the database, and the call is refused without the PDP being consulted at all:

go test -run Example ./pkg/mcp/ -v

Contributing

The most useful contribution right now is not code. It is disagreement with the design from someone who has run agent systems in production, or a concrete scenario that the specification cannot express. The specification has an open-questions section that is genuinely open.

See CONTRIBUTING.md.

License

Apache License 2.0.


References

  1. Cloud Security Alliance and Strata Identity, Securing Autonomous AI Agents, February 2026 (n=285).
  2. Cloud Security Alliance and Aembit, Identity and Access Gaps in the Age of Autonomous AI, March 2026 (n=228).

Directories

Path Synopsis
pkg
authzen
Package authzen speaks the OpenID AuthZEN Authorization API 1.0.
Package authzen speaks the OpenID AuthZEN Authorization API 1.0.
issue
Package issue builds and signs delegation chains.
Package issue builds and signs delegation chains.
jose
Package jose implements the minimum of JWS needed to sign and verify Mandatum Delegation Assertions.
Package jose implements the minimum of JWS needed to sign and verify Mandatum Delegation Assertions.
mcp
Package mcp enforces a Mandatum delegation chain on an MCP tool call.
Package mcp enforces a Mandatum delegation chain on an MCP tool call.
mda
Package mda defines the Mandatum Delegation Assertion wire format.
Package mda defines the Mandatum Delegation Assertion wire format.
revoke
Package revoke implements the compact revocation set of specification section 7.1, so that a Policy Enforcement Point can evaluate rule V8 locally instead of asking a service on the path of every authorized call.
Package revoke implements the compact revocation set of specification section 7.1, so that a Policy Enforcement Point can evaluate rule V8 locally instead of asking a service on the path of every authorized call.
sequence
Package sequence evaluates constraints over a chain's action history.
Package sequence evaluates constraints over a chain's action history.
verify
Package verify implements chain verification for Mandatum Delegation Assertions, rules V1 through V9 of docs/spec/delegation-assertion.md.
Package verify implements chain verification for Mandatum Delegation Assertions, rules V1 through V9 of docs/spec/delegation-assertion.md.

Jump to

Keyboard shortcuts

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