Protocol -- June 2026

The Agent Registration Protocol

Giving autonomous agents a real identity -- without passwords, without long-lived API keys, and without a human approving every enrollment. Just cryptographic key pairs and tokens that expire before anyone can steal them.

Ayush Gupta June 6, 2026 9 min read
Hand-drawn diagram of autonomous AI agents enrolling with a central registry server using public keys and signed tokens, in a warm beige and brown palette

In my last essay I argued that the internet is changing again -- that every foundational layer of software is growing an agentic counterpart. One of those layers is authentication. Traditional auth assumes a human sits on one end: a username, a password, a session cookie, an OAuth consent screen. Agents break that assumption completely. When an autonomous agent spins up inside a container at 3 a.m. and needs to call your API, there is no human to click "Allow," no browser to hold a session, and no safe place to stash a long-lived secret.

So I built a small protocol to explore what agent-native authentication could look like: the Agent Registration Protocol. It is deliberately minimal -- dependency-light, standard primitives, readable in an afternoon -- but it captures the core idea: an agent should be able to enroll and prove who it is using cryptography, not shared secrets.


01 The Problem With Secrets

Almost every way we authenticate machines today comes down to a secret that both sides know: an API key, a password, a bearer token minted once and pasted into an environment variable. That model has three problems that get dramatically worse when the "client" is an autonomous agent rather than a careful human.

1

Secrets leak, and leaked secrets are forever

A long-lived API key in a log line, a git commit, or a compromised container is valid until someone notices and rotates it. Agents copy context around constantly -- the surface area for a leak is enormous.

2

Onboarding needs a human

Provisioning an API key is usually a manual step -- a dashboard, an approval, a copy-paste. That does not scale to fleets of agents that come and go by the thousand.

3

The server has to store the secret

If the server keeps a copy of the credential to compare against, a database breach hands an attacker every identity at once. The blast radius is the whole system.

The fix is old and well understood: asymmetric cryptography. Let each agent hold a private key that never leaves the machine, hand the server only the matching public key, and prove identity by signing things rather than by revealing a secret. The Agent Registration Protocol is just a careful application of that idea to the agent enrollment problem.


02 The Core Idea

Three actors, and a strict rule about who knows what:

Host

A tenant or organization. Registers once and receives a one-time enrollment token that lets its agents join under it.

Agent

Generates its own key pair locally, registers its public key under a host, and signs a fresh token for every request it makes.

Server

Stores only public keys and hashed tokens. Verifies signatures on every request. Never sees a private key or a password.

The protocol leans on two primitives that exist in every major language: Ed25519 key pairs (fast, modern elliptic-curve signatures) and short-lived JWTs signed with those keys. An agent's stable identity is the SHA-256 fingerprint of its public key -- deterministic, collision-resistant, and derived from the key itself rather than assigned by anyone.


03 The Three-Phase Flow

Enrollment and authentication break cleanly into three phases. Each one tightens the trust boundary a little further.

Phase 1 · Host Registration
An organization gets an enrollment token
A host registers and receives a one-time enrollment token (64 hex characters, with an expiry). Critically, the server stores only the SHA-256 hash of that token -- so even a database dump does not reveal a usable credential.
Phase 2 · Agent Enrollment
An agent registers its public key
The agent generates an Ed25519 key pair locally, then calls POST /agents/register with the enrollment token and its base64-encoded 32-byte public key. The server checks the token hash, verifies the key is exactly 32 bytes, confirms the fingerprint is unique, and stores the public key. The private key never leaves the agent.
Phase 3 · Authenticated Requests
The agent signs a fresh token per call
For every protected request, the agent mints a new JWT valid for 60 seconds, signs it with its private key, and sends it as Authorization: Bearer <jwt>. The server looks up the stored public key by fingerprint and verifies the signature. No session, no stored secret, no replay window worth attacking.
Why this is safer

At no point does a reusable secret cross the wire or sit in a database. The server holds public keys (safe to leak) and token hashes (useless once hashed). An attacker who captures a signed request gets a token that is already expiring in under a minute and is bound to a specific agent. There is nothing durable to steal.


04 What the Token Looks Like

The authentication token is a standard JWT, but signed with EdDSA rather than the usual HMAC or RSA. The header advertises the algorithm and a custom type so the server can reject anything that is not an agent token:

Agent JWT
// header
{ "alg": "EdDSA", "typ": "agent+jwt" }

// payload
{
  "sub": "<sha256-fingerprint-of-public-key>",
  "iat": 1780000000,
  "exp": 1780000060,   // 60 seconds later
  "jti": "<random-uuid>"  // unique per request
}

The clever part is the sub claim. It carries the agent's fingerprint, which lets the server look up the right public key before trusting anything else in the token. You peek at sub, fetch the stored key, and only then verify the signature. If the signature does not check out against that key, the whole token is rejected -- the claim is never trusted on its own. The jti is a fresh UUID per request, which makes optional replay-prevention (remembering recently seen jti values) cheap to bolt on.


05 Enrolling an Agent, End to End

Here is the whole agent side in a few lines of standard-library Node.js -- generate a key pair, derive the fingerprint, register, then sign a request token. No SDK required.

agent.js
import { generateKeyPairSync, createHash, sign } from 'node:crypto';

// 1. Generate an Ed25519 key pair locally
const { publicKey, privateKey } = generateKeyPairSync('ed25519');

// 2. Extract the raw 32-byte public key and derive the fingerprint
const raw = publicKey.export({ type: 'spki', format: 'der' }).subarray(-32);
const fingerprint = createHash('sha256').update(raw).digest('hex');

// 3. Register the public key under a host using its enrollment token
await fetch('https://api.example.com/agents/register', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({
    enrollmentToken: process.env.ENROLLMENT_TOKEN,
    publicKey: raw.toString('base64'),
    name: 'invoice-reconciler',
  }),
});

// 4. For each request, mint and sign a 60-second JWT
const now = Math.floor(Date.now() / 1000);
const header  = b64url({ alg: 'EdDSA', typ: 'agent+jwt' });
const payload = b64url({ sub: fingerprint, iat: now, exp: now + 60, jti: uuid() });
const data    = `${header}.${payload}`;
const sig     = sign(null, Buffer.from(data), privateKey).toString('base64url');
const jwt     = `${data}.${sig}`;
// -> Authorization: Bearer ${jwt}

The Python client mirrors this almost line for line with PyNaCl's SigningKey.generate() and hashlib.sha256(). Because the protocol is built on plain Ed25519 and base64url JWTs, it is genuinely framework-agnostic -- any language with a crypto library can speak it.


06 The One Sharp Edge: Key Wrapping

There is exactly one place where this trips people up, and it is worth calling out because I lost time to it. Most JWT libraries -- jose on the server, for example -- expect Ed25519 public keys in SPKI (ASN.1) format, not as raw 32-byte blobs. But the protocol transmits and stores the raw key, because that is what the fingerprint is computed over.

So before you can import a stored key for verification, you have to wrap the 32 raw bytes back into SPKI by prepending a fixed 12-byte ASN.1 prefix:

server.js -- verify
// Raw 32-byte Ed25519 key -> SPKI DER for jose
const SPKI_PREFIX = Buffer.from('302a300506032b6570032100', 'hex');
const spki = Buffer.concat([SPKI_PREFIX, rawPublicKey]);
const key  = await importSPKI(derToPem(spki), 'EdDSA');

// Peek at sub WITHOUT trusting it, look up the key, THEN verify
const { sub } = decodeJwt(token);
const stored  = await lookupAgentByFingerprint(sub);
const { payload } = await jwtVerify(token, key, {
  algorithms: ['EdDSA'],
  typ: 'agent+jwt',
});

The order matters: look up the key by the claimed fingerprint, then verify the signature against that key. Never trust sub until the signature proves the holder controls the matching private key.


07 Old World vs. Agent-Native

It helps to put the two models side by side. The shift is from "share a secret and hope it stays secret" to "prove control of a key and let the proof expire."

API Keys & Passwords

Shared secret known to both sides.

Server stores a comparable secret
Leaked key is valid until rotated
Human provisions each credential

Agent Registration

Cryptographic proof, no shared secret.

Server stores only public keys
Captured token expires in 60 seconds
Agent self-enrolls, no human in loop

08 The Security Model in Full

Pulling the guarantees together, here is what the protocol gives you and what it deliberately keeps simple:

Concern How it is handled
Private key safety Generated and stored only on the agent, file mode 0600. Never transmitted.
Token theft 60-second expiry makes a captured JWT worthless almost immediately.
Replay Unique jti per request; server can remember recent ones (Redis-backed) to reject reuse.
Database breach Server stores only public keys and hashed enrollment tokens -- nothing reusable.
Revocation Revoke a single agent's key, or revoke a host to cut off every agent enrolled under it at once.
Key rotation An agent can rotate its key pair while keeping a stable identity record.
Transport HTTPS only, with same-origin redirect restrictions.

Multi-tenancy falls out naturally: enrollment tokens are scoped per host, so an agent always belongs to exactly one organization, and capability controls can be layered on top per agent or per host.


09 Where This Fits

I do not think the Agent Registration Protocol is the final answer -- bigger efforts like Google's agent identity work and emerging verifiable-credential standards will define the production shape of this. What I wanted was something small enough to understand completely, that demonstrates the principles that any serious agent-auth system will share:

  • Identity is a key pair, not a row in a credentials table.
  • Proof is a signature, not a revealed secret.
  • Credentials expire by default, so capture is not catastrophe.
  • Enrollment is autonomous, because agents will outnumber humans.

As autonomous agents move from demos into infrastructure -- running in containers, calling each other, acting with delegated authority -- they need an identity layer built for machines, not retrofitted from human login flows. Agent identity will end up as fundamental to the agentic internet as TLS certificates were to the early web. This protocol is one small, readable step toward that.

The full implementation -- Node and Python clients, a reference server, and the spec -- is on GitHub. It is meant to be read, forked, and argued with.