How it works

What happens between asking and acting.

An agent calls /authorize before it does something consequential. What comes back is not a yes or a no — it is a receipt naming the exact operation, the information flow it was decided under, and a signature anyone can check with a published key.

The path a request takes

Ordered deliberately. Everything cheap and deterministic runs before anything expensive or arguable, and the two evidence steps come last so they seal what actually happened rather than what was expected.

  1. 01

    Identity

    hard check

    Ed25519 JWT. A tampered token is a 401 before any decision is reached.

  2. 02

    Normalization

    hard check

    The resource is URL-decoded twice, stripped of null bytes and POSIX-normalized. Traversal is a 400, not a low score.

  3. 03

    Policy

    hard check

    Operator rules run before scoring. A policy denial is final.

  4. 04

    Trust score

    heuristic

    Identity, delegation chain, purpose alignment, behaviour — weighted, compared against a threshold set by the sensitivity of what is being touched.

  5. 05

    Sequence

    heuristic

    Kill chain, lateral movement, resource hammering, purpose drift, time-of-day anomaly. Patterns no single request reveals.

  6. 06

    Information flow

    lattice

    Outside the score entirely. A lattice violation denies regardless of how well everything else scored.

  7. 07

    Seal

    evidence

    action_ref over the whole operation, the flow state, a nonce, an HMAC and an Ed25519 signature.

  8. 08

    Record

    evidence

    Written synchronously, before the response leaves. The caller acts on a PERMIT, so an entry still queued would mean an action running with no record of what authorized it.

A fact and a judgement are not the same answer.

The trust score is a weighted average of four heuristics. It is useful, it catches real things, and nobody outside can verify that any particular number is correct — there is no oracle for it.

A flow violation is different in kind. What leaves must not outrank where it goes; once a session carries content the agent did not author, the agent no longer chooses the destination. Those are lattice properties, and they are checked outside the score on purpose. Folding them in would let a high score buy its way past a flow that must not happen.

hard checks
Scope, action, delegation chain, traversal, contract limits. Binary, and final.
lattice
Confidentiality and integrity. A violation denies whatever the score says.
heuristics
Trust score and sequence detectors. Arguable, occasionally wrong, labelled as such in the code.
evidence
The receipt. Checkable by someone who was not there and cannot forge one.

What a receipt binds

A decision naming only a request id proves who decided and when. It does not prove what was decided, so nothing stops that verdict being spent on a different operation — the failure Loopjacking reproduced in September 2026 against Agno AgentOS and LangGraph Agent Server.

action_ref is a SHA-256 over the agent, the action, the resource, every material argument and the policy version. Nonce, timestamp and decision are deliberately outside it: those belong to one authorization event and differ between the request and the dispatch that follows, which would make the reference unrecomputable — the opposite of what it is for. They are bound separately, by the signature.

the descriptor
action_ref = SHA-256(canonical_json({
  v:              1,
  agent_id:       "pay_bot",
  action:         "transfer",
  resource:       "/payments/outbound",
  arguments:      { amount_minor, currency, recipient },
  policy_version: "",
}))

Two signatures, and only one of them is for you

An HMAC authenticates a receipt to whoever holds the shared secret — which would let an auditor mint receipts. A symmetric scheme can only offer trust us, or become us. So receipts carry both.

algorithmwho can verifywho can issue
response_sigHMAC-SHA256holders of the shared secretholders of the shared secret
receipt_sigEd25519anyonethe instance only

The public key is served at GET /receipts/public-key, without authentication. A verifier is usually not a customer — an auditor, a regulator, an insurer's assessor — and requiring a credential to check a signature defeats the point of having one.

What this does not do

  • Session-grained flow, not value-grained.CaMeL and FIDES track labels inside the agent — one owns an interpreter, the other is framework middleware. AgentGate is reached over HTTP and never sees the agent's variables, so it bounds what a session could have seen rather than what it used. It over-approximates; declared destinations are the escape.
  • Scoped to consequential actions. One process, one SQLite file, and deliberately so: money, deletion, export — the actions a human would otherwise have approved. Gating every read an agent performs buys latency, not safety.
  • 130 ms median to authorize over HTTP, 181 ms end to end with redemption. Dominated by the embedding computed for purpose alignment.
  • Canonicalization is a subset of JCS, not JCS. Integers and strings agree; some floating-point values may not. Keep money in minor units.
  • Only what passes through it. Data an agent obtains by a path AgentGate does not mediate is invisible to it, as it is to any reference monitor.