/** * In-memory port fakes — the ONLY seams that stand in for the true I/O boundaries P1/P3 own * (host registry / session registry / revocation store / token buckets / audit sink / revocation * bus). Every security decision still runs through the REAL relay-auth exports; these fakes only * supply storage. Adapted from `relay-auth/test/_helpers.ts` (same shapes, bare import paths). */ import type { HostRecord, KillSignal, RevocationBus } from 'relay-contracts' import type { AuthenticatedPrincipal, AuditEvent, AuditSink, HostRegistryPort, SessionRegistryPort, RevocationStore, TokenBucketStore, } from 'relay-auth' /** An authenticated principal (the ONLY source of accountId, INV3). */ export function principal( accountId: string, overrides: Partial = {}, ): AuthenticatedPrincipal { return { kind: 'human', accountId, principalId: 'cred-' + accountId, amr: ['passkey'], authAt: 1000, stepUpAt: null, ...overrides, } } /** A minimal online HostRecord for the authz registry (agentPubkey here is unused by authz). */ export function makeHostRecord( accountId: string, hostId: string, status: HostRecord['status'] = 'online', ): HostRecord { return { hostId, accountId, subdomain: 'sub-' + hostId, agentPubkey: new Uint8Array(32), enrollFpr: 'fpr-' + hostId, status, lastSeen: '2026-01-01T00:00:00.000Z', createdAt: '2026-01-01T00:00:00.000Z', revokedAt: null, } } export function fakeHostRegistry(hosts: readonly HostRecord[]): HostRegistryPort { const map = new Map(hosts.map((h) => [h.hostId, h])) return { getById: async (id) => map.get(id) ?? null } } export interface MutableSessionRegistry extends SessionRegistryPort { add(entry: { sessionId: string; hostId: string; accountId: string }): void } export function fakeSessionRegistry( seed: readonly { sessionId: string; hostId: string; accountId: string }[] = [], ): MutableSessionRegistry { const map = new Map(seed.map((s) => [s.sessionId, { hostId: s.hostId, accountId: s.accountId }])) return { getById: async (id) => map.get(id) ?? null, add: (e) => { map.set(e.sessionId, { hostId: e.hostId, accountId: e.accountId }) }, } } export interface RevocationFake extends RevocationStore { readonly revoked: Set readonly consumed: Set } export function fakeRevocationStore(): RevocationFake { const revoked = new Set() const consumed = new Set() return { revoked, consumed, isRevoked: async (jti) => revoked.has(jti), revokeJti: async (jti) => { revoked.add(jti) }, consumeOnce: async (jti) => { if (consumed.has(jti)) return false consumed.add(jti) return true }, } } export interface TokenBucketFake extends TokenBucketStore { readonly blocked: Set readonly calls: string[] } /** Token bucket that always allows unless a key is added to `blocked`. */ export function fakeTokenBucket(): TokenBucketFake { const blocked = new Set() const calls: string[] = [] return { blocked, calls, take: async (key) => { calls.push(key) return !blocked.has(key) }, } } export interface AuditFake extends AuditSink { readonly events: AuditEvent[] } export function fakeAuditSink(): AuditFake { const events: AuditEvent[] = [] return { events, append: async (e) => void events.push(e) } } export interface RevocationBusFake extends RevocationBus { readonly published: KillSignal[] } export function fakeRevocationBus(): RevocationBusFake { const published: KillSignal[] = [] return { published, publish: async (s) => void published.push(s) } }