/* RFC-1187: contracts for the agent registration tier — the persisted registration record, the storage port (Upstash Redis REST adapter satisfies it), the key-proof verifier port (the astro adapter supplies the network fetch), and the token-side verified-identity shape. Pure types + interfaces — no fetch, no Redis client, no key material here. Do not implement storage — the adapter lives in integration/agent-registry.ts. Do not implement signature verification — the verifier impl lives in the engine gate. Do not define route shapes — the gate handlers own request/response schemas. RFC-1187: initial agent registration contracts (registry, verifier, verified identity). RFC-1187: phases 1-3 — contracts, Redis registry, AS core + Bearer pipeline RFC-1187: phases 5-7 — OAuth route/discovery generators, env schema, operator commands RFC-1187: fo-review — VerifiedAgent carries the registration record (no second registry fetch) */ /** * Persisted registration. `credentialSecretHash` is a sha256 hex of the * issued secret — plaintext is returned to the registrant once and NEVER * stored (enhance decision: hash-only persistence). */ export interface AgentRegistration { /** Stable identity — `agent-`, derived, never chosen. */ agentId: string; /** https URL of the agent's HTTP-message-signatures key directory (JWKS). */ signatureAgent: string; /** sha256 hex of the current credential secret. */ credentialSecretHash: string; /** Granted scopes (e.g. ["agent.actions.elevated","agent.registration"]). */ scopes: string[]; /** ISO 8601 first-registration timestamp. */ registeredAt: string; /** Monotonically increasing — bumped on rotate; tokens carry it implicitly via secret validity. */ credentialVersion: number; /** ISO 8601 revocation timestamp — present only while the registration is dead. */ revokedAt?: string; /** Optional operator note recorded by `agent.registration.revoke --reason`. */ revocationReason?: string; } /** * Registration storage port — satisfied by `restRedisAgentRegistry` * (integration/agent-registry.ts) over the Upstash Redis EU REST endpoint. * All methods throw on transport error; callers decide fail-open semantics. */ export interface AgentRegistrationRegistry { /** Fetch a registration by id; null when absent. Revoked records are still returned — the caller checks `revokedAt`. */ get(agentId: string): Promise; /** Upsert a registration (create or rotate-rewrite). */ put(registration: AgentRegistration): Promise; /** * Issue a single-use challenge nonce (SET with TTL). The register endpoint * consumes it via `consumeChallenge` — a nonce verifies at most once. */ putChallenge(nonce: string, ttlSeconds: number): Promise; /** Atomically consume a challenge (GETDEL semantics). True iff the nonce existed and was unconsumed. */ consumeChallenge(nonce: string): Promise; /** All registrations — operator `agent.registration.list`. */ list(): Promise; } /** * Registration proof submitted by the registrant: `signature` is the agent's * Ed25519 signature over the UTF-8 bytes of `challenge`, verifiable against a * key from the JWKS document at `signatureAgent`. */ export interface AgentKeyProof { signatureAgent: string; /** base64url Ed25519 signature over the challenge bytes. */ signature: string; /** Server-issued, single-use nonce. */ challenge: string; /** Optional key id — narrows verification to one JWK in the directory. */ kid?: string; } /** * Key-proof verifier port. The concrete impl lives at the Worker boundary * (engine astro.ts) — it performs the signatureAgent JWKS fetch under the * RFC-1187 SSRF guards (https-only, redirect/size/timeout caps). The gate * core sees only this narrow surface, keeping it pure and vitest-loadable. */ export interface AgentRegistrationVerifier { /** Returns the derived agentId when the proof verifies; null otherwise. */ verify(proof: AgentKeyProof): Promise<{ agentId: string } | null>; } /** The resolved identity of a verified Bearer token — stamped into the action pipeline. */ export interface VerifiedAgent { agentId: string; scopes: string[]; /** * The registration record fetched during Bearer resolution — callers * (whoami/rotate) reuse it instead of a second registry roundtrip. */ registration: AgentRegistration; } /** Scopes the site issues — closed vocabulary, extended only by RFC. */ export const AGENT_SCOPE_ACTIONS_ELEVATED = "agent.actions.elevated" as const; export const AGENT_SCOPE_REGISTRATION_SELF = "agent.registration" as const; /** Derive the stable agentId from the registration's identity key material. */ export function deriveAgentId( signatureAgent: string, sha256Hex: (input: string) => string, ): string { return `agent-${sha256Hex(signatureAgent).slice(0, 16)}`; }