/** * `_broker` reserved-collection seed lifecycle (#479, credential-broker * spec §2/§3). Modeled on `with-party/team/sync-credentials.ts`: a * `_`-prefixed reserved collection, record id = `brokerId`, encrypted under * the collection's own DEK, `requireAdminAccess` role gate (owner/admin * only — custodian intentionally excluded, same rationale as * `_sync_credentials`: this is transport-auth material, not the * custodian's operational scope). * * Three operations, each a free function taking a {@link BrokerSeedCtx} * (the {@link BrokerCtx} threaded through `with-party/broker/active.ts`, * augmented there with the `config` its closure was built with): * * - {@link enrollSeed} — two-phase commit (I9): create-if-absent CAS the * seed (I4), then register its derived proof key with the broker host, * then (only on a 2xx) mark the record `registered: true`. A seed * persisted whose `/enroll` POST failed stays `registered: false` so a * later `credentialSource()` fails fast instead of degrading into an * opaque `BrokerProofError` (I9/V23). * - {@link rotateSeed} — quiesce-then-swap (I5): mint a fresh seed, * register-new-first, THEN overwrite the local record — closing the * torn-proof window a naive local-overwrite-then-register order would * open. The single-flight cache quiesce itself (await in-flight, bump * epoch) is the caller's job (`with-party/broker/active.ts`) since the * cache lives in that closure, not here. * - {@link mintStoreCredentials} — the challenge/response round trip * (`/challenge` then `/credentials`) that produces one `StoreCredentials` * value; the single-flight cache wrapping it lives in `active.ts`. * * First-seed creation calls `ensureCollectionDEK`, which throws * (`ValidationError`) when the keyring's KEK is `null` (PIN quick-resume / * session-restore) — this module intercepts that and re-surfaces it as * {@link BrokerEnrolmentError} (R-B8/I3): *enrol* needs the KEK; *use* of an * already-enrolled seed needs only the DEK. * * @module */ import type { StoreCredentials } from '../../kernel/types.js'; import type { BrokerSeedCtx } from '../../port/with/broker-strategy.js'; /** The `_broker/` record payload (spec §3). */ export interface BrokerSeedRecord { readonly brokerId: string; /** base64(32 random bytes). */ readonly seed: string; readonly endpoint: string; readonly createdAt: string; /** `true` only once `/enroll` has returned a 2xx for this seed (I9). */ readonly registered: boolean; } /** * Enrol (idempotent, two-phase commit — I9): create-if-absent the `_broker` * seed (I4), derive+register its proof key with the broker host, and only * on a 2xx mark the record `registered: true`. Requires owner/admin role * and (for first-seed creation only) the KEK. */ export declare function enrollSeed(ctx: BrokerSeedCtx): Promise; /** * Rotate (quiesce-then-swap — I5): mint a fresh seed, register its proof * key with the broker host FIRST, then overwrite the local `_broker` * record. The broker host is expected to accept both the old and new * registration for a short grace window, so an in-flight proof computed * under the old key (from before rotation started) still verifies. Cache * quiescing (awaiting any in-flight round-trip, bumping the refresh-cache * epoch) is the caller's job — `with-party/broker/active.ts` — since the * cache lives in that closure, not here. */ export declare function rotateSeed(ctx: BrokerSeedCtx): Promise; /** * The challenge/response round trip: `POST /challenge`, derive+sign the * proof from the decrypted seed, `POST /credentials`. One network round * trip per call — the single-flight/cache wrapping lives in `active.ts`. * * Fails fast with {@link BrokerEnrolmentError} if the seed hasn't completed * enrolment yet (`registered !== true` — I9/V23), never degrading into an * opaque {@link BrokerProofError}. */ export declare function mintStoreCredentials(ctx: BrokerSeedCtx, profile?: string): Promise;