import type { DIDDoc, DIDLog, ServiceEndpoint, Signer } from '@interop/did-method-webvh'; import type { IZcap } from '@interop/data-integrity-core'; import type { ZcapClient } from '@interop/ezcap'; import type { IDelegatedZcap, SpaceMetadata, WasClient } from '@interop/was-client'; import type { ResourceLogPinStore } from '@interop/vh-resource-log'; import type { LadderRung } from './ladder.js'; import type { PublishedWebvhLog, WebvhIdStore } from '../webvh/didWebvh.js'; import type { AccountLogSigner } from '../webvh/accountEntry.js'; import type { PublishedKeyDocument } from '../webvh/listClients.js'; import type { WebvhLogResourceStore } from '../webvh/wasIdStore.js'; /** * The Space Description `type` array of the auxiliary annex Space, set at * creation (the server treats a Space's `type` as immutable afterwards). * Wire-level and permanent: the server's inspector clause recognizes the * `DelegatedClientsSpace` member, and user-data surfaces exclude auxiliary * Spaces by it. Sorted lexically, as the WAS spec recommends for a stable * serialization. */ export declare const CLIENT_ANNEX_SPACE_TYPE: string[]; /** * A Space Description together with the validator the version it was read or * written at is pinned to. The validator is what a following compare-and-swap * write hands back as its baseline; it is absent only where the `ETag` header * did not reach the client. */ export type SpaceDescriptionRead = SpaceMetadata & { etag?: string; }; /** * The literal prefix of every generation collection's name. Wire-level and * permanent: orphan discovery is a plain prefix match over the auxiliary * Space's collection listing, and the generation id embeds in every annex * DID string ever published. */ export declare const GENERATION_ID_PREFIX = "gen-"; /** * Mints a fresh generation id -- the generation collection's name, e.g. * `gen-Ux3v0kQf9aPmB2hZ`. Random rather than a counter on purpose: never-reuse * is structural (nothing durable survives GC to carry a counter), at the same * probabilistic order as every other random-id convention in the system. * * @returns {string} */ export declare function mintGenerationId(): string; /** * Refuses anything that is not a well-formed generation id. Run by every * annex builder that takes a generation id, so a malformed one is refused * before it can reach a DID string, an HKDF label, or a collection id. * * @param generationId {string} */ export declare function assertGenerationId(generationId: string): void; /** * The WAS-backed store an annex generation's ceremonies read and publish * through with controller-tier signing (an enrolled client). A transient * session writes through the delegated store instead * (`delegatedWebvhLogStore`, invoking the credential's sibling delegation); * both carry the same CAS/ETag conditional-publish discipline. * * @param options {object} * @param options.was {WasClient} * @param options.spaceId {string} the auxiliary annex Space's id * @param options.generationId {string} the generation collection's name * @param options.pinStore {ResourceLogPinStore} this client's chain-head * pins; the store derives each log's slot * @param [options.capability] {IZcap} an invocation capability every request * rides (the sibling delegation, where the caller is not an enrolled * invoker); absent, requests invoke the root capability * @returns {WebvhLogResourceStore} */ export declare function clientAnnexLogStore({ was, spaceId, generationId, pinStore, capability }: { was: WasClient; spaceId: string; generationId: string; pinStore: ResourceLogPinStore; capability?: IZcap; }): WebvhLogResourceStore; /** * Creates the one-entry annex generation log. The genesis parameters are * the annex inventory (see the module doc): prerotation on via the rung-0 * hash commitments, no witnesses, portability off (the library's default, * stated explicitly in the emitted entry), and a bare document -- id and the * DID core context, nothing else. * * The caller supplies the update authority: the minting credential's * annex rung-0 key as the sole `updateKeys` member, `nextKeyHashes` as * every standing credential's rung-0 hash (restated explicitly on every later * entry, never inherited), and rung 0's signer. The minting key's own * carry-over hash MUST be among the commitments -- every annex entry * re-states `updateKeys` containing the revealed rung-0 keys, and the * resolver checks the re-statement against the previous entry's commitments * -- so a `nextKeyHashes` that omits it is refused here rather than * publishing a generation no one can ever extend. * * @param options {object} * @param options.wasServerUrl {string} * @param options.spaceId {string} the auxiliary annex Space's id * @param options.generationId {string} the generation collection's name * @param options.updateKeyPublicKeyMultibase {string} the minting * credential's annex rung-0 key * @param options.nextKeyHashes {string[]} every standing credential's * rung-0 hash, the minting credential's included * @param options.signer {Signer} the minting credential's rung-0 signer * @returns {Promise<{ log: DIDLog; did: string; doc: DIDDoc }>} */ export declare function createClientAnnexLog({ wasServerUrl, spaceId, generationId, updateKeyPublicKeyMultibase, nextKeyHashes, signer }: { wasServerUrl: string; spaceId: string; generationId: string; updateKeyPublicKeyMultibase: string; nextKeyHashes: string[]; signer: Signer; }): Promise<{ log: DIDLog; did: string; doc: DIDDoc; }>; /** * Ensures the auxiliary annex Space exists: created with the typed * Description ({@link CLIENT_ANNEX_SPACE_TYPE}) under the given controller when * absent, verified when present. The `type` array must ride the create -- the * server accepts it at creation only and treats it as immutable afterwards -- * which is also why an existing Space at this id that is NOT typed as the * delegated-clients Space is refused loudly: it can never become one. * * The Space id is minted by the caller at credential bind time with the * account Space's `mintSpaceId` convention (32 random bytes, base64url * no-pad): the sibling delegation's `invocationTarget` embeds the id and is * sealed into the unlock record before the account DID exists, so no * derivation over the account identity is possible -- and hash-derived * addressing would import the unlock Spaces' existence-oracle exposure, * unwanted here. * * Both arms answer with the read's or the write's own `ETag`, so a caller * flipping the controller straight afterwards can hand the Description back * as `current` instead of paying for a second read: a compare-and-swap write * is refused outright when its baseline carries no validator. The create runs * through `configure`, which sends the guarded create, owns the create-race * rebase, and answers with the write's own validator. * * @param options {object} * @param options.was {WasClient} * @param options.spaceId {string} the auxiliary annex Space's id * @param options.controller {string} the Space controller (the account * did:webvh where it exists; a bootstrap did:key on a ladder-anchored signup, * promoted the same way the account Space's controller is) * @returns {Promise} the Space * Description this call read (the Space already existed) or wrote (it did * not), with the validator that version is pinned to */ export declare function ensureClientAnnexSpace({ was, spaceId, controller }: { was: WasClient; spaceId: string; controller: string; }): Promise; /** * Mints a fresh annex generation with controller-tier signing: ensures * the typed auxiliary Space, mints a fresh random generation id, creates the * generation collection, and publishes the genesis `did.jsonl` as a * create-if-absent -- the same conditional-publish discipline as every log * write, though a fresh random generation id makes a create collision * negligible. * * The account document's `#DelegatedClients` service entry is deliberately * NOT written here: the annex log publishes first, and the caller * re-points the account document at the returned DID afterwards. A run torn * between the two leaves an unpointed generation -- authorization-inert (no * delegation names it), collected by the standing `gen-` prefix orphan * discovery at the next remembered login. * * A re-run after a tear mints a FRESH generation rather than resuming: the * genesis entry is timestamped, so a re-created log has a different SCID and * a resume could never land its create-if-absent PUT; the torn generation is * an inert orphan like any other. * * @param options {object} * @param options.was {WasClient} the storage client, signing as an enrolled * client (or the bootstrap controller on a ladder-anchored signup) * @param options.wasServerUrl {string} * @param options.spaceId {string} the auxiliary annex Space's id * @param options.controller {string} the auxiliary Space's controller, used * only when the Space does not exist yet * @param options.updateKeyPublicKeyMultibase {string} the minting * credential's annex rung-0 key * @param options.nextKeyHashes {string[]} every standing credential's * rung-0 hash, the minting credential's included * @param options.signer {Signer} the minting credential's rung-0 signer * @param options.pinStore {ResourceLogPinStore} this client's chain-head * pins; the store derives each log's slot * @returns {Promise} the published head of the * genesis log, which a stage building the generation's next entry can * stand on instead of re-reading it, carrying the PUT's own ETag when the * store handed one back. `spaceDescription` is the auxiliary Space's * Description as the ensure read or wrote it, so a caller flipping the * controller afterwards need not re-read it. Always present here, since * this minter always runs the ensure. */ export declare function mintClientAnnexGeneration({ was, wasServerUrl, spaceId, controller, updateKeyPublicKeyMultibase, nextKeyHashes, signer, pinStore }: { was: WasClient; wasServerUrl: string; spaceId: string; controller: string; updateKeyPublicKeyMultibase: string; nextKeyHashes: string[]; signer: Signer; pinStore: ResourceLogPinStore; }): Promise; /** * Mints a fresh annex generation signed by a standing CREDENTIAL's * annex rung 0 -- the mint a ladder-seed holder runs (a credential-in-hand * login, or a test harness standing in for one). The generation id must exist * before the update authority can: the rung-0 key derives from the ladder * seed AND the generation id (`clientAnnexRung`), so this helper mints the * generation id * first, derives the rung, and states its own carry-over hash in * `nextKeyHashes` -- {@link mintClientAnnexGeneration}'s caller-supplied-key * shape cannot express that ordering. Everything else matches it: the typed * Space ensure, the collection create, the create-if-absent genesis publish, * and the pointer deliberately left to the caller. * * @param options {object} * @param options.was {WasClient} the storage client, signing as an enrolled * client (or the bootstrap controller on a ladder-anchored signup) * @param options.wasServerUrl {string} * @param options.spaceId {string} the auxiliary annex Space's id * @param options.controller {string} the auxiliary Space's controller, used * only when the Space does not exist yet * @param options.ladderSeed {Uint8Array} the minting credential's ladder * seed, from its unlock record * @param options.pinStore {ResourceLogPinStore} this client's chain-head * pins; the store derives each log's slot * @param [options.extraNextKeyHashes] {string[]} the OTHER standing * credentials' rung-0 hashes for this generation id, when the account has * more * than one; the minting credential's own carry-over hash is always included * @param [options.capability] {IZcap} an invocation capability the mint * rides -- the transient-recovery continuation minting its fresh generation * through the credential's sibling delegation (the auxiliary Space). The * typed-Space ensure is then skipped, and must stay skipped: a standing * sibling delegation presupposes the auxiliary Space, and the ensure's * Metadata PUT is refused for the sibling delegation at the server, on * either signing arm, by the storage server's container rule. Re-adding * the ensure here would answer a masked 404 instead of minting the * generation * @returns {Promise} the published head of the * genesis log, which a stage building the generation's next entry can * stand on instead of re-reading it, carrying the PUT's own ETag when the * store handed one back. `spaceDescription` is the auxiliary Space's * Description as the ensure read or wrote it, so a caller flipping the * controller afterwards need not re-read it. Present exactly when the * ensure ran, so absent under a supplied `capability`. */ export declare function mintCredentialClientAnnexGeneration({ was, wasServerUrl, spaceId, controller, ladderSeed, pinStore, extraNextKeyHashes, capability }: { was: WasClient; wasServerUrl: string; spaceId: string; controller: string; ladderSeed: Uint8Array; pinStore: ResourceLogPinStore; extraNextKeyHashes?: string[]; capability?: IZcap; }): Promise; /** * The type IRI of the account document's delegated-clients service entry -- * the pointer at the current annex generation's DID. Wire-level and * permanent: readers (this module's {@link delegatedClientsPointer}, the * server's annex-chain inspector clause) dispatch on this IRI, never on * the entry's fragment id, which is non-semantic by convention. */ export declare const DELEGATED_CLIENTS_SERVICE_TYPE = "https://w3id.org/byoe#DelegatedClients"; /** * Builds a fresh delegated-clients service entry for the account document. * The `serviceEndpoint` is the annex DID STRING, deliberately not a URL: * the DID is self-certifying and host-independent, and the account pointer * already carries the host. * * @param options {object} * @param options.accountDid {string} the account did:webvh * @param options.clientAnnexDid {string} the current generation's annex * DID * @returns {ServiceEndpoint} */ export declare function delegatedClientsServiceEntry({ accountDid, clientAnnexDid }: { accountDid: string; clientAnnexDid: string; }): ServiceEndpoint; /** * The annex DID the account document currently points at: the * `serviceEndpoint` of the service entry whose `type` names (or includes) * {@link DELEGATED_CLIENTS_SERVICE_TYPE}. Only a bare DID-string endpoint * counts -- the same predicate the server's inspector clause evaluates, so * wallet and server can never disagree on which generation is pointed. * * @param options {object} * @param options.doc {DIDDoc} the resolved (and verified) account document * @returns {string | undefined} */ export declare function delegatedClientsPointer({ doc }: { doc: DIDDoc; }): string | undefined; /** * Every auxiliary annex Space the account log's `#DelegatedClients` pointer * has ever named, oldest first and one entry per Space. A pointer entry is * append-only: a superseded value stops being current but its Space survives * the move, so an enumeration that reads only the resolved document (the * {@link delegatedClientsPointer} case) misses every Space a generation swap * has left behind. Several generations of one Space collapse to the entry * that named it first. * * The account deletion ceremony is the reader: it must name each auxiliary * Space it is about to destroy, and the log is the only durable record of * the superseded ones. Each entry carries the annex DID and its host beside * the Space id, because an account that has migrated hosts leaves entries * this deployment cannot address: deleting `spaceId` on the CURRENT host * would address a Space that is not the one the entry names, and a 404 there * would otherwise read as a clean deletion. Such an entry is the caller's to * report as a residue. * * The acting unlock record's own `delegatedClients` sibling target is NOT * included here; a caller that wants it unions it in itself, since a torn * establishment can converge on a Space no pointer entry ever named. * * An entry carrying no document state, and an endpoint that does not parse * as a client-annex DID, are skipped rather than refused: the walk is an * enumeration aid, and a caller cannot act on an id it could not read. * * @param options {object} * @param options.log {DIDLog} the VERIFIED account log * @returns {Array<{ did: string, host: string, spaceId: string }>} the * Spaces, in log order */ export declare function delegatedClientsSpaceHistory({ log }: { log: DIDLog; }): Array<{ did: string; host: string; spaceId: string; }>; /** * The account document's `service` array with the delegated-clients pointer * set to `clientAnnexDid`. An existing pointer entry -- one * {@link delegatedClientsPointer} would read, by the same predicate -- is * re-pointed in place, its fragment id preserved verbatim (the id is * non-semantic and stable); absent one, a fresh entry is appended. Every * other service entry is carried through untouched. * * Shared by the two writers of the pointer: the standalone * {@link setDelegatedClientsPointer} entry, and the transient-recovery * continuation, which folds the pointer into its own add-and-retire entry so * the pointer can never lag the entry that retires the standing ladder VMs. * * @param options {object} * @param options.doc {DIDDoc} the current account document * @param options.accountDid {string} the account did:webvh * @param options.clientAnnexDid {string} the generation to point at * @returns {ServiceEndpoint[]} */ export declare function servicesPointedAtClientAnnex({ doc, accountDid, clientAnnexDid }: { doc: DIDDoc; accountDid: string; clientAnnexDid: string; }): ServiceEndpoint[]; /** * The unlock-record sibling delegation's `allowedAction` set: GET beside PUT, * so an enrolling transient client can read the annex head it appends to. * Wire-level and permanent (wallet-core decision 0005): the server's * inspector clause admits a delegated-clients delegation with `allowedAction` * a subset of exactly this pair. * * The PUT in this set covers the annex log append it is minted for. It does * not reach the auxiliary annex Space's own Metadata PUT: the storage * server's container rule accepts only a direct root invocation there, * whatever `allowedAction` a delegated capability carries. That refusal holds * whether the delegation is signed by the account ladder VM or by an * enrolled client's promoted signer. */ export declare const DELEGATED_CLIENTS_DELEGATION_ACTIONS: string[]; /** * The sibling delegation's lifetime: the house standing-zcap value (one * year; see `standingZcap.ts`). It rots on exactly the account bridge's axis * -- same signer, same current-key-set rule, same renewal window -- so * whatever refreshes the bridge refreshes it too. */ export declare const DELEGATED_CLIENTS_DELEGATION_TTL_MS: number; /** * Mints one delegated-clients (annex Space) delegation: the pre-minted * zcap sealed into a standing credential's unlock record beside the account * bridge, which is what lets a transient login reach the annex log with * nothing but the credential. The shape is a permanent wire artifact * (wallet-core decision 0005): * * - `invocationTarget` is the AUXILIARY annex Space, named by its canonical * container URL (the trailing-slash form) and built with was-client's * paths helpers so the bytes match the server's target check on a sub-path * deployment. That URL is the Space and the root of its subtree alike, so * the delegation reaches every Collection beneath it. Generation coverage * comes from generation-id-bounded attenuation over the flat `gen-` * collection names, so no GC cycle rewrites the record or the registry. * - `controller` is the credential-derived signing DID (the same grantee * the account bridge names). * - `allowedActions` is {@link DELEGATED_CLIENTS_DELEGATION_ACTIONS}. * - The chain is rooted directly in the auxiliary Space's root zcap. * - `expires` is {@link DELEGATED_CLIENTS_DELEGATION_TTL_MS} out. * * @param options {object} * @param options.zcapClient {ZcapClient} the delegating signer (an * enrolled client's promoted signer, or the account ladder VM) * @param options.wasServerUrl {string} the auxiliary Space's storage * server (the account pointer's host) * @param options.clientAnnexSpaceId {string} the auxiliary annex * Space's id * @param options.controller {string} the credential-derived signing DID * @param [options.now] {number} epoch milliseconds, for tests * @returns {Promise} */ export declare function mintDelegatedClientsDelegation({ zcapClient, wasServerUrl, clientAnnexSpaceId, controller, now }: { zcapClient: ZcapClient; wasServerUrl: string; clientAnnexSpaceId: string; controller: string; now?: number; }): Promise; /** * Builds the annex-side sibling-delegation minter a record re-bind takes as * an injected closure -- the boundary keeping a caller outside this subpath * free of annex imports. The returned closure reads the auxiliary annex Space * id off the verified document's delegated-clients service entry (the annex * DID string embeds it) and mints a fresh * {@link mintDelegatedClientsDelegation} to the named controller; it resolves * `undefined` while the document points at no generation, which a caller * reads as "carry the old sealed member verbatim". * @param options {object} * @param options.doc {object} the locally verified account document * @param options.zcapClient {ZcapClient} the acting client's promoted * signer, which mints the fresh delegations * @param options.wasServerUrl {string} the auxiliary Space's storage * server (the account pointer's host) * @returns {Function} `({ controller }) => Promise` */ export declare function delegatedClientsDelegationMinter({ doc, zcapClient, wasServerUrl }: { doc: object; zcapClient: ZcapClient; wasServerUrl: string; }): (options: { controller: string; }) => Promise; /** * The auxiliary annex Space id a delegated-clients delegation targets, * read out of its `invocationTarget` (the items-subtree URL, * `.../space//`). The id has no other home -- a transient * login learns the Space from the delegation it unwraps, and a refresh pass * that holds the old delegation rebuilds the target from it -- so this parse * is the one reader. Returns `undefined` on anything that is not an * items-subtree Space URL. * * @param options {object} * @param options.delegation {IZcap} a delegated-clients delegation * @returns {string | undefined} */ export declare function delegatedClientsDelegationSpaceId({ delegation }: { delegation: IZcap; }): string | undefined; /** * The type IRI of the annex document's generation-delegation service * entry -- the generation's standing Space-scoped zcap, embedded where an * enrolling transient client can reach it before it holds any other * authority. Wire-level and permanent: readers (this module's * {@link embeddedGenerationDelegation}, the app-side loader) dispatch on this * IRI, never on the entry's fragment id, which is non-semantic by the byoe * service-entry convention. */ export declare const GENERATION_DELEGATION_SERVICE_TYPE = "https://w3id.org/byoe#GenerationDelegation"; /** * The generation delegation's `allowedAction` set: the full closed WAS * action vocabulary. Wire-level and permanent (the app-connect-spec * generation-delegation record): attenuation is structural, not enumerated * -- child-within-parent is enforced on both actions and targets, so any * verb missing here would cap every transient-visit App Connect grant below * its enrolled-client shape. * * The target's own bytes have never changed: this delegation named the Space * URL with its trailing slash before WAS v0.5 and names it still. What moved * is the server's layout beneath those bytes. The two writes that would end * the account -- the Space Metadata PUT (a controller rewrite) and the Space * DELETE -- used to sit at the slash-less Space URL, outside the target. * Under v0.5 the DELETE is at the target itself and the Metadata PUT is one * segment inside it, so both are now within the capability. * * The storage server's container rule closes both on every signing arm, * independent of who signed the chain: the Space Metadata PUT accepts only a * direct root invocation, and the Space DELETE accepts a delegated capability * only when the invoked capability's target is exactly the Space URL with * `allowedAction` exactly `['DELETE']`. This delegation's full-action-set * grant on the subtree target meets neither shape, so both writes are refused * whether the delegation is signed by the account ladder VM or by an enrolled * client's promoted signer. Narrowing the set here is still not the remedy * for the same reason as before: a verb missing from it would cap every * transient App Connect grant below its enrolled-client shape. * * The one remaining narrowing is closed by the signer, not by the shape. A * transient annex VM holds both the invocation and delegation relations, so * it could mint a `['DELETE']`-only child of this delegation whose target is * exactly the Space URL and invoke that child itself, meeting the exception. * The server's client-annex clause refuses a Space DELETE or Space Metadata * PUT whose chain carries any link signed by a transient annex VM, whoever * signed the links above it: a per-visit key never ends an account or its * annex. The clause recognizes that VM as the two-relation sibling in the * annex document, the same walk that recognizes a ladder VM by its relation * asymmetry. No wallet delete is signed by a transient VM (a transient login * signs its DELETE-only children with the ladder VM), so the rule costs no * admitted shape. */ export declare const GENERATION_DELEGATION_ACTIONS: string[]; /** * The generation delegation's lifetime: the house standing-zcap value (one * year; see `standingZcap.ts`). GC's explicit revoke is the intended * end-of-life; expiry is the backstop, deliberately not matched to the * quarterly GC cadence -- a 90-day-class TTL would begin renewal churn * exactly when GC is merely due. */ export declare const GENERATION_DELEGATION_TTL_MS: number; /** * Mints one generation delegation: the standing Space-scoped zcap a * generation's transient clients invoke under. The shape is a permanent wire * artifact (the app-connect-spec generation-delegation record): * * - `invocationTarget` is the ACCOUNT Space, named by its canonical * container URL (the trailing-slash form) and built with was-client's * paths helpers so the bytes match the server's target check on a sub-path * deployment. That URL is the Space and the root of its subtree alike (see * {@link GENERATION_DELEGATION_ACTIONS} for what keeps the Space itself * out of reach). * - `controller` is the bare annex DID string. Transient keys invoke as * `#`, and the server's inspector clause compares this * string against the account document's delegated-clients pointer. * - The chain is rooted directly in the account Space's root zcap, so an * App Connect grant delegated under it forms the depth-3 chain * `[root id string, this delegation embedded]`. * - `expires` is {@link GENERATION_DELEGATION_TTL_MS} out. * * The delegation signer is the caller's choice of licensed authority: the * account ladder VM (`ladderVmZcapClient`) or an enrolled client's * promoted signer (`webvhZcapClient`). * * @param options {object} * @param options.zcapClient {ZcapClient} the delegating signer (ladder VM * or an enrolled client's promoted signer) * @param options.wasServerUrl {string} the ACCOUNT Space's storage server * @param options.spaceId {string} the ACCOUNT Space's id * @param options.clientAnnexDid {string} the generation's annex DID * @param [options.now] {number} epoch milliseconds, for tests * @returns {Promise} */ export declare function mintGenerationDelegation({ zcapClient, wasServerUrl, spaceId, clientAnnexDid, now }: { zcapClient: ZcapClient; wasServerUrl: string; spaceId: string; clientAnnexDid: string; now?: number; }): Promise; /** * A child grant's `expires` under the generation delegation: the requested * TTL, clamped to the delegation's own expiry -- the library's per-hop * monotonicity rule IS the TTL clamp, so a grant minted past the parent's * `expires` would verify nowhere. By construction the bounded grants (30-day * read, 7-day write) always receive their full TTL; only 365-day-class * grants ever meet the clamp, at 30 or more days remaining (the * renew-precedes-mint stage keeps the delegation outside its renewal window * whenever a grant is minted). * * @param options {object} * @param options.ttlMs {number} the grant's requested TTL * @param options.delegation {IZcap} the generation delegation * @param [options.now] {number} epoch milliseconds, for tests * @returns {Date} */ export declare function clampGrantExpires({ ttlMs, delegation, now }: { ttlMs: number; delegation: IZcap; now?: number; }): Date; /** * Builds a fresh generation-delegation service entry for an annex * document. The `serviceEndpoint` is the full delegated-zcap JSON as a * single map, byte-identical to what `zcapClient.delegate` produced -- the * annex entry proof (JCS canonicalization) then covers it byte for byte, * so host tampering with the stored delegation is client-visible. * * @param options {object} * @param options.clientAnnexDid {string} the generation's annex DID * @param options.delegation {IZcap} the minted generation delegation * @returns {ServiceEndpoint} */ export declare function generationDelegationServiceEntry({ clientAnnexDid, delegation }: { clientAnnexDid: string; delegation: IZcap; }): ServiceEndpoint; /** * The generation delegation an annex document carries: the * `serviceEndpoint` map of the service entry whose `type` names (or * includes) {@link GENERATION_DELEGATION_SERVICE_TYPE}. Only a map-form * endpoint counts (the delegation is embedded as the zcap JSON itself, * never as a URL or an encoded string). * * @param options {object} * @param options.doc {DIDDoc} the resolved (and verified) annex * document * @returns {IZcap | undefined} */ export declare function embeddedGenerationDelegation({ doc }: { doc: DIDDoc; }): IZcap | undefined; /** * Every generation delegation a generation's log has ever embedded, in log * order and deduplicated by zcap id -- the annex-log HISTORY WALK the * last-client forget revokes from (decision 0004's 2026-08-19 * amendment): a renewal replaces the head service entry's endpoint in place, * so a superseded delegation's bytes survive only in earlier entries' * re-stated full state, and a renewal inside the 30-day window can leave TWO * still-unexpired ladder-signed delegations. The caller filters (signer, * expiry) and revokes; this walk only recovers the bytes. * * @param options {object} * @param options.log {DIDLog} the generation's VERIFIED log * @returns {IZcap[]} */ export declare function generationDelegationHistory({ log }: { log: DIDLog; }): IZcap[]; /** * What {@link revokeTreatingAlreadyRevokedAsSuccess} did: `revoked` (the POST * landed), `already-revoked` (was-client's `AlreadyRevokedError`, the * server's genuine `capability-already-revoked` answer to a resumed * ceremony's blind re-POST), `expired` (the delegation's own `expires` is * past beyond any clock's doubt, so no POST was sent; or the server refused * the POST while `now` sits inside the skew band around `expires`, where the * refusal is read as the server's own expiry check), or `signer-gone` (the * server refused the POST and the delegation's proof key has checkably left * the supplied verified account document -- the current-key-set rule -- so * the refusal is read as the chain having rotted, the same axis * `ensureGenerationDelegationCurrent`'s SIGNER DEATH replaces such a * delegation on). */ export type RevokeGenerationDelegationOutcome = 'revoked' | 'already-revoked' | 'expired' | 'signer-gone'; /** * Submits the revocation of a generation delegation and classifies the * server's answer. The one local skip is a delegation whose own `expires` is * past by more than the revocation clock-skew margin * ({@link delegationExpired}): every server within the margin refuses it as * expired, so the POST is not sent. Everything else is POSTed, whatever the * caller's own account document says about the signer: the document a login * read is a snapshot, a ladder VM struck by credential retirement or by the * last-client transition's strike-and-reinstall pair can stand again on the * server by the time this runs, and a client clock ahead of the server's * would otherwise read a delegation the server still honors as dead. A * revocation the server accepts is the only proof the delegation is off the * account. * * A refusal is then read in three steps. was-client's `AlreadyRevokedError` * -- the server's genuine `capability-already-revoked` 400, raised on a * resumed ceremony's blind re-POST -- is success. A plain `ValidationError` * is the server's one answer for a chain that fails to verify (an expired * delegation, a proof key the resolved document no longer lists, a tampered * or foreign-rooted chain) and for a malformed submission alike, so it is * classified on what the client can check: with `now` inside the skew band * around the delegation's `expires` ({@link delegationAtExpiry}) it reads * as `expired`, and with the proof key checkably absent from `accountDoc` * under `capabilityDelegation` ({@link delegationSignerGone}) it reads as * `signer-gone`. Every other refusal is rethrown: a root-capability refusal, * a foreign `invocationTarget`, a malformed body, an id mismatch, or a chain * the server refuses for a reason the client cannot see are all still * failures the caller decides on. Matched on `err.name` -- error classes do * not survive crossing package copies. The `revoke` seam is was-client's * `WasClient#revoke`, bound by the caller. * * An absent proof key id, or one with no `#fragment`, never reads as * `signer-gone`: skipping past a refusal on nothing more than a missing * field would risk leaving a genuinely live delegation unrevoked forever. * The classification's residue is stated rather than hidden: a refusal for * an unrelated reason, landing while the caller's snapshot happens to lack * the signer, reads as `signer-gone` and is not retried. * * @param options {object} * @param options.revoke {Function} `(delegation) => Promise` -- * POSTs the revocation (`was.revoke`) * @param options.delegation {IZcap} * @param options.now {number} epoch milliseconds, read against the * delegation's own `expires` * @param options.accountDoc {PublishedKeyDocument} the locally VERIFIED * account document, read against the delegation's proof key when the * server refuses the POST * @returns {Promise} */ export declare function revokeTreatingAlreadyRevokedAsSuccess({ revoke, delegation, now, accountDoc }: { revoke: (delegation: IDelegatedZcap) => Promise; delegation: IZcap; now: number; accountDoc: PublishedKeyDocument; }): Promise; /** * Parses the host, the auxiliary Space id and the generation id out of an * annex DID string. All three are permanent substrings of every annex DID by * construction: the generation id is the final path segment of the annex * DID (`did:webvh:::...:space::`), and it * is the generation-identifying half of the annex rung HKDF * labels, so this parse is what lets an enrollee derive its writing key from * the pointer alone -- no log read, no registry. * * The host is the method-specific id's second segment, percent-decoded (a * port rides as `%3A` inside the one segment). A caller enumerating Spaces * out of a log compares it against the deployment it is talking to: an * account that has migrated hosts carries entries naming the old one, which * this deployment cannot address. * * @param options {object} * @param options.did {string} an annex did:webvh string * @returns {{ host: string, spaceId: string, generationId: string }} */ export declare function clientAnnexDidParts({ did }: { did: string; }): { host: string; spaceId: string; generationId: string; }; /** * Thrown when the published annex log commits neither the writing * credential's rung-0 key nor its hash -- the mid-generation lockout: a * credential bound after the generation's genesis cannot write the annex * until an existing writer commits its rung-0 hash or the next GC swap's * genesis does. Typed so callers can map it to the fresh-generation path * where one is licensed (the transient-recovery continuation) or to honest * copy where none is. */ export declare class ClientAnnexRungUncommittedError extends Error { constructor(message: string); } /** * The narrow store seam an annex entry is read and published through: the * log read and the conditional `did.jsonl` PUT, nothing else (an annex has * no `did.json` projection and no key map). Satisfied by * {@link clientAnnexLogStore} (controller-tier signing) and by the delegated * store a transient session writes through (`delegatedWebvhLogStore`, * invoking the credential's sibling delegation). */ export type ClientAnnexWriteStore = Pick; /** * Reads and resolves a generation's published annex log, or resolves * `undefined` when its `did.jsonl` does not exist -- even under a held pin. * * The pinned read refuses an absent log under a held pin as a `rollback`, * which is right for the account log: a full truncation is never "not yet * provisioned". A generation is different. It is deleted by design (the GC * collect, an abandoned annex Space), and every arm that tells a dead or * absent generation from a live one -- the readiness ensure's re-point, the * last-client transition's `log-unreadable` escape, the GC's orphan collect * -- is gated on that absence. A client that read the generation earlier and * still holds its pin would otherwise be refused exactly where it must mend, * with nothing that ever drops the slot. So the read runs in the pinned * read's absence-tolerant mode: a genuinely absent log is absence (the pin * left standing), and a log that is served and falls behind the pin stays * refused, so a served prefix is never mistaken for a missing log. * * @param options {object} * @param options.store {ClientAnnexWriteStore} * @param [options.expectedDid] {string} * @returns {Promise} */ export declare function readClientAnnexLogOrAbsent({ store, expectedDid }: { store: ClientAnnexWriteStore; expectedDid?: string; }): Promise; /** * TRANSIENT ENROLLMENT: publishes one per-visit verification method into a * annex generation's log -- one atomic entry, signed by the writing * credential's static rung 0 (derived from the ladder seed and the generation * id; * see `clientAnnexRung`). The entry: * * - reveals the writer's rung-0 key into `updateKeys` at its first annex * write (later writes re-state it unchanged); * - re-states `nextKeyHashes` verbatim -- every standing credential's rung-0 * hash, the writer's own carry-over hash included -- explicitly on the * entry, never inherited from the prior entry's parameters; * - adds the transient VM under `capabilityInvocation` AND * `capabilityDelegation`, with all five relationship arrays stated * explicitly. Both relations are deliberate (decision 0013): the key * invokes the generation delegation for the visit's own WAS requests, and * it delegates the App Connect and share grants that visit mints onward * under that same parent. The other three stay excluded, each on its own * reason -- `authentication` because the DIDAuth path signs as the bare * did:key, `assertionMethod` because the annex log is not an assertion * venue for a per-visit key, and the `keyAgreement` twin because the * controller-marker convention does not arise in the annex at all. * * The transient key set carries no update key, and nothing here touches the * ACCOUNT log's `updateKeys` or `nextKeyHashes`. There is no two-entry * reveal/add split and no attribution scan: a CAS loser re-signs with the * SAME key via the ordinary conflict retry, and resumability reduces to the * published document's own state -- a VM already present is a no-op. * * A writer whose rung-0 key is neither revealed nor committed is refused * ({@link ClientAnnexRungUncommittedError}): annex entries verify against * the log's own hash-commitment chain, so no admission rule can make an * uncommitted key verify mid-log. * * @param options {object} * @param options.store {ClientAnnexWriteStore} the generation's log store * (delegated through the credential's sibling delegation, or * controller-tier) * @param options.ladderSeed {Uint8Array} the credential's ladder seed, from * its unlock record * @param options.generationId {string} the generation collection's name * @param options.transientKeyMultibase {string} the visit's in-memory * Ed25519 signing key, public multibase * @param [options.services] {ServiceEndpoint[]} the annex document's * full service-entry list, replacing the published one wholesale; omitted, * the prior entries are preserved verbatim (or extended by * `mintGenerationDelegation` below). Supplying both is refused in favor of * the explicit list * @param [options.mintGenerationDelegation] {Function} * `({ clientAnnexDid }) => Promise` -- mints the generation * delegation this entry installs when it publishes the generation's FIRST * transient verification method (and the document carries no delegation * entry yet). Never invoked otherwise: the delegation is installed with * the first transient VM or by the GC ceremony's own install stage, never * by genesis (a genesis-embedded signed zcap can never verify -- its * `controller` embeds the SCID the genesis hash derives from) * @param [options.expectedDid] {string} the annex DID the log must * resolve to, from the account document's pointer. The read and the publish * run under the store's own chain-head pin * @param [options.published] {PublishedWebvhLog} a head the caller already * read and verified under this same pin slot, so the enrollment builds its * entry on it instead of spending a second round trip on the same log (the * transient visit's one-read composition). The FIRST attempt alone rides it: * a lost compare-and-swap means the head is stale by definition, so the * conflict retry re-reads under the pin. That threaded attempt is EXTRA * rather than one of the retry's three: a caller who saved a read is left * with the same conflict budget as one who did not * @returns {Promise<{ did: string, doc: DIDDoc, log: DIDLog }>} */ export declare function enrollClientAnnexTransientClient({ published: threadedHead, ...rest }: { store: ClientAnnexWriteStore; ladderSeed: Uint8Array; generationId: string; transientKeyMultibase: string; services?: ServiceEndpoint[]; mintGenerationDelegation?: (options: { clientAnnexDid: string; }) => Promise; expectedDid?: string; published?: PublishedWebvhLog; }): Promise<{ did: string; doc: DIDDoc; log: DIDLog; }>; /** * Points the account document's delegated-clients service entry at a * annex DID -- the first install after a generation's genesis, and the GC * swap's re-point alike. One ordinary document-update entry on either signer * arm; the annex log always publishes FIRST (see * {@link mintClientAnnexGeneration}), so a tear leaves an unpointed, * authorization-inert generation, never a dangling pointer. * * On the ladder arm the entry is the pointer move a credential-only caller * makes (the transient readiness pass, the establishment's stage 3): the * seam attributes the ladder's current rung per attempt, the rung reveals * itself in the entry it signs, and when it stood only committed the entry * also commits the next rung's hash, so one entry does what a reveal entry * followed by a client-signed pointer entry used to. A lost race re-runs the * attribution from the winner's head, which is what climbs to the winner's * committed rung (retry-up-the-ladder). * * An existing delegated-clients entry is re-pointed in place, its fragment id * preserved verbatim (the id is non-semantic and stable); absent one, a fresh * entry is appended ({@link delegatedClientsServiceEntry}). Every other * service entry, the verification methods, and the relationship arrays are * preserved untouched. Idempotent: a document already pointing at the DID is * a no-op on the log (and, unless `logOnly`, it republishes `did.json` from * the resolved log, which a controller-invoking caller has the authority to * do). * * @param options {object} * @param options.idStore {WebvhIdStore} the ACCOUNT log's store; with * `logOnly`, only its log read and `did.jsonl` PUT are used, so the narrow * delegated seam satisfies it * @param options.signer {AccountLogSigner} who signs the pointer entry: an * enrolled client's own update-key seeds (`{ kind: 'client', updateKeys }`, * the GC swap's re-point and an enrolled client's stage-3 fold), or a * standing credential's ladder seed (`{ kind: 'ladder', ladderSeed }`), * under which the acting rung is attributed from the head each attempt * builds on * @param options.clientAnnexDid {string} the generation to point at * @param [options.expectedDid] {string} the account DID the log must * resolve to, from the account pointer. The read and the publish run under * the store's own chain-head pin * @param [options.logOnly] {boolean} publish `did.jsonl` only, never the * `did.json` projection. Defaults per arm, as {@link signAccountEntry} * does: `true` on the ladder arm, since a transient visit writes through * the record's bridge delegation, whose narrow scope covers nothing but the * log, and `false` on the client arm. The establishment's stage 3, a ladder * signer over a root-invoking store, passes `false` explicitly. A lagging * projection is republished when some caller holding an `id`-collection * writer runs `ensureDidWebProjection` over the resolved log; on a * client-less account that is a transient visit under its generation * delegation. The log stays the source of truth meanwhile, and the server * reads it rather than the projection, so the lag is a `did:web` * verifier's concern alone * @param [options.published] {PublishedWebvhLog} a head the caller already * read and verified under this same pin slot, so the pointer entry builds * on it instead of spending a second round trip on the log the caller just * read or published (the establishment's one-read composition). The FIRST * attempt alone rides it: a lost compare-and-swap means the head is stale * by definition, so the conflict retry re-reads under the pin. That * threaded attempt is EXTRA rather than one of the retry's three: a caller * who saved a read is left with the same conflict budget as one who did not * @returns {Promise<{ did: string, doc: DIDDoc, * published: PublishedWebvhLog, rung?: LadderRung }>} `published` is the * head this call leaves standing: the post-entry one, paired with its * publish's own ETag, when the entry was appended; the head it stood on * verbatim when the document already pointed at the DID. `rung` stands on * the ladder arm alone: the rung the entry was signed with, or, when the * document already pointed, the ladder's currently attributed rung */ export declare function setDelegatedClientsPointer({ published: threadedHead, ...rest }: { idStore: WebvhIdStore; signer: AccountLogSigner; clientAnnexDid: string; expectedDid?: string; logOnly?: boolean; published?: PublishedWebvhLog; }): Promise; /** * What a pointer entry leaves standing: the resolved document, the head * (post-entry, or the read verbatim on the idempotent path), and on the * ladder arm the rung the seam attributed. */ export type PointerEntryOutcome = { did: string; doc: DIDDoc; published: PublishedWebvhLog; rung?: LadderRung; }; /** * ONE attempt of {@link setDelegatedClientsPointer}, re-invoked by that * function's conflict retry and exported for a caller that runs its own * retry around it. * * A caller that already read the head passes it as `published`, and the * entry is built on exactly that head. A racing entry landing in between * then loses the CAS on the PUT and surfaces as a * {@link WebvhLogConflictError} for the retry to re-run. * * @param options {object} see {@link setDelegatedClientsPointer}, plus: * @param [options.published] {PublishedWebvhLog} the verified head to build * this entry on, when the caller has already read it under the same pin; * absent, the attempt reads the head itself * @returns {Promise} */ export declare function setDelegatedClientsPointerOnce({ idStore, signer, clientAnnexDid, expectedDid, logOnly, published: alreadyRead }: { idStore: WebvhIdStore; signer: AccountLogSigner; clientAnnexDid: string; expectedDid?: string; logOnly?: boolean; published?: PublishedWebvhLog; }): Promise; /** * The whole transient-enrollment ceremony as the enrollee runs it: resolve * the account document's delegated-clients pointer, enroll the visit's key * into the pointed generation, then RE-READ the pointer -- the GC-race * closure. An enrollment landing between a GC pass's guard check and its * re-point would otherwise yield a session whose generation the pointer then * abandons and whose delegation is already revoked; the enrollee closes the * race with one extra read, re-enrolling into the fresh generation on a * mismatch. Convergent under retry (each round enrolls into whatever the * pointer names NOW), and idempotent per generation like the entry itself. * * @param options {object} * @param options.readAccountDocument {Function} reads the VERIFIED account * document (the caller's `verifyAccountLog` read, pins and `expectedDid` * applied there); called once per round * @param options.storeForGenerationId {Function} builds the generation's log * store for a generation id (the delegated store over the credential's * sibling * delegation, or a controller-tier store) * @param options.ladderSeed {Uint8Array} the credential's ladder seed * @param options.transientKeyMultibase {string} the visit's in-memory * signing key, public multibase * @param [options.mintGenerationDelegation] {Function} * `({ clientAnnexDid }) => Promise` -- forwarded to the enrollment * entry, which installs the minted delegation when it publishes the * generation's first transient VM (see * {@link enrollClientAnnexTransientClient}). The closure receives whichever * annex DID the round enrolls into, so a GC-race re-enroll mints for * the fresh generation * @param [options.maxRounds] {number} how many pointer moves to chase * before giving up (a GC pass is quarterly, so more than one mid-ceremony * move means something else is wrong) * @param [options.published] {PublishedWebvhLog} a generation head the * caller already read and verified under this same pin store (the readiness * stage's, when it published nothing to that log). Round 0 alone rides it, * and only when it is the generation the account document points at NOW: a * head for any other generation is ignored and the round reads fresh, as * every later round does * @returns {Promise<{ clientAnnexDid: string, doc: DIDDoc, log: DIDLog }>} */ export declare function enrollTransientClient({ readAccountDocument, storeForGenerationId, ladderSeed, transientKeyMultibase, mintGenerationDelegation: mintDelegation, maxRounds, published: threadedHead }: { readAccountDocument: () => Promise; storeForGenerationId: (generationId: string) => ClientAnnexWriteStore; ladderSeed: Uint8Array; transientKeyMultibase: string; mintGenerationDelegation?: (options: { clientAnnexDid: string; }) => Promise; maxRounds?: number; published?: PublishedWebvhLog; }): Promise<{ clientAnnexDid: string; doc: DIDDoc; log: DIDLog; }>; /** * The fresh-generation block every separate-pointer-entry caller runs: mint * a generation in the annex Space, install its generation delegation, then * append the account document's pointer entry naming it. The pointer write is * the injected step (`point`): an enrolled client signs it under its own * update keys, and a credential-only caller (the establishment, the * transient visit) moves it as the ladder, one rung-signed entry per attempt. A * caller with a step that must land * between the install and the pointer entry (the establishment's bootstrap * arm, whose Space still answers to its creation controller until it flips * it to the account DID) supplies `beforePointerEntry`. * * The install stands on the head the mint just published rather than * re-reading the log this run wrote a moment ago -- but only when that head * carries the PUT's own ETag, since the install's entry publishes under a * compare-and-swap and a head with no validator would degrade that to an * unconditional write. With no ETag the install reads for itself. Either way * its own publish advances the pin, so this generation's pin slot is * established by this run. * * @param options {object} * @param options.was {WasClient} the storage client the mint and the * install write through * @param options.wasServerUrl {string} * @param options.spaceId {string} the auxiliary annex Space's id * @param options.controller {string} the Space's creation controller (see * {@link mintCredentialClientAnnexGeneration}) * @param options.ladderSeed {Uint8Array} the minting credential's ladder * seed * @param options.mintGenerationDelegation {Function} * `({ clientAnnexDid }) => Promise` * @param options.point {Function} `(clientAnnexDid) => Promise` -- the * pointer entry, whose result rides the return verbatim * @param options.pinStore {ResourceLogPinStore} this client's chain-head * pins; the store derives each log's slot * @param [options.capability] {IZcap} the sibling delegation the annex * writes ride, for a caller holding a standing invocation authority * @param [options.beforePointerEntry] {Function} * `({ minted }) => Promise` -- runs after the install and before the * pointer entry, with the mint's published head (its `spaceDescription` * included) * @param [options.now] {number} epoch milliseconds, for tests * @returns {Promise<{ clientAnnexDid: string, generationDelegation: IZcap, * pointed: T }>} */ export declare function mintPointedClientAnnexGeneration({ was, wasServerUrl, spaceId, controller, ladderSeed, mintGenerationDelegation, point, pinStore, capability, beforePointerEntry, now }: { was: WasClient; wasServerUrl: string; spaceId: string; controller: string; ladderSeed: Uint8Array; mintGenerationDelegation: (options: { clientAnnexDid: string; }) => Promise; point: (clientAnnexDid: string) => Promise; pinStore: ResourceLogPinStore; capability?: IZcap; beforePointerEntry?: (options: { minted: Awaited>; }) => Promise; now?: number; }): Promise<{ clientAnnexDid: string; generationDelegation: IZcap; pointed: T; }>; /** * RENEW PRECEDES MINT: the blocking pre-mint stage a transient App Connect * approval runs before delegating any grant. Reads the annex document * and hands back its embedded generation delegation -- renewing it first * when it is stale under the house policy ({@link standingZcapStale}): a * fresh delegation is minted through the caller's closure * (ladder-signed -- the renewal must not depend on the very delegation it * replaces; published through the store, which in a transient session is * the credential's sibling delegation, so even a hard-expired delegation is * recoverable), and one annex entry replaces the service entry's * endpoint in place, signed by the writing credential's static rung 0. * * An annex document carrying no delegation entry at all installs one the * same way (the GC ceremony's own install stage and the first-VM install * make this rare; a heal, not a policy). * * That policy is three axes. Expiry -- past, or inside the 30-day renewal * window. SIGNER DEATH, which an `accountDoc` adds: a standing delegation * whose proof key is no longer in the supplied verified account document has * rotted under the current-key-set rule (the enrolled client that minted it * was revoked, or the ladder VM that signed it left with the first * self-enrollment). And RETIREMENT, the projected post-edit reading a caller * asks for with `retiringKeyMultibases`, for a key still listed whose * authority the ceremony is about to end. No revocation POST accompanies the * replacement: a rotted chain no longer verifies at the revocation endpoint, * and the expiry-renewal path never revoked either. * * Failure is the caller's failure: a renewal that cannot complete throws, * and the App Connect approval fails with the standard retryable-ceremony * convention -- deliberately no clamp-on-failure fallback, which would deliver * exactly the silently short grant this stage exists to prevent. By * construction a grant minted behind a completed renewal never meets the * monotonicity clamp below its full TTL except for 365-day-class grants at * 30 or more days remaining ({@link clampGrantExpires}). * * @param options {object} * @param options.store {ClientAnnexWriteStore} the generation's log store * (delegated through the credential's sibling delegation, or * controller-tier) * @param options.ladderSeed {Uint8Array} the credential's ladder seed, from * its unlock record * @param options.generationId {string} the generation collection's name * @param options.mintGenerationDelegation {Function} * `({ clientAnnexDid }) => Promise` -- mints the replacement * delegation (ladder-signed in a transient session) * @param [options.expectedDid] {string} the annex DID the log must * resolve to, from the account document's pointer. The read and the publish * run under the store's own chain-head pin * @param [options.accountDoc] {PublishedKeyDocument} the locally VERIFIED * account document; supplied, a standing delegation whose proof key it no * longer lists is replaced (the signer-death axis above) * @param [options.retiringKeyMultibases] {string[]} keys whose authority is * about to end, read as a projected post-edit document: a standing * delegation one of them signed is replaced even though the served document * still lists the key. The last-client transition's replacement stage names * its own ladder VM (whose delegations it revokes server-side in the next * breath, a state no client-side predicate can read) and the client its * removal entry has yet to strike * @param [options.now] {number} epoch milliseconds, for tests * @param [options.published] {PublishedWebvhLog} a head the caller already * read and verified under this same pin slot, so the stage builds on it * instead of spending a second round trip on the same log (the transient * visit's one-read composition). The FIRST attempt alone rides it: a lost * compare-and-swap means the head is stale by definition, so the conflict * retry re-reads under the pin. That threaded attempt is EXTRA rather than * one of the retry's three: a caller who saved a read is left with the same * conflict budget as one who did not * @returns {Promise<{ delegation: IZcap, renewed: boolean, * published?: PublishedWebvhLog }>} `published` is the verified head this * pass stood on -- the one read, or the one handed in -- and is present * ONLY when `renewed` is false. The two store implementations differ on * what a renewal's publish hands back: the controller-tier store forwards * the PUT's own ETag, while the delegated store discards the response and * yields none. This stage takes either, and cannot tell which from here, so * it stays conservative and passes no post-renewal head on; a caller * wanting one reads for itself */ export declare function ensureGenerationDelegationCurrent({ published: threadedHead, ...rest }: { store: ClientAnnexWriteStore; ladderSeed: Uint8Array; generationId: string; mintGenerationDelegation: (options: { clientAnnexDid: string; }) => Promise; expectedDid?: string; accountDoc?: PublishedKeyDocument; retiringKeyMultibases?: string[]; now?: number; published?: PublishedWebvhLog; }): Promise<{ delegation: IZcap; renewed: boolean; published?: PublishedWebvhLog; }>; /** * THE CLIENT-ANNEX RUNG STRIKE: drops a retired credential's annex inventory * from a generation's log -- its revealed rung-0 key out of `updateKeys` and * its standing rung-0 hash out of `nextKeyHashes` -- in one atomic entry * signed by ANOTHER credential's committed rung 0 (an annex entry cannot * remove its own signing key: the entry verifies against its own re-stated * `updateKeys`). The credential-rotation ceremony's annex reach. * * A log committing neither the retired rung's key nor its hash is already * clean and the strike no-ops (`struck: false`) -- the resumable shape, and * the common one: a credential that never minted or wrote this generation * has no inventory in it. An acting rung the log does not commit (after the * retired members are excluded -- so the retired credential can never sign * its own strike) is refused with {@link ClientAnnexRungUncommittedError}, * which the caller maps to the generation-swap fallback: a fresh generation * minted from a surviving credential's seed retires the rung with the whole * generation. * * @param options {object} * @param options.store {ClientAnnexWriteStore} the pointed generation's log * store (controller-tier, or delegated through a sibling delegation) * @param options.retiredLadderSeed {Uint8Array} the RETIRED credential's * ladder seed (its rung is derived per generation, so the seed is the only * way to name what to strike) * @param options.actingLadderSeed {Uint8Array} a surviving credential's * ladder seed, whose committed rung 0 signs the strike entry * @param options.generationId {string} the generation collection's name * @param [options.expectedDid] {string} the annex DID the log must * resolve to, from the account document's pointer. The read and the publish * run under the store's own chain-head pin * @returns {Promise<{ struck: boolean }>} */ export declare function retireClientAnnexRung(options: { store: ClientAnnexWriteStore; retiredLadderSeed: Uint8Array; actingLadderSeed: Uint8Array; generationId: string; expectedDid?: string; }): Promise<{ struck: boolean; }>; /** * THE CLIENT-ANNEX RUNG COMMIT: adds a freshly bound credential's rung-0 * hash to a generation's `nextKeyHashes` -- one atomic hash-restating entry * signed by an already-committed credential's rung 0. The bind ceremonies' * annex reach (passkey add, passphrase change): a bind runs from a logged-in * session whose own login credential's rung 0 is committed, so committing the * new credential's hash here is what keeps it out of the mid-generation * lockout ({@link ClientAnnexRungUncommittedError} at its first transient * login, otherwise standing until the next GC swap's genesis). * * A log already committing the bound rung's hash (or carrying its revealed * key) is a no-op (`committed: false`) -- the resumable shape. An acting rung * the log does not commit is refused with * {@link ClientAnnexRungUncommittedError}: the bind ceremony maps that to an * honest skip (nothing licenses it to mint a generation), and the lockout * consequence stands as documented. * * @param options {object} * @param options.store {ClientAnnexWriteStore} the pointed generation's log * store (controller-tier, or delegated through a sibling delegation) * @param options.boundLadderSeed {Uint8Array} the freshly bound * credential's ladder seed (its rung is derived per generation, so the seed * is the only way to name what to commit) * @param options.actingLadderSeed {Uint8Array} the logged-in session's * login credential's ladder seed, whose committed rung 0 signs the entry * @param options.generationId {string} the generation collection's name * @param [options.expectedDid] {string} the annex DID the log must * resolve to, from the account document's pointer. The read and the publish * run under the store's own chain-head pin * @returns {Promise<{ committed: boolean }>} */ export declare function commitClientAnnexRung(options: { store: ClientAnnexWriteStore; boundLadderSeed: Uint8Array; actingLadderSeed: Uint8Array; generationId: string; expectedDid?: string; }): Promise<{ committed: boolean; }>; //# sourceMappingURL=log.d.ts.map