Summary
ZeroID establishes who an agent is (identity) and what it may do (authorization), but has no channel layer. All transport security relies on standard mTLS, which provides server authentication and encryption but does not bind the channel to the specific agent identity presented in the ZeroID JWT. The proposed NHI IAM standard mandates forward-secret, identity-bound channels — specifically Noise XX or an equivalent construction — as a normative requirement.
Problem
With mTLS-only transport:
- No identity binding: The TLS certificate authenticates the service (hostname), not the agent. A valid ZeroID JWT can be replayed from any network-adjacent process that holds the token, not necessarily the agent that originally obtained it.
- No forward secrecy per agent: Standard TLS provides forward secrecy at the session level, but if an agent's long-term private key is compromised, past sessions cannot be proven secure in a post-incident forensic context.
- No mutual agent authentication at the channel layer: mTLS with service certificates does not prove that the specific agent identity in the JWT (
sub claim) corresponds to the private key used to establish the channel. The JWT is a bearer credential layered on top of an unrelated TLS session.
- Compliance gap: The proposed standard treats mTLS alone as insufficient for high-trust agent-to-agent communication. Forward-secret, identity-bound channels are required for Tier 2 and above trust levels.
Proposed Implementation
1. Per-agent ephemeral key pairs
Each agent, on initialization, generates an ephemeral Curve25519 key pair. The public key is registered with ZeroID at token-request time and embedded in the issued JWT as a confirmation key (cnf.x5t#S256 or a new cnf.x25519 claim following RFC 7800 / DPoP patterns).
{
"sub": "spiffe://acme.highflame.io/acct_123/proj_456/service/billing-agent",
"cnf": {
"x25519": "<base64url-encoded Curve25519 public key>"
}
}
Token issuance binds the JWT to this public key. The agent proves possession at every request by signing a per-request challenge (DPoP-style) or by using the key in the Noise handshake.
2. Noise XX handshake for agent-to-agent calls
For direct agent-to-agent communication (A2A patterns, multi-agent delegation chains):
- Use Noise XX pattern: both parties authenticate with static key pairs; transcript hash provides channel binding.
- Initiating agent presents its ZeroID JWT during the Noise handshake prologue so the responder can verify identity claims before the handshake completes.
- The resulting Noise session provides:
- Mutual authentication bound to ZeroID identity
- Forward secrecy (Diffie-Hellman ratchet)
- Channel binding: the session transcript hash can be included in subsequent ZeroID delegation tokens to prove the delegation occurred over an authenticated channel
3. DPoP-style proof for Gateway calls
For agent→Gateway (HTTP) calls, implement a DPoP-style mechanism (RFC 9449):
- Agent signs a proof JWT with its ephemeral private key, bound to the HTTP method, URL, and a timestamp.
- Gateway verifies the proof against the
cnf claim in the ZeroID access token.
- This prevents token replay: even a stolen ZeroID JWT cannot be used without the corresponding private key.
4. Key storage and lifecycle
- Development / standard deployments: ephemeral key in process memory; rotated on agent restart.
- Elevated trust: key stored in hardware (HSM/enclave) — see companion issue for hardware attestation.
- Key rotation: agent requests a new token with a new public key before the old token expires; ZeroID issues a new token with the updated
cnf claim.
- Revocation: revoking an agent's token implicitly invalidates the key binding.
5. Trust level gating
Introduce a channel_binding field in the ZeroID token trust level policy:
| Trust Level |
Channel Requirement |
standard |
mTLS (current behavior, no change) |
elevated |
mTLS + DPoP proof on every request |
high |
Noise XX with per-agent key, or mTLS + DPoP + hardware-backed key |
Agents requesting elevated or high trust tokens must supply a cnf public key during token request or the issuance fails.
6. SDK changes
- Add
HighflameClient(enable_dpop=True) / withDPoP() constructor option
- SDK generates ephemeral key pair, includes public key in token request, signs DPoP proofs transparently on each request
zeroid.TokenRequest struct gains PublicKey field
Acceptance Criteria
References
- NHI IAM proposed standard, Directive 3 (forward-secret identity-bound channels, Noise XX)
- RFC 9449 (DPoP — Demonstrating Proof of Possession)
- RFC 7800 (Proof-of-Possession Key Semantics for JWTs)
- Noise Protocol Framework: https://noiseprotocol.org/noise.html
- ZeroID SPIFFE URI and trust levels:
zeroid/internal/identity/
- ZeroID token issuance:
zeroid/internal/token/
Summary
ZeroID establishes who an agent is (identity) and what it may do (authorization), but has no channel layer. All transport security relies on standard mTLS, which provides server authentication and encryption but does not bind the channel to the specific agent identity presented in the ZeroID JWT. The proposed NHI IAM standard mandates forward-secret, identity-bound channels — specifically Noise XX or an equivalent construction — as a normative requirement.
Problem
With mTLS-only transport:
subclaim) corresponds to the private key used to establish the channel. The JWT is a bearer credential layered on top of an unrelated TLS session.Proposed Implementation
1. Per-agent ephemeral key pairs
Each agent, on initialization, generates an ephemeral Curve25519 key pair. The public key is registered with ZeroID at token-request time and embedded in the issued JWT as a confirmation key (
cnf.x5t#S256or a newcnf.x25519claim following RFC 7800 / DPoP patterns).{ "sub": "spiffe://acme.highflame.io/acct_123/proj_456/service/billing-agent", "cnf": { "x25519": "<base64url-encoded Curve25519 public key>" } }Token issuance binds the JWT to this public key. The agent proves possession at every request by signing a per-request challenge (DPoP-style) or by using the key in the Noise handshake.
2. Noise XX handshake for agent-to-agent calls
For direct agent-to-agent communication (A2A patterns, multi-agent delegation chains):
3. DPoP-style proof for Gateway calls
For agent→Gateway (HTTP) calls, implement a DPoP-style mechanism (RFC 9449):
cnfclaim in the ZeroID access token.4. Key storage and lifecycle
cnfclaim.5. Trust level gating
Introduce a
channel_bindingfield in the ZeroID token trust level policy:standardelevatedhighAgents requesting
elevatedorhightrust tokens must supply acnfpublic key during token request or the issuance fails.6. SDK changes
HighflameClient(enable_dpop=True)/withDPoP()constructor optionzeroid.TokenRequeststruct gainsPublicKeyfieldAcceptance Criteria
cnf.x25519claim defined and documentedelevated/hightrust tokens)snowfor Rust,noise-protocolfor Python/JS)channel_bindingfield respected at issuanceReferences
zeroid/internal/identity/zeroid/internal/token/