Technology · Protocol

JSON Web Token (JWT)

Scoped JWTs for agent seats, employee-scoped tokens for Microsoft identities, bearer introspection that fails closed. Identity in my systems is derived from the token, never from tool input.

Auth and access plane · Agent Swarm · FAFO CoreWhere identity, scope and lifetime are decided
Issuersigns RS256
Published keysthe anchor
GatewayRust, offline
Edge proxyWebCrypto
  • 1 houreach issuer's key set is cached at the gateway
  • 3runtimes trusting the same kind of anchor

Tokens checked without calling the issuer

In the Auth and access plane I had assistants from several vendors calling one private tool estate, and I wanted every call checked without a round trip to the issuer. I made JWTs the token format and published keys the trust anchor. My Python OAuth server issues RS256-signed access tokens carrying issuer, audience, expiry and coarse MCP scope claims, publishes its key set and persists its signing key, so assistants holding long-lived refresh tokens keep working through a restart.

The Rust tool gateway validates against published key sets from more than one issuer, which lets it accept tokens from my server and from Microsoft's platform. For Microsoft Copilot Studio, an edge proxy checks tenant-pinned Entra JWTs with WebCrypto against a cached key set.

The token
Checked
Call to the issuer
This replaced an arrangement in which issuer and gateway shared a private key.

The consent model around these tokens is in The hardening fix that locked everyone out.

Layers on the gateway that provisions GPUs

The model-facing MCP gateway in FAFO Core and Function Forge lets agents provision GPUs and push secrets, so one bearer check was not enough for it. I built a layered stack.

A granted call
Scope too narrow: stopped
Parent revoked: sub-agent ended
key set, rotatingwildcard scopesstep-upblacklist, rate limitsthe tool

A separate lane authenticates swarm agents by JWT. A sub-agent's identity is bound to its parent's lifetime, and minting it is atomic against the parent being revoked. Every tool call an agent makes carries its job identity, so its activity lands in a searchable trail in FAFOHive.

Signed messages between agents

When Agent Swarm agents delegate to one another, the receiver has to know who really sent a request and how deep the delegation already runs. I made every agent-to-agent message a signed JWT aligned with the Agent Client Protocol.

Envelope
Agent A
Agent B
Agent Cwithin depth
Agent A
Agent B
Agent Aloops: refused
Agent letters are illustrative. The receiver checks audience, issuer, hash and the chain.

Envelope hashes are computed over a canonical form with normalised floating-point values. Without that, the same number serialised at single and at double precision produced different hashes and broke signatures. Checking the chain at each hop defends against confused-deputy calls and runaway delegation, and the signing code is a small crate with no I/O and no business logic, so the protocol stack can be lifted out of my estate.

Identity from the token, not the arguments

The AgentOS managed-team runtime authenticates its member transport with JWTs, and on the AgentOS MCP tools a participant's identity comes from the authenticated token. The same rule holds on every surface: identity is derived server-side.

A caller cannot name itself in the arguments.

Name in the arguments
Identity in the token
tool inputserver side

I use standard JWT libraries and WebCrypto and wrote no cryptographic primitives. An agent cannot call what it was not granted and never holds a credential it does not need.