/**
 * vault — publication (protocol rev 41 §6 + §4.4–4.7 + §5A client side;
 * task 5.4).
 *
 * What lives here:
 *   - the PURE evaluators: ref transactions (§6.1 — force-with-lease only,
 *     fast-forward for non-force branch updates, immutable tags, deletes need
 *     expected-old, pairwise-distinct refs refused BEFORE evaluation, the
 *     §6.5 cardinality bounds), retention-root evolution (§4.5 — map keyed
 *     `(ref, oid)`, renewal, expiry ONLY at checkpoints against the pre-signing
 *     cutoff ticket, strict `effective + 90d < cutoff_at`), the heads-listing
 *     page contract (§6.3 / D186 — anchor echo, cursor echo, has_more/
 *     next_cursor coupling, strictly-above-anchor, gapless across pages,
 *     truthful total), chain linkage (`CHAIN_BROKEN`), and the transition
 *     fail-closed rule (`UPGRADE_REQUIRED`);
 *   - {@link VaultTransport} — the control-plane + bucket operations the
 *     vault needs (extends task 5.3's creation transport) — and
 *     {@link createVaultHttpTransport}, the `fetch`-backed implementation
 *     over the SDK kernel (upload sessions → presigned PUT with
 *     `If-None-Match: *` → finalize);
 *   - {@link Vault} — discover/verify to newest with the RESUMABLE
 *     verification budget, materialize (decrypt ref_state + retention_roots →
 *     the materialized pin), push (pack_set or a checkpoint-bearing head when
 *     the delta exceeds the 64-receipt budget, upload with receipt-compare,
 *     admission with 409 re-apply-and-retry, head read-back before any pin
 *     advances), checkpoint build + acceptance self-check, and repair (the
 *     mandatory fresh checkpoint + the exact repair-root algebra).
 *
 * Dual pins (§6.4) live in the keystore repo file: `head_pin` is
 * `highest_authenticated`, `materialized_pin` is `highest_materialized` — the
 * ONLY push base. A regression below the authenticated pin is
 * `GENERATION_REGRESSION`; an authenticated-but-undecryptable head is
 * `CHAIN_UNUSABLE` (read-only at the materialized pin); an admitted non-null
 * transition is `UPGRADE_REQUIRED` (read-only past it, no publish).
 */
import type { Client } from "../kernel.js";
import { type VaultMirrorBackend } from "./vault-mirror-backend.js";
import type { VaultMirrorCredential, VaultMirrorDestination } from "./vault-mirror-config.js";
import { type VaultCheckpointStaleness } from "../namespaces/repos.js";
import type { VaultActivationToken, VaultCaptureBinding, VaultCaptureReceipt, VaultCheckpointBlock, VaultCheckpointClaimSet, VaultCheckpointManifest, VaultDigestLabel, VaultHead, VaultHeadTarget, VaultHeadsListingPage, VaultHeadsListingRequest, VaultOpenReceipt, VaultRecipientConfirmationReceipt, VaultRefState, VaultRefTransaction, VaultRefUpdate, VaultRepairDescriptor, VaultRetentionCutoff, VaultRetentionCutoffReceipt, VaultRetentionRoot, VaultRetentionRoots, VaultRetentionRootsReceipt, VaultRotateEpochPayload, VaultRotationAttemptDescriptor, VaultRotationReason, VaultGenesis } from "../namespaces/vault.types.js";
import type { VaultCreationTransport } from "./vault-creation-journal.js";
import { VaultKeystore, type VaultRepoFile } from "./vault-keystore.js";
import type { VaultPruneIntentRecord } from "./vault-prune.js";
export declare const VAULT_MAX_CANONICAL_REFS = 10000;
export declare const VAULT_MAX_REF_UPDATES_PER_TRANSACTION = 1000;
export declare const VAULT_MAX_RETENTION_ROOT_ENTRIES = 50000;
export declare const VAULT_MAX_REPAIR_ADDED_ROOTS = 10000;
export declare const VAULT_MAX_WAL_RECEIPTS_PER_HEAD = 64;
export declare const VAULT_MAX_CHECKPOINT_PACKS = 4096;
export declare const VAULT_MAX_CHECKPOINT_TOTAL_STORED_BYTES = 858993459200n;
export declare const VAULT_MULTI_OBJECT_PACK_TARGET_BYTES = 201326592;
export declare const VAULT_MAX_REF_STATE_OBJECT_BYTES = 33554432;
export declare const VAULT_MAX_HEADS_PER_LISTING_PAGE = 1000;
export declare const VAULT_VERIFICATION_BUDGET_HEADS = 100000;
export declare const VAULT_RETENTION_MIN_DAYS = 90;
/** Default admission-conflict retries before the push gives up (each retry re-verifies + re-applies to the winner). */
export declare const VAULT_PUSH_CONFLICT_RETRIES = 5;
/**
 * `run402@<version>/<entrypoint>` — the audit-provenance string named on
 * `self_open_attestation.reader_entrypoint` (D209) and
 * `recipient_open_receipt.reader_entrypoint` (D210): "names the client
 * implementation + entry point that produced the evidence." Never an
 * authorization input; no wire grammar is promised for it (protocol-v0.md
 * §4.14, `rotate_epoch_payload.json`'s own field `$comment`).
 */
export declare function vaultReaderEntrypoint(entrypoint: string): string;
export declare function generationToBigInt(generation: string): bigint;
export declare function bigIntToGeneration(value: bigint): string;
export declare function nextGeneration(generation: string): string;
export type VaultRefMap = Record<string, string>;
/** A tip that left the canonical map in this transaction — it enters `retention_roots` in the same generation. */
export interface VaultDroppedTip {
    ref: string;
    oid: string;
    reason: "deleted" | "force_displaced";
}
export interface VaultRefTransactionEvaluation {
    refs: VaultRefMap;
    dropped: VaultDroppedTip[];
}
export interface VaultEvaluateRefTransactionOptions {
    /** Ancestry oracle: true iff `ancestor` is reachable from `descendant`. */
    isAncestor: (ancestor: string, descendant: string) => Promise<boolean> | boolean;
    /** `refuse` (default — user pushes): any `refs/run402/*` update is refused; `allow`: the protocol's own deploy-ref move. */
    protocol_refs?: "refuse" | "allow";
}
/** One failing update of a refused transaction (never a silent revert). */
export interface VaultRefUpdateFailure {
    ref: string;
    reason: "expected_old_mismatch" | "non_fast_forward" | "tag_immutable" | "delete_requires_expected_old" | "noop";
    expected_old_oid: string | null;
    current_oid: string | null;
}
/**
 * Evaluate a §6.1 transaction against the materialized map. Refusals in
 * order: pairwise-distinct refs (before evaluation), grammar, the update cap,
 * then per-update semantics collected into ONE `REF_EXPECTED_OLD_MISMATCH`
 * (every failing update listed), then the resulting-state cardinality.
 */
export declare function evaluateRefTransaction(current: VaultRefMap, transaction: VaultRefTransaction, options: VaultEvaluateRefTransactionOptions): Promise<VaultRefTransactionEvaluation>;
/** §6.5 bound: ≤ 10 000 canonical refs and the serialized map ≤ 32 MiB. */
export declare function assertRefMapCardinality(refs: VaultRefMap): void;
/** The §4.4 deploy-ref move: force-with-lease `refs/run402/deploys/latest` → `oid` (creation when absent). */
export declare function deployRefTransaction(current: VaultRefMap, oid: string): VaultRefTransaction;
/** `effective_admitted_at = max(prepared_at, storage creation time of the winning admission record)` (§4.10). */
export declare function effectiveAdmittedAt(preparedAt: string, recordStorageCreatedAt: string): string;
/** A root may be removed iff `effective_admitted_at + 90 days < cutoff_at` (STRICT; §4.5a). */
export declare function isRootEligibleForRemoval(effectiveAdmittedAtIso: string, cutoffAtIso: string, retentionDays?: number): boolean;
export interface VaultEvolveRootsOptions {
    /** The generation being built (`g+1`) — stamped as `dropped_at_generation` on new/renewed keys. */
    generation: string;
    /** Tips dropped or force-displaced by this generation's transaction. */
    dropped: Array<{
        ref: string;
        oid: string;
    }>;
    /**
     * Present ONLY when this generation carries a checkpoint bound to a cutoff
     * ticket: expiry is evaluated against `cutoff_at` using the resolver for each
     * root's drop generation. Removal is PERMISSIVE — a resolver returning
     * `null` keeps the root.
     */
    checkpoint_cutoff?: {
        cutoff_at: string;
        effectiveAdmittedAt: (droppedAtGeneration: string) => string | null;
    };
}
export declare function compareRoots(a: VaultRetentionRoot, b: VaultRetentionRoot): number;
/** roots(g+1) = roots(g) ∪ dropped (RENEWING an existing `(ref, oid)` key) ∖ {expired, only at a checkpoint with a ticket}. */
export declare function evolveRetentionRoots(previous: VaultRetentionRoot[], options: VaultEvolveRootsOptions): VaultRetentionRoot[];
/**
 * The client-local, protocol-owned namespace (design D4). Distinct from the
 * VAULT-side `refs/run402/*` (e.g. `VAULT_DEPLOY_REF`) that rides the wire
 * as part of `ref_state` — `refs/r402/*` never rides the wire at all; it is
 * written directly into the local `.git` by the materializer and reconciled
 * on every later fetch/fsck. A push naming any ref under it is refused by the
 * remote helper before a transaction is ever built (see
 * `git-remote-run402.mjs`'s `partitionProtectedRefPushes`) — this constant is
 * the ONE place the namespace string is spelled, shared by both sides.
 */
export declare const VAULT_R402_REF_NAMESPACE = "refs/r402/";
/** Where a retained (branch-unreachable) deploy-capture tip gets its local ref (design D1/D2). */
export declare const VAULT_RETAIN_REF_PREFIX = "refs/r402/retain/";
/**
 * D1's ref-identity choice, recorded here because the design doc's own
 * assumption did not hold: `VaultRetentionRoot` carries no per-capture
 * stable id (only `{ref, oid, dropped_at_generation}`) — the capture id that
 * DOES exist (`VaultHead.capture_binding.capture_id`) lives on the head
 * that INTRODUCED a tip onto a canonical ref, not on the retention-root entry
 * recording its later displacement, and correlating the two would require
 * walking the chain further back than materialization already reads (D2
 * forbids new reads here). The commit oid itself is already in hand, is
 * content-addressed (so it is exactly as stable as a capture id — neither
 * ever changes for the same history), and needs no correlation at all — the
 * ref name IS the tip's own identity.
 */
export declare function vaultRetainedRefName(oid: string): string;
export interface VaultRetainedRefsReconcileResult {
    /** `refs/r402/retain/<oid>` refs created or moved this call. */
    written: string[];
    /** `refs/r402/retain/*` refs removed this call — a root the vault no longer retains. */
    deleted: string[];
    /** How many distinct retained, branch-unreachable, locally-present tips this call found. */
    retained_count: number;
    /**
     * Non-null on ANY bookkeeping failure (D3) — permissions, an exotic
     * filesystem, a git invocation error. The caller (fetch/fsck) turns this
     * into exactly one stderr note and otherwise proceeds unchanged: a clone,
     * fetch, or fsck NEVER fails because this could not be written.
     */
    warning: string | null;
}
/**
 * D2: install/remove local `refs/r402/retain/<oid>` refs so every retained
 * (branch-unreachable) tip the vault's materialized retention roots name is
 * locally referenced — the git-ecosystem `refs/pull/*` precedent, so a fresh
 * `git fsck` is silent and `git for-each-ref refs/r402/` names what is
 * retained and why (D6).
 *
 * Skips a root tip already reachable from a canonical ref (`state.refs`) or
 * the HEAD target when detached — no redundant refs (D2). Reconciliation is
 * namespace-scoped: only `refs/r402/retain/*` is ever read, written, or
 * deleted; nothing else is touched, even when other bookkeeping under
 * `refs/r402/*` exists.
 *
 * D3 — warn, never fail: driven from a SINGLE try/catch around the whole
 * operation (list existing → compute the desired set → one atomic
 * `update-ref --stdin` transaction for every create/update/delete). Any
 * failure anywhere in that sequence returns a `warning` string and touches
 * nothing further; it never throws, so a clone/fetch/fsck calling this can
 * never fail on it. Called only when `repoDir` is an actual git repository —
 * `repos fsck` addresses a vault by `repo_id`/`project_id` alone as often as
 * by a local checkout, and "no local repo here" is a normal, silent no-op,
 * never a warning.
 */
/**
 * Name prefix of the scratch directory {@link Vault.buildPacks}
 * allocates INSIDE the repository's git common dir. Dotted and clearly named
 * so it can never be mistaken for `objects/`, `refs/`, or a pack directory.
 */
export declare const VAULT_PACK_SCRATCH_PREFIX = ".run402-vault-packs-";
/**
 * Allocate the pack-objects scratch directory on the OBJECT STORE's own
 * filesystem: `<git-common-dir>/.run402-vault-packs-XXXXXX`, never under
 * `os.tmpdir()`.
 *
 * `git pack-objects <base>` writes its temporary pack under
 * `<git-common-dir>/objects/pack/` and then `rename(2)`s it onto
 * `<base>-<hash>.pack`. When `/tmp` is a different filesystem (tmpfs in a
 * cloud container) that rename fails INSIDE git with EXDEV ("unable to rename
 * temporary file … Invalid cross-device link") and no JS-side fallback can
 * catch it — so the base path has to share the object store's filesystem.
 * Git ignores unknown siblings of `objects/` and `refs/` (fsck, gc, and
 * object enumeration never walk them), so a leftover from a crash is inert;
 * the caller still removes it on every path.
 */
export declare function allocatePackScratchDir(repoDir: string): Promise<string>;
export declare function reconcileRetainedTipRefs(repoDir: string, state: {
    refs: VaultRefMap;
    roots: readonly VaultRetentionRoot[];
    head_target: VaultHeadTarget;
}): Promise<VaultRetainedRefsReconcileResult>;
export interface VaultListingProgress {
    /** The anchor (constant across the sequence). */
    after_generation: string;
    /** The highest generation delivered so far (== anchor before page 1). */
    last_generation: string;
    /** Generations delivered so far (for the truthful-total check). */
    delivered: number;
}
/** Validate a listing request before it is sent (the request schema, D186). */
export declare function validateHeadsListingRequest(request: VaultHeadsListingRequest): void;
/**
 * Validate one page against the request and the sequence so far. Returns the
 * advanced progress. Refusals: anchor not echoed / wrong vault / coupling
 * violation / retired member → `VAULT_LISTING_PAGE_INVALID`; an entry at or
 * below the anchor → `GENERATION_REGRESSION`; a gap within or across pages →
 * `CHAIN_BROKEN`; an untruthful final `total` → `CHAIN_BROKEN`.
 */
export declare function verifyHeadsListingPage(page: VaultHeadsListingPage, request: VaultHeadsListingRequest, progress: VaultListingProgress, expectedRepoId?: string): VaultListingProgress;
/**
 * The continuation request for `page`, or `null` when the sequence is complete.
 * The anchor stays CONSTANT and `next_cursor` is echoed UNCHANGED — the cursor
 * is stored and echoed, never parsed or edited (a client that re-anchors or
 * edits a byte earns `INVALID_CURSOR` from the platform, D186).
 */
export declare function nextListingRequest(request: VaultHeadsListingRequest, page: VaultHeadsListingPage): VaultHeadsListingRequest | null;
/**
 * §6.4: the vault's newest generation may never fall BELOW the authenticated
 * pin. A listing (or a storage read) that says otherwise is a rollback, not a
 * quiet vault — `GENERATION_REGRESSION`, no publish.
 */
export declare function checkGenerationRegression(listedNewestGeneration: string, pinnedGeneration: string): void;
/** The parameters a maintenance open requests, and that the C1 record must echo. */
export interface VaultOpenBindingRecord {
    base_head_sha256: string;
    prior_checkpoint_claim_set_sha256: string | null;
    r2_cap_size_bytes: string;
}
/** `SHA-256("r402s/v0/open-binding" ‖ lp(client_open_id) ‖ lp(base_head) ‖ lp_opt(prior) ‖ lp(cap))`. */
export declare function openBindingDigest(clientOpenId: string, record: VaultOpenBindingRecord): string;
/**
 * Recompute the binding from the record's OWN fields and compare it bytewise
 * with the signed issuance digest. The error registry has no dedicated code for
 * the fence inequality (D145), so the client surfaces
 * `VAULT_OPEN_BINDING_MISMATCH`; a same-`client_open_id` retry that carries a
 * DIFFERENT binding is the registry's `CLIENT_OPEN_ID_CONFLICT`.
 */
export declare function checkOpenBinding(clientOpenId: string, record: VaultOpenBindingRecord, issuanceOpenBindingSha256: string, options?: {
    retry?: boolean;
}): void;
export interface VaultChainLinkInput {
    head: VaultHead;
    stored_bytes: Uint8Array;
    /** The listing's hash for this generation. */
    listed_sha256: string;
    expected_generation: string;
    prev_sha256: string;
    repo_id: string;
    /** The registered writer public key (V0: the genesis creator key). */
    writer_public_key: Uint8Array | string;
    writer_key_id: string;
    /**
     * The predecessor's own `epoch` (D194, rev 42) — `VAULT_GENESIS_EPOCH`
     * for generation 1's predecessor (genesis). Drives the D193 epoch-
     * continuity check below; pure and keyless (no envelope is ever opened
     * here — only the head's own `epoch` FIELD is checked).
     */
    prev_epoch: string;
}
/** Verify one link: bytes hash to the listing, strict parse, generation, prev linkage, epoch pin, repo, writer signature. */
export declare function checkChainLink(input: VaultChainLinkInput): void;
/**
 * The transition fail-closed rule: a V0 client that encounters an ADMITTED
 * transition kind it cannot validate stops advancing — read-only at the
 * materialized pin, no publish past it, `UPGRADE_REQUIRED`. Unknown kinds
 * are a parse reject.
 *
 * `rotate_epoch` is EXEMPT from this rule as of rev 42 (D193: "epoch
 * rotation is ACTIVATED") — `checkChainLink`'s own D194 epoch-continuity
 * check already validates its structural admissibility, and
 * {@link parseRotateEpochPayload} / the caller's own envelope-open step
 * (`Vault.verifyToNewest`) handle it fully. `add_writer_key` is
 * EXEMPT as of rev 47 (vault-multi-writer) the same way — {@link
 * parseAddWriterKeyPayload} + `validateAddWriterKeyPayload`
 * (`vault-writer-state.js`) handle it fully, INSIDE `verifyToNewest`'s
 * own loop, BEFORE this function ever runs on that head (so a transition
 * that fails writer validation never reaches here at all — this function
 * only ever sees ones that already passed). The other two kinds
 * (`add_envelope`, `transfer_binding`) remain genuinely unactivated and stay
 * fail-closed exactly as before.
 */
export declare function assertNoTransition(head: VaultHead): void;
/** One object to upload — identity fixed BEFORE the PUT; the receipt is compared against it. */
export interface VaultUploadObject {
    /**
     * Storage path relative to the vault root (§3). CLIENT-LOCAL addressing only:
     * the control plane derives the bucket key from `object_kind` + the ledger
     * identity and REFUSES a manifest entry carrying an unexpected member, so
     * this never rides the wire (5.6c).
     */
    path: string;
    object_kind: string;
    /** `null` for path-addressed kinds (envelopes) — those ride `epoch` + `recipient_fingerprint`. */
    object_id: string | null;
    bytes: Uint8Array;
    /** SHA-256 of `bytes` (ciphertext hash for frames, stored-bytes hash for plaintext kinds). */
    sha256: string;
    size_bytes: string;
    /** `wal_pack` only — §4.1: the ONLY receipt kind carrying `base_generation`. */
    base_generation?: string;
}
export interface VaultUploadReceipt {
    path: string;
    object_id: string | null;
    sha256: string;
    size_bytes: string;
}
/**
 * The control plane addresses stored objects by LEDGER IDENTITY, never by
 * bucket path: heads and admission records have their own generation-addressed
 * routes, and every uploadable kind is named by `object_kind` + `object_id`
 * (or, for envelopes, `epoch` + `recipient_fingerprint`). This resolver maps
 * the SDK's internal `vaultPaths` strings onto that identity so the rest of
 * the vault can keep addressing objects the way §3 describes them.
 */
export type VaultWireRef = {
    kind: "head";
    generation: string;
} | {
    kind: "admission";
    generation: string;
} | {
    kind: "object";
    read: VaultObjectReadRequest;
};
/** One entry of a `POST …/object-reads` batch. */
export interface VaultObjectReadRequest {
    object_kind: string;
    object_id?: string;
    epoch?: string;
    recipient_fingerprint?: string;
    /** D195, rev 42 — present only for a rotation-attempt `key_envelope` read. */
    rotation_id?: string;
    /** D197, rev 42 — present only for a `recipient_pin_manifest` read. */
    pin_manifest_version?: string;
}
/** `null` for a path with no wire identity (e.g. a locally-held cutoff ticket). */
export declare function vaultWireRefForPath(path: string): VaultWireRef | null;
/** The manifest entry for one upload — closed-key, exactly what the control plane validates. */
export declare function vaultManifestEntry(object: VaultUploadObject): VaultObjectReadRequest & {
    sha256: string;
    size_bytes: string;
    base_generation?: string;
};
/** Mirrors the gateway's `VAULT_INLINE_UPLOAD_MAX_OBJECT_BYTES` (`services/vault/upload-sessions.ts`). */
export declare const VAULT_INLINE_UPLOAD_MAX_OBJECT_BYTES = 262144;
/** Mirrors the gateway's `VAULT_INLINE_UPLOAD_MAX_REQUEST_BYTES`. */
export declare const VAULT_INLINE_UPLOAD_MAX_REQUEST_BYTES = 1048576;
/**
 * The client-side mirror of the gateway's `isInlineUploadRequest` + per-object/
 * per-request cap check: every object must fit under the PER-OBJECT cap AND
 * the batch's total under the PER-REQUEST cap, or the whole batch takes the
 * presigned session+PUT+finalize shape — no per-object mixing, matching the
 * server's `VALIDATION_FAILED` refusal on a mixed request. An empty batch is
 * never "inline" (nothing to send either way; `upload()`'s own early return
 * already short-circuits before this is consulted, and `putObject` always
 * wraps exactly one object so it never hits this branch).
 *
 * Takes the narrowest shape that satisfies every call site (`VaultUploadObject[]`
 * for `uploadObjects`, a single `{bytes}`-shaped array for `putObject`) so
 * neither caller needs to fabricate unrelated fields just to ask the
 * question.
 */
export declare function vaultInlineUploadEligible(objects: readonly {
    bytes: Uint8Array;
}[]): boolean;
/**
 * The stable key both sides agree on, used to pair receipts back to
 * requests — MIRRORS the gateway's `keyEnvelopeLedgerId`/`pinManifestLedgerId`
 * (services/vault/epoch-rotation.ts) exactly; drift here breaks receipt
 * pairing at upload finalize for a rotation-attempt envelope or a pin
 * manifest.
 */
export declare function vaultLedgerId(read: VaultObjectReadRequest): string;
export interface VaultAdmitHeadRequest {
    repo_id: string;
    generation: string;
    stored_bytes: Uint8Array;
    stored_bytes_sha256: string;
}
export type VaultAdmitHeadResult = {
    outcome: "admitted";
    admission_record_sha256: string;
    capture_receipt: VaultCaptureReceipt | null;
} | {
    outcome: "conflict";
    winner: {
        generation: string;
        stored_bytes_sha256: string;
    };
};
export interface VaultRetentionCutoffIssued {
    ticket: VaultRetentionCutoff;
    receipt: VaultRetentionCutoffReceipt;
}
/**
 * Everything the vault needs from the control plane + bucket. Extends the
 * creation transport so one implementation serves 5.3–5.6. All methods are
 * idempotent from the state machines' point of view.
 */
/** The `resource_binding` an upload session is charged against (§7.2 / §9.3). */
export type VaultResourceBinding = {
    kind: "ordinary_push";
} | {
    kind: "maintenance_cycle";
    maintenance_lease_id: string;
} | {
    kind: "repair_attempt";
    repair_attempt_id: string;
};
/** `POST …/maintenance-leases` — the owner's compact/prune reservation (§7.2). */
export interface VaultMaintenanceLeaseRequest {
    repo_id: string;
    base_head_sha256: string;
    current_checkpoint_hash?: string | null;
    r1_size_bytes: string;
    r2_cap_size_bytes: string;
    p_before_c1_size_bytes?: string;
    p_before_c2_size_bytes?: string;
}
export interface VaultMaintenanceLease {
    maintenance_lease_id: string;
    repo_id: string;
    base_head_sha256: string;
    current_checkpoint_hash: string | null;
    reservation_size_bytes: string;
    maintenance_headroom_bytes: string;
    /** Returned ONCE — the liveness instrument (heartbeat / release). Never logged, never cached. */
    holder_token: string;
    expires_at: string | null;
    hard_deadline_at: string | null;
}
/** `POST …/compaction-grant`'s result (vault-checkpoint-cadence design D3). */
export interface VaultCompactionGrant {
    /**
     * The vault's server-measured `source_bytes` at grant time, capped — never
     * client-declared. NOTE the byte fields arrive as STRINGS on the wire (the
     * gateway serializes Postgres BIGINTs as strings, verified live) — consumers
     * MUST `Number(...)` before arithmetic; `Number.isFinite` on the raw value
     * is false and silently discards the grant.
     */
    granted_bytes: number | string;
    expires_at: string;
    /** The org's pooled storage already in use, at grant time. */
    pool_used_bytes: number | string;
    /** The org's plain tier storage limit (unraised). */
    pool_limit_bytes: number | string;
    /** `pool_limit_bytes` + this grant's `granted_bytes` — the limit the preflight arithmetic should use while this grant is active. */
    effective_pool_limit_bytes: number | string;
}
export interface VaultTransport extends VaultCreationTransport {
    /**
     * Read N independent carrier objects (ref_state, retention_roots, WAL/
     * checkpoint packs — anything `object-reads`-addressed, never a
     * generation-addressed head/admission) in ONE presigned batch (vault-
     * client-round-trips design D2): one `object-reads` POST naming every
     * path, then the resulting GETs issued with bounded concurrency. Order in
     * the result array matches `paths`; a missing object is `null` at its
     * index, the same "absent" reading {@link VaultCreationTransport.getObject}
     * gives for one object. Callers that need exactly one object still use
     * {@link VaultCreationTransport.getObject} — this is for the plural
     * case only, so a single-object caller never pays a batch's overhead.
     *
     * `expected` (vault-small-object-inline design D3), when supplied, is
     * INDEX-ALIGNED with `paths`: `expected[i]`, if present, is the sha256 hex
     * the caller will itself check `paths[i]`'s bytes against. An
     * `object-reads`-backed implementation MAY use it to verify a
     * gateway-supplied `inline` reply before trusting it, falling back to
     * that slot's ordinary fetch on a mismatch — client-internal plumbing,
     * never a new verification obligation (every real caller already
     * hash-checks its bytes before use) and never required: an absent array,
     * or an absent element within it, is byte-identical to before that
     * change.
     */
    getObjects(request: {
        repo_id: string;
        paths: string[];
        expected?: Array<string | undefined>;
    }): Promise<Array<Uint8Array | null>>;
    /**
     * OPTIONAL per-object settlement over the SAME batch shape as
     * {@link getObjects} (vault-pipelined-restore D2): one presign POST,
     * the same bounded-concurrency GETs — counted ops identical — but the
     * result is one promise PER path, each settling when its own object's
     * bytes land, so a consumer can decrypt/verify/apply object i while later
     * objects are still downloading. Order and absence semantics match
     * `getObjects` (index-aligned; `null` for absent), including `expected`
     * (vault-small-object-inline design D3 — see {@link getObjects}'s doc
     * comment). Every returned promise is pre-marked handled, so an abandoned
     * tail after a mid-batch failure never surfaces as an unhandled
     * rejection. A transport without this method degrades to the
     * `getObjects` barrier — pipelining is a wall-clock property, never a
     * correctness dependency.
     */
    getObjectsSettled?(request: {
        repo_id: string;
        paths: string[];
        expected?: Array<string | undefined>;
    }): Promise<Array<Promise<Uint8Array | null>>>;
    /**
     * Read the EXACT stored bytes of many generation-addressed heads in ONE
     * POST (`…/head-reads`, vault-batched-head-reads).
     *
     * Heads are the one hot read that cannot ride {@link getObjects}: that
     * batch is carrier-only by wire design and fails closed on a
     * generation-addressed path, so a cold chain walk otherwise pays ~G/6
     * sequenced waves of full round trips for bytes that are ~1.2 KB each.
     *
     * `generations` must be STRICTLY ASCENDING; the route is all-or-nothing, so
     * the result is either one `Uint8Array` per requested generation in request
     * order, or `null` meaning UNSUPPORTED — an older gateway, a refusal, a
     * network fault, anything. `null` is never "absent bytes": it is the
     * caller's signal to fall back to per-generation reads, which produce the
     * per-item nulls and the real failure envelopes. Bytes are raw and
     * UNTRUSTED exactly as a per-generation read's are — this batch changes
     * transport, never trust.
     */
    getHeads(request: {
        repo_id: string;
        generations: string[];
    }): Promise<Uint8Array[] | null>;
    listHeads(request: VaultHeadsListingRequest & {
        repo_id: string;
    }): Promise<VaultHeadsListingPage>;
    /**
     * Session → create-only presigned PUTs (`If-None-Match: *`) → finalize;
     * receipts in request order.
     *
     * vault-byo-primary-bucket task 3.2 — `byo`, when present, marks this
     * vault as `storage_profile: "byo"` and names where the CLIENT itself
     * writes payload-kind objects directly (never through run402's own
     * bucket): the inline-upload fast path is skipped entirely (a BYO vault
     * refuses inline bytes-in-body — the transport shape itself would route
     * payload bytes through run402), every session object with `put: null`
     * is written by THIS caller straight to `byo.destination` with
     * `byo.credential` (resolved at use time, never transmitted), and
     * finalize carries the resulting per-object attestations. Omitted (the
     * default) is byte-identical to today.
     */
    uploadObjects(request: {
        repo_id: string;
        objects: VaultUploadObject[];
        resource_binding?: VaultResourceBinding;
        byo?: {
            destination: VaultMirrorDestination;
            credential?: VaultMirrorCredential;
        };
    }): Promise<VaultUploadReceipt[]>;
    admitHead(request: VaultAdmitHeadRequest): Promise<VaultAdmitHeadResult>;
    requestRetentionCutoff(request: {
        repo_id: string;
        base_head_sha256: string;
    }): Promise<VaultRetentionCutoffIssued>;
    exchangeActivationToken(request: {
        repo_id: string;
        operation_id: string;
        capture_receipt: VaultCaptureReceipt;
    }): Promise<VaultActivationToken>;
    submitOverrideCompletion(request: {
        repo_id: string;
        operation_id: string;
        capture_receipt: VaultCaptureReceipt;
    }): Promise<{
        cleared: boolean;
    }>;
    /** The vault record — policy, allocation generation, storage + maintenance state (§9.2). */
    getVaultRecord(request: {
        repo_id: string;
    }): Promise<VaultRecord>;
    /**
     * `GET …/state` (vault-composite-state-read design D1) — the pin-current
     * fast path: the vault record, the newest generation, its head's exact
     * stored bytes, and both carriers (`ref_state`/`retention_roots`) resolved
     * to raw bytes here (inline decoded, or fetched from the presigned URL —
     * both arms are indistinguishable to the caller after this returns, and
     * NEITHER is verified by this call). `head`/`carriers` are `null` together
     * for a freshly allocated vault with no admitted generation yet (mirrors
     * `newest_generation: null`); a carrier can independently be `null` when
     * its stored bytes are absent (the same "absent" reading {@link
     * VaultCreationTransport.getObject} gives for one object — the caller
     * treats it identically, never a route-level distinction).
     *
     * {@link Vault} verifies every field here EXACTLY as it does
     * walking the paginated listing — chain link from the caller's own pin,
     * carrier hashes against the head's own embedded receipts, and the writer
     * signature. This route bundles bytes; it never becomes the verifier. A
     * caller more than one generation behind its own pin ignores `head`/
     * `carriers` and falls back to {@link listHeads} — that decision lives in
     * the vault, never here.
     *
     * `since` (vault-delta-fetch): the caller's materialized generation.
     * A gateway that recognizes it MAY answer with a bounded `delta`
     * (intermediate heads + their WAL packs, inline under caps) — and MAY
     * ignore it entirely (older gateway, disqualified span); absence of
     * `delta` is never an error. Delta bytes are UNTRUSTED exactly like every
     * other stored byte: consumers hash-check before use, and a failed check
     * is a plain miss that the ordinary reads absorb.
     *
     * `restore` (vault-restore-recipe design D2), ORTHOGONAL to `since` —
     * declares restore intent. A gateway that recognizes it AND can locate a
     * checkpoint boundary within its own bound MAY answer with `restore_plan`
     * (the heads from that boundary to newest, the boundary checkpoint's
     * claim set + manifest, and every pack the span references) — and MAY
     * ignore it entirely (older gateway, no locatable boundary, a transition
     * inside the span); absence of `restore_plan` is never an error. Plan
     * bytes are UNTRUSTED exactly like `delta`'s: {@link Vault}
     * re-derives the full backward chain-link, claim-set signature,
     * cross-equality, per-pack receipt-hash, and AEAD-open obligations before
     * using anything here, and falls back to the ordinary backward walk on
     * any failure.
     */
    getState(request: {
        repo_id: string;
        since?: string;
        restore?: boolean;
    }): Promise<VaultState>;
    /** Resolve a project's vault without local state (the cold-restart entry point). */
    findVaultByProject(request: {
        project_id: string;
    }): Promise<VaultRecord>;
    /**
     * Resolve a vault by its address-form `org-slug/name` (repo-first-onramp
     * task 4.3, design D6) — `GET /vaults/v1?repo=<org-slug>/<name>`.
     * `RESOURCE_NOT_FOUND` for no such org OR no such name within it (the two
     * collapse deliberately, so slug-namespace probing learns nothing extra);
     * `SLUG_RELEASED` (with `successor_slug`/`released_at`/`cooldown_until` on
     * the error) while the slug is in its post-rename cooldown — never
     * auto-followed.
     */
    findVaultByRepo(request: {
        org_slug: string;
        repo_name: string;
    }): Promise<VaultRecord>;
    acquireMaintenanceLease(request: VaultMaintenanceLeaseRequest): Promise<VaultMaintenanceLease>;
    heartbeatMaintenanceLease(request: {
        repo_id: string;
        maintenance_lease_id: string;
        holder_token: string;
    }): Promise<{
        maintenance_lease_id: string;
        expires_at: string | null;
    }>;
    releaseMaintenanceLease(request: {
        repo_id: string;
        maintenance_lease_id: string;
        holder_token: string;
    }): Promise<{
        maintenance_lease_id: string;
        status: string;
    }>;
    /**
     * `POST …/compaction-grant` (vault-checkpoint-cadence design D3) — a
     * short-lived, TTL'd, at-most-one-per-project headroom grant that raises
     * the org's EFFECTIVE pooled storage limit by at most this vault's
     * server-measured `source_bytes`, so compaction's own transient ~2x
     * overshoot (the new checkpoint coexisting with the not-yet-pruned
     * history) can land without the routine manual override
     * (`--force-headroom`). Rejects `VAULT_COMPACTION_GRANT_ACTIVE` (409)
     * when this project already holds an active grant — the caller reads
     * that as "another compaction is already in flight for this vault" and
     * skips its own cycle rather than racing it. An older gateway 404s/
     * `ROUTE_NOT_FOUND`s; the caller falls back to compacting without a
     * grant, exactly as before this route existed.
     */
    openCompactionGrant(request: {
        repo_id: string;
    }): Promise<VaultCompactionGrant>;
    /**
     * `DELETE …/compaction-grant` — idempotent; `{closed: false}` when
     * nothing was active (already closed, already expired, or never
     * opened). Always safe to call best-effort in a `finally`: closing an
     * absent grant is a no-op, never an error.
     */
    closeCompactionGrant(request: {
        repo_id: string;
    }): Promise<{
        closed: boolean;
    }>;
    /**
     * `POST …/prune-intents` — the intent's EXACT BYTES (§7.3).
     *
     * The route is parsed with `express.raw` and the owner signature is verified
     * over the bytes as sent, so the transport MUST NOT re-serialize: it puts
     * `intent_bytes` on the wire verbatim under `Content-Type: application/json`.
     * An implementation that accepts a parsed object here and stringifies it is
     * signing one thing and sending another.
     */
    submitPruneIntent(request: {
        repo_id: string;
        intent_bytes: Uint8Array;
    }): Promise<VaultPruneIntentRecord & {
        stored: boolean;
    }>;
    /** `GET …/prune-intents/:id` — the intent's state and, once signed, its completion. */
    getPruneIntent(request: {
        repo_id: string;
        prune_intent_object_id: string;
    }): Promise<VaultPruneIntentRecord | null>;
    /**
     * The org's directory of envelope-capable principals (vault-human-
     * envelopes design D7, `GET /orgs/v1/:org_id/encryption-keys`) — every
     * active human member who has published an encryption key. Read by
     * {@link Vault.reconcileEnvelopeRecipients} to diff against a
     * vault's current recipient set.
     */
    listOrgEncryptionKeys(request: {
        org_id: string;
    }): Promise<VaultOrgEncryptionKeyDirectory>;
    /**
     * The `ek_` fingerprints already covering this vault, at any epoch
     * (`GET /vaults/v1/:vault_id/envelope-recipients`, task 2.2) —
     * fingerprints only, never envelope bytes; the recipient-only rule on
     * envelope BYTES elsewhere is unaffected.
     */
    listEnvelopeRecipients(request: {
        repo_id: string;
    }): Promise<VaultEnvelopeRecipientsResponse>;
    /**
     * `POST …/rotation-attempts` (D195) — the FIRST write of any rotation
     * attempt, BEFORE any `key_envelope` upload for it. `descriptor` is the
     * COMPLETE, signed `rotation_attempt_descriptor`; the gateway re-derives
     * `rotation_id` from the stored bytes and returns it alongside the
     * (possibly-idempotent-replayed) descriptor.
     */
    createRotationAttempt(request: {
        repo_id: string;
        descriptor: VaultRotationAttemptDescriptor;
    }): Promise<{
        rotation_id: string;
        descriptor: VaultRotationAttemptDescriptor;
        deduplicated: boolean;
    }>;
    /** `POST …/recipients/:principal_id/confirm` (D197) — first-seen pin confirmation; owner + step-up. */
    confirmRecipient(request: {
        repo_id: string;
        principal_id: string;
        new_fingerprint: string;
    }): Promise<VaultRecipientConfirmationReceipt>;
    /** `POST …/recipients/:principal_id/repin` (D197) — re-pin ceremony; owner + step-up. */
    repinRecipient(request: {
        repo_id: string;
        principal_id: string;
        old_ek_fingerprint: string;
        new_fingerprint: string;
    }): Promise<VaultRecipientConfirmationReceipt>;
    /** `POST …/recipients/:principal_id/key-revocation` (D199) — declares `reason:"recipient_key_revoked"` admissible; owner + step-up. Returns the D194 counters this rotation must be fenced against — the ONE reason value with a client-visible counter read. */
    declareRecipientKeyRevoked(request: {
        repo_id: string;
        principal_id: string;
    }): Promise<{
        recipient_state_version: string;
        recipient_revocation_version: string;
    }>;
    /** `POST …/epoch-secret-exposure` (D199) — declares `reason:"epoch_secret_exposed"` admissible, VAULT-scoped; owner + step-up. */
    declareEpochSecretExposed(request: {
        repo_id: string;
    }): Promise<{
        epoch_secret_exposure_version: string;
    }>;
    /** `POST …/writer-authority/declare-unavailable` (D202) — an explicit, audited fact that the writer signing key is gone; owner + step-up. */
    declareWriterAuthorityUnavailable(request: {
        repo_id: string;
    }): Promise<{
        declared_at: string;
        declared_by: string | null;
    }>;
    /**
     * `POST …/recipients/:principal_id/proof-of-open` (D210, rev 44, §9.2) —
     * submit fsck's OWN `chain_verified_to_generation` /
     * `decryptable_to_generation` evidence VERBATIM, plus the `ek_fingerprint`
     * of the envelope this identity actually opened. Self-match only: the
     * gateway requires `principal_id` to equal the AUTHENTICATED caller,
     * never overridable — a mismatch is the ordinary 403
     * `VAULT_ACCESS_DENIED`, not a `proof-of-open`-specific error.
     * Idempotent on `(repo_id, principal_id, ek_fingerprint,
     * decryptable_to_generation)`: `deduplicated: true` means the gateway
     * returned the tuple's EXISTING receipt (HTTP `200`) rather than minting
     * a fresh one (HTTP `201`) — both are the tuple's one committed winner,
     * never a partial/pretend re-insert.
     */
    submitOpenProof(request: {
        repo_id: string;
        principal_id: string;
        ek_fingerprint: string;
        chain_verified_to_generation: string;
        decryptable_to_generation: string;
        reader_entrypoint: string;
    }): Promise<{
        receipt: VaultOpenReceipt;
        deduplicated: boolean;
    }>;
}
/**
 * One row of the org's envelope-capable-principal directory
 * ({@link VaultTransport.listOrgEncryptionKeys}).
 *
 * The gateway route (`GET /orgs/v1/:org_id/encryption-keys`, `routes/org.ts`
 * in run402-private) returns `public_key` on every row — the raw key
 * material is what makes the directory usable for wrapping at all.
 * The field stays OPTIONAL in this wire type the
 * same way `desired[]` does on {@link VaultEnvelopeRecipientsResponse}:
 * a response is network data, and an older/rolling-deploy gateway that
 * omits the field must degrade to a per-entry `skipped` report
 * (`missing_public_key`) from {@link Vault.reconcileEnvelopeRecipients},
 * never a hardcoded assumption or a thrown error.
 */
export interface VaultOrgEncryptionKeyEntry {
    principal_id: string;
    display_name: string | null;
    ek_fingerprint: string;
    suite: string;
    created_at: string;
    /** Raw base64url X25519 public key. Present on every current-gateway row; tolerated as absent (per-entry `missing_public_key` skip) for wire robustness only. */
    public_key?: string;
    /**
     * vault-multi-writer (rev 47) D9 — the SAME row's vault-WRITER signing
     * half (`routes/org.ts` in run402-private, deployed alongside `public_key`
     * D9). Raw base64url Ed25519 public key; `null` when this principal has
     * never published a signing half (a pre-rev47 or encryption-only
     * enrollment), absent entirely on an older gateway that predates D9.
     */
    signing_pubkey?: string | null;
    signing_fingerprint?: string | null;
    signing_possession_verified_at?: string | null;
    [key: string]: unknown;
}
/** `GET /orgs/v1/:org_id/encryption-keys` — {@link VaultTransport.listOrgEncryptionKeys}'s result. */
export interface VaultOrgEncryptionKeyDirectory {
    org_id: string;
    keys: VaultOrgEncryptionKeyEntry[];
}
/**
 * One row of the vault's org-level, membership-driven DESIRED-recipient
 * state. `status` is the server's own honest accounting of what membership
 * currently wants, NOT a claim about whether access was actually revoked:
 * `"pending_removal"` means membership removed this principal but vault
 * protocol v0 has no epoch-rotation mechanism yet to un-wrap their existing
 * `key_envelope`, so `covered: true` on a `pending_removal` row is a REAL
 * continuing-access fact, not stale data.
 */
export interface VaultDesiredRecipientEntry {
    principal_id: string;
    display_name: string | null;
    status: "active" | "pending_removal";
    /** `null` when this principal is desired but has not published an encryption key yet. */
    ek_fingerprint: string | null;
    public_key: string | null;
    suite: string | null;
    /** `true` when `ek_fingerprint` currently has a `key_envelope` on this vault. */
    covered: boolean;
}
/** `GET /vaults/v1/:vault_id/envelope-recipients` — {@link VaultTransport.listEnvelopeRecipients}'s result. */
export interface VaultEnvelopeRecipientsResponse {
    vault_id: string;
    recipient_fingerprints: string[];
    /**
     * The org's desired-recipient state, cross-referenced against this
     * vault's coverage — see {@link VaultDesiredRecipientEntry}. OPTIONAL:
     * absent (not empty) on an older gateway that predates this field —
     * callers MUST distinguish "field absent" (older gateway, state genuinely
     * unknown) from "field present and empty" (org has no desired recipients)
     * rather than treating both as "no data."
     */
    desired?: VaultDesiredRecipientEntry[];
    /** The desired-recipient substrate's own monotonic version, for cheap client-side diffing. Present iff `desired` is present. */
    desired_state_version?: number;
    /**
     * D194's two org-level rotation counters, read (never locked) alongside
     * `desired` so a writer can drive the writer-capable
     * `reason:"member_removed"` rotation from the same read it partitions H
     * from ({@link Vault.rotateEpochForMemberRemoval}). OPTIONAL:
     * absent on a gateway that predates them.
     */
    recipient_state_version?: string;
    recipient_revocation_version?: string;
}
/** `GET /vaults/v1/:vault_id` — the shape `reads.ts:getVaultRecord` returns. */
export interface VaultRecord {
    repo_id: string;
    project_id: string;
    org_id: string;
    vault_policy: "required" | "grandfathered" | null;
    vault_policy_version: string;
    vault_policy_changed_at: string | null;
    allocation_generation: string;
    allocation_sha256: string | null;
    newest_generation: string | null;
    genesis_admitted_at: string | null;
    latest_effective_admitted_at: string | null;
    admitted_generations: string;
    gc_epoch: string;
    repair_version: string;
    repair_fence_state: string;
    /**
     * vault-byo-primary-bucket task 3.1/3.5 (protocol-v0.md rev 46 §9.2,
     * D220). Absent-or-`"managed"` is BYTE-IDENTICAL to every vault allocated
     * before this fold. `byo_destination` is the destination's ADDRESS ONLY
     * (never credential material) and is non-null iff `storage_profile ===
     * "byo"`. Chosen at allocation only in v1 — no route flips it on an
     * existing vault.
     */
    storage_profile?: "managed" | "byo";
    byo_destination?: string | null;
    storage: {
        source_bytes: string;
        open_session_reserved_bytes: string;
        objects: Record<string, string>;
    };
    maintenance: {
        lease: {
            maintenance_lease_id: string;
            base_head_sha256: string;
            reservation_size_bytes: string;
            expires_at: string | null;
            hard_deadline_at: string | null;
        } | null;
        open_cycle: {
            maintenance_cycle_id: string;
            state: string;
            last_cycle_progress_at: string | null;
        } | null;
        pending_repair_attempt_id: string | null;
    };
    /**
     * vault-multi-writer (rev 47) task 3.7 — the writer chain state's read
     * surface, mirrored client-side from the gateway's `VaultRecord` (see
     * `services/vault/reads.ts` in run402-private for the authoritative doc
     * comment). `writer_set` is the on-chain-recognized set exactly as the
     * chain itself would verify (regardless of any gateway-only block — that's
     * `ineligible_members`). `pending_writers` is the reverse direction:
     * active org members at role developer+ with a published,
     * possession-verified signing key who are NOT yet in `writer_set.writers`
     * — candidates for the "writer"-door `add_writer_key` (task 5.7's
     * `reconcile()`). `ineligible_members` is a chain-recognized active writer
     * the gateway has flagged for removal, still active on-chain pending the
     * next `rotate_epoch{writer_set_update}`.
     */
    writer_set?: {
        version: string;
        sha256: string | null;
        writers: VaultWriterSetEntry[];
    };
    pending_writers?: VaultPendingWriter[];
    ineligible_members?: VaultIneligibleMember[];
    /** `vault_vaults.read_only_terminal_at IS NOT NULL` — the D228 forced sole-writer-removal terminal state: no further writer-authenticated push is admissible. Absent (never `undefined`-checked as `true`) on an older gateway — treat as `false`. */
    read_only_terminal?: boolean;
    /**
     * vault-multi-writer (rev 47) D6/D227, task 5.9 — decimal-string
     * uint64 counter, bumped by the gateway every time a membership/role/key
     * change gateway-blocks one or more writer keys. A client building a
     * `writer_set_update` freezes THIS value into `rotation_attempt_
     * descriptor.writer_revocation_version` at its own admission fence —
     * there is no writer-side "declare" round-trip (unlike the encryption
     * side's `declareRecipientKeyRevoked`), so this read is the only source.
     * Absent on an older gateway that predates this field.
     */
    writer_revocation_version?: string;
    warnings: {
        kind: string;
        message: string;
    }[];
    created_at: string | null;
    /**
     * The control plane's SIGNED allocation record for the vault (present on
     * gateways that wrap it into the vault read; `null`/absent otherwise).
     * vault-agent-envelopes D4: a cold open compares genesis's creator
     * fingerprints against these — platform-attested consistency, never
     * independent authentication (the platform serves both sides).
     */
    allocation?: {
        creator_signing_fingerprint: string;
        creator_encryption_fingerprint: string;
        status?: string;
        service_key_id?: string;
        [key: string]: unknown;
    } | null;
}
/** One chain-recognized active writer on {@link VaultRecord.writer_set} — the on-chain shape only; `gateway_blocked_at` (gateway-only) rides {@link VaultIneligibleMember} instead. */
export interface VaultWriterSetEntry {
    writer_key_id: string;
    principal_id: string;
    authorization_kind: "writer" | "handoff";
    admitted_generation: string;
    admitted_head_sha256: string;
}
/** An org member eligible to become a writer (active membership at role developer+, a published possession-verified signing key) who is NOT yet in {@link VaultRecord.writer_set}'s `writers` — a candidate for the "writer"-door `add_writer_key` ({@link Vault.reconcileWriterAdmissions}). */
export interface VaultPendingWriter {
    principal_id: string;
    writer_key_id: string;
}
/** A chain-recognized active writer the gateway has flagged for removal — still active on-chain, pending the next `rotate_epoch{writer_set_update}`. */
export interface VaultIneligibleMember {
    principal_id: string;
    writer_key_id: string;
    gateway_blocked_at: string | null;
    reason: "membership_revoked" | "role_below_developer" | "encryption_key_revoked" | "gateway_blocked_pending_removal";
}
/**
 * {@link VaultTransport.getState}'s result — the SAME bytes the per-object
 * routes serve, resolved to raw `Uint8Array` here (never verified here; see
 * that method's own doc comment). `head`/`carriers` are `null` together for
 * a freshly allocated vault (mirrors `newest_generation: null`); a `carriers`
 * field can independently be `null` when that carrier's stored bytes are
 * absent from the backing store.
 */
export interface VaultState {
    vault: VaultRecord;
    newest_generation: string | null;
    head: {
        stored_bytes: Uint8Array;
        stored_bytes_sha256: string;
    } | null;
    carriers: {
        ref_state: Uint8Array | null;
        retention_roots: Uint8Array | null;
    } | null;
    /** Present only when the gateway answered a `since` request with a qualifying span (vault-delta-fetch); absent otherwise, including on every older gateway. */
    delta?: VaultStateDelta | null;
    /** Present only when the caller declared `restore: true` AND the gateway located a qualifying checkpoint boundary (vault-restore-recipe); absent otherwise, including on every older gateway. */
    restore_plan?: VaultRestorePlan | null;
}
/**
 * The state read's restore recipe (vault-restore-recipe design D1-D5):
 * the heads from the newest checkpoint boundary through the caller's own
 * newest generation (ascending, boundary-first — ONE entry when the
 * boundary IS the newest head), that boundary's checkpoint claim set +
 * manifest (`null` exactly when `boundary_generation` is the genesis
 * sentinel — no checkpoint exists in the whole bounded chain), and every
 * checkpoint + WAL pack the span references. Heads and checkpoint bytes are
 * pure base64url decodes (the gateway sends them inline ALWAYS — design
 * D4); `packs[].bytes` is ALREADY resolved here exactly as `object-reads`
 * resolves its own entries (db8d745c's `resolveObjectReadTarget` — a
 * self-consistency-gated `inline` decode, falling back to the entry's own
 * `url`/`edge_url` on a lying, absent, or over-cap `inline`), `null` when
 * neither arm produced bytes (an absent object, a failed GET). UNTRUSTED
 * THROUGHOUT: nothing here is verified by this decode — {@link
 * Vault} re-derives the full chain-link/signature/cross-equality/
 * receipt-hash/AEAD-open obligations before consuming anything, matching
 * one for one by `(object_kind, object_id)`, and falls back to the ordinary
 * backward walk byte-identically on any failure.
 */
export interface VaultRestorePlan {
    boundary_generation: string;
    heads: Array<{
        generation: string;
        stored_bytes: Uint8Array;
        stored_bytes_sha256: string;
    }>;
    checkpoint: {
        claim_set: {
            object_id: string;
            stored_bytes: Uint8Array;
        };
        manifest: {
            object_id: string;
            stored_bytes: Uint8Array;
        };
    } | null;
    packs: Array<{
        object_kind: string;
        object_id: string;
        bytes: Uint8Array | null;
    }>;
}
/**
 * The state read's bounded delta (vault-delta-fetch): the span's heads in
 * chain order plus their WAL packs' INLINE bytes. Over-cap packs arrive as
 * presigned references on the wire and are DROPPED here (v1 consumes inline
 * only — an uncovered pack simply rides the ordinary batched fetch, so the
 * reference arm costs nothing to ignore). Untrusted throughout: every
 * consumer hash-checks before use.
 */
export interface VaultStateDelta {
    heads: Array<{
        generation: string;
        stored_bytes: Uint8Array;
        stored_bytes_sha256: string;
    }>;
    packs: Array<{
        object_id: string;
        bytes: Uint8Array;
    }>;
}
export interface VaultHttpTransportOptions {
    /** Wire shape: every vault-scoped route is `/vaults/v1/:vault_id/...`; `vault_id` is the `repo_id` unless a mapping is supplied (D185). */
    vaultIdFor?: (repoId: string) => string;
    /**
     * vault-byo-primary-bucket: where THIS machine reads a BYO vault's
     * payload from. The gateway holds no payload copy of a `storage_profile:
     * "byo"` vault, so its `object-reads` entries and `GET …/state` carriers
     * for the payload kinds carry `byo_key` (the object's relative key under
     * the vault's `byo_destination`) instead of a presign; the transport then
     * asks this resolver for the vault's locally configured destination
     * backend (`byo/<repo_id>.json` in the keystore — destination + credential
     * NAME, never material) and reads the key from it. `null` means this
     * machine has no such config, and the read fails
     * `VAULT_BYO_NOT_CONFIGURED` by name — never a silent "absent". Absent
     * option: same as `null`. Managed vaults never consult it.
     */
    byoBackend?: (repoId: string) => VaultMirrorBackend | null;
    /**
     * vault-object-host-predial (design D1, task 1.2): called with the
     * ORIGIN(s) (`scheme://host`) of an object-store URL this transport just
     * completed a round trip against — the presigned `url`'s origin, and the
     * `edge_url`'s origin too when the target carried one, regardless of
     * which one actually served the bytes (both are worth a future predial;
     * see `vault-prewarm.ts`). Fired ONLY after a completed fetch (any
     * status, including 404 — a real "absent" response still proves the
     * origin is reachable); NEVER fired for an `inline`-satisfied read
     * (nothing was dialed) or a fetch that threw. This is the ONE place the
     * transport observes origins — the caller (who holds the keystore) is
     * expected to persist them via `VaultKeystore.recordObjectStoreOrigins`;
     * the transport itself has no keystore and does no persistence. Called
     * synchronously and never awaited — a throwing callback must never
     * surface into the read it rode along with, so callers wrap their own
     * persistence in their own try/catch (this transport does not).
     */
    onObjectStoreOriginObserved?: (repoId: string, origins: string[]) => void;
}
/**
 * Independent reads/PUTs within one vault step run at this concurrency
 * (design D2) — "browser-era origin etiquette", well within what S3/the
 * gateway tolerate, and it keeps in-flight memory bounded by ~6×frame size.
 * Chain-ordered steps (the head-chain walk, admission, readback) never call
 * this — they stay strictly sequential.
 */
export declare const VAULT_TRANSPORT_CONCURRENCY = 6;
/**
 * Run `fn` over `items` with at most `limit` in flight at once, preserving
 * result order regardless of completion order. A plain worker-pool: `limit`
 * workers each pull the next unclaimed index until the queue is empty.
 */
export declare function mapBounded<T, R>(items: readonly T[], limit: number, fn: (item: T, index: number) => Promise<R>): Promise<R[]>;
/**
 * The `fetch`-backed transport over the SDK kernel. Presigned PUTs carry
 * `If-None-Match: *` (create-only — the bucket policy demands it) and the
 * FULL_OBJECT SHA-256 checksum header.
 */
export declare function createVaultHttpTransport(client: Client, options?: VaultHttpTransportOptions): VaultTransport;
export declare const vaultPaths: {
    readonly head: (generation: string) => string;
    readonly admission: (generation: string) => string;
    readonly wal: (id: string) => string;
    readonly refState: (id: string) => string;
    readonly retentionRoots: (id: string) => string;
    readonly checkpointManifest: (id: string) => string;
    readonly checkpointPack: (id: string) => string;
    readonly claimSet: (id: string) => string;
    readonly cutoffTicket: (id: string) => string;
    /** `verifier-receipts/<vr>.json` — plaintext-structured, uploaded before a prune intent may reference it (§7.3). */
    readonly verifierReceipt: (id: string) => string;
    /**
     * `envelopes/<epoch>/<recipient_fingerprint>` — mirrors
     * `vault-creation-journal.ts`'s private `envelopePath` (the genesis
     * creator's envelope); this is the same addressing for every OTHER
     * recipient's `key_envelope`. `rotationId` present (D195, rev 42) widens
     * this to `envelopes/<epoch>/<rotation_id>/<recipient_fingerprint>` — a
     * rotation-attempt envelope's own path (protocol §1: `rotation_id`
     * ABSENT, never explicit null, in the genesis/ADD-workaround case).
     */
    readonly envelope: (epoch: string, recipientFingerprint: string, rotationId?: string | null) => string;
    /** `recipient-pins/<pin_manifest_version>.json` (D197, rev 42) — version-addressed, plaintext-structured, writer-signed. */
    readonly pinManifest: (pinManifestVersion: string) => string;
};
export interface VaultRestoreMarker {
    generation: string;
    head_sha256: string;
}
/** Read this target directory's `restored_through` marker, or `null` when nothing was ever restored into it. */
export declare function readVaultRestoreMarker(targetRepoDir: string): Promise<VaultRestoreMarker | null>;
/** Default `auto_gc_generations` — see the change proposal's rationale (≈2-3s over the fresh-checkpoint floor at this backlog, a busy repo compacts roughly once per few dozen pushes). */
export declare const VAULT_AUTO_GC_GENERATIONS_DEFAULT = 32;
/**
 * Read this checkout's auto-gc threshold, or the default when unset or
 * unparseable. Never throws — a corrupt local config value degrades to the
 * default rather than blocking a push's own auto-gc check.
 */
export declare function readVaultAutoGcThreshold(targetRepoDir: string): Promise<number>;
/** Set this checkout's auto-gc threshold. `0` disables auto-gc entirely. */
export declare function writeVaultAutoGcThreshold(targetRepoDir: string, generations: number): Promise<void>;
/**
 * {@link Vault.ensureRepoState}'s report when a cold keystore was
 * restored from its own envelope (vault-agent-envelopes D4). `trust` is
 * `receipt` only when a creator-held recovery receipt pinned genesis;
 * `platform_attested` means the control plane's signed allocation matched —
 * consistency the platform itself vouches for, so `independently_verified`
 * is `false` there and this label must never be read as end-to-end
 * authentication. `continuity` is `pinned` when this keystore had already
 * seen this genesis (a later open), `first_seen` on the first.
 */
export interface VaultColdOpenResult {
    repo_id: string;
    org_id: string;
    provenance: "restored_from_envelope";
    trust: "receipt" | "platform_attested" | "unauthenticated_salvage";
    continuity: "first_seen" | "pinned";
    independently_verified: boolean;
    epoch: string;
    recipient_fingerprint: string;
}
export interface VaultOptions {
    keystore: VaultKeystore;
    transport: VaultTransport;
    repo_id: string;
    /** The local git repository (objects for pack building / ancestry); optional for read-only use. */
    repo_dir?: string;
    now?: () => Date;
    /** Verification budget per call (resumable — the verified prefix persists). */
    verification_budget?: number;
    conflict_retries?: number;
    /** The signing service key resolved through the registry — when supplied, control-plane-signed messages (cutoff tickets) are signature-checked. */
    service_public_key?: Uint8Array | string;
    /**
     * Consult round 2 §5: an ordinary open REFUSES a cold restore whose genesis
     * the control plane cannot attest (no signed allocation on the vault
     * record) — a data-path that cannot forge the allocation could otherwise
     * simply omit it and hand the client a fabricated vault. Only the explicit
     * recovery flow sets this, and the result still says
     * `unauthenticated_salvage`.
     */
    allow_unauthenticated_salvage?: boolean;
}
/** How a checkpoint-bearing head binds a `retention_cutoff` ticket and expires roots (§4.5a / §4.5). */
export interface VaultCutoffOptions {
    /**
     * `effective_admitted_at` for a root's drop generation, or `null` when this
     * client cannot resolve it — a `null` RETAINS the root (expiry is permissive).
     * `effective_admitted_at = max(prepared_at, the admission record's storage
     * creation time)`; deriving it from `prepared_at` alone shortens the lane.
     */
    effectiveAdmittedAt?: (droppedAtGeneration: string) => string | null;
}
/** One admitted `rotate_epoch` transition {@link Vault.verifyToNewest} walked over, KEYLESS (structural only — no envelope opened yet). */
export interface VaultEncounteredRotation {
    /** The rotate_epoch head's own generation. */
    generation: string;
    /** == `payload.new_epoch` == the head's own `epoch`. */
    epoch: string;
    payload: VaultRotateEpochPayload;
    /**
     * vault-multi-writer rev 47 — the writer who actually signed THIS
     * rotation head (already resolved + signature-verified by the forward
     * chain walk at the point this was collected — never the vault's fixed
     * genesis creator in a multi-writer vault). Optional: the BACKWARD
     * catch-up walk in `verifyToNewest` (decrypt-lag recovery) does not yet
     * resolve this for a rotation it discovers walking `prev_sha256`
     * backward from `lastHead` — a documented, narrower remaining gap
     * distinct from the forward walk's own full fix; `openEpochRotationForRecipient`
     * falls back to the vault's genesis-creator key when absent, byte-identical
     * to this field's pre-rev-47 non-existence.
     */
    signing_pubkey?: string;
}
/** Named detail for a reader's own `VAULT_EPOCH_NOT_OPENABLE` / decrypt stop point (Part C: `repos fsck`'s honest `chain_verified_to` vs `decryptable_to` split). */
export interface VaultEpochDecryptFailure {
    generation: string;
    epoch: string;
    rotation_id: string | null;
    code: string;
    message: string;
}
/**
 * The result of an OPT-IN decrypt-validation pass over the walked chain
 * (`verifyToNewest({decryptValidate: true})`) — never present on an ordinary
 * keyless chain verify. `decryptable_to_generation` is the newest generation
 * whose own `ref_state`/`retention_roots` this call actually decrypted
 * (opening every `rotate_epoch` envelope needed along the way); it EQUALS
 * the outer state's `generation` on success and can fall short of it in
 * `strict: false` (tolerant) mode, in which case `failure` names exactly
 * where and why (never a bare, undifferentiated AEAD failure).
 */
export interface VaultDecryptValidationResult {
    decryptable_to_generation: string;
    /** The newest successfully-decrypted `ref_state`/`retention_roots`, or `null` if none this call reached (generation zero, or the very first head failed). */
    ref_state: VaultRefState | null;
    retention_roots: VaultRetentionRoots | null;
    /** Every locally-known epoch key at the end of this call, hex-encoded — seeded from the keystore's persisted `epoch_keys` plus every rotation this call itself opened. */
    epoch_keys_hex: Record<string, string>;
    failure: VaultEpochDecryptFailure | null;
}
export interface VaultVerifiedState {
    generation: string;
    head_sha256: string;
    /** `null` at generation zero. */
    head: VaultHead | null;
    genesis: VaultGenesis;
    /** Every admitted `rotate_epoch` transition walked THIS call, oldest first — keyless (Part A's structural half; D202's join predicate needs only this). */
    rotations: VaultEncounteredRotation[];
    /** Present iff `decryptValidate` was requested. */
    decrypt: VaultDecryptValidationResult | null;
}
export interface VaultMaterializedState extends VaultVerifiedState {
    ref_state: VaultRefState | null;
    retention_roots: VaultRetentionRoots | null;
    refs: VaultRefMap;
    roots: VaultRetentionRoot[];
    head_target: VaultHeadTarget;
    /** Every locally-known epoch key after this call, hex-encoded, keyed by epoch — `restoreObjectsInto` needs every epoch spanned by the covered generations, not just the newest. */
    epoch_keys_hex: Record<string, string>;
}
/** One directory entry {@link Vault.reconcileEnvelopeRecipients} wrapped a fresh `key_envelope` for. */
export interface VaultReconcileEnvelopeRecipientsWrapped {
    principal_id: string;
    ek_fingerprint: string;
}
/** Why {@link Vault.reconcileEnvelopeRecipients} did NOT wrap a directory entry it otherwise would have. */
export type VaultReconcileEnvelopeRecipientsSkipReason = "missing_public_key" | "invalid_public_key" | "pinned_key_mismatch";
export interface VaultReconcileEnvelopeRecipientsSkipped {
    principal_id: string;
    ek_fingerprint: string;
    reason: VaultReconcileEnvelopeRecipientsSkipReason;
    /** `pinned_key_mismatch`: `{pinned_fingerprint, directory_fingerprint}`. `invalid_public_key` (derivation mismatch): `{derived_fingerprint}`. Absent for `missing_public_key` and a bad-encoding `invalid_public_key`. */
    details?: Record<string, unknown>;
}
/** {@link Vault.reconcileEnvelopeRecipients}'s full per-recipient breakdown. */
export interface VaultReconcileEnvelopeRecipientsResult {
    repo_id: string;
    org_id: string;
    /** The epoch every wrap in this call used (V0: always {@link VAULT_GENESIS_EPOCH}). */
    epoch: string;
    /** Directory entries this call itself wrapped a NEW `key_envelope` for. */
    wrapped: VaultReconcileEnvelopeRecipientsWrapped[];
    /** `ek_` fingerprints already covering the vault before (or, for a raced wrap, as of) this call — no action taken. */
    already_covered: string[];
    /** Directory entries this call could not (or, for `pinned_key_mismatch`, would not) wrap. */
    skipped: VaultReconcileEnvelopeRecipientsSkipped[];
}
/** One `pending_writers[]` entry {@link Vault.reconcileWriterAdmissions} admitted via a fresh `add_writer_key{"writer"}` head. */
export interface VaultReconcileWriterAdmissionsAdmitted {
    principal_id: string;
    writer_key_id: string;
    generation: string;
}
/** Why {@link Vault.reconcileWriterAdmissions} did NOT admit a `pending_writers[]` entry it otherwise would have. */
export type VaultReconcileWriterAdmissionsSkipReason = "missing_signing_pubkey" | "invalid_signing_pubkey";
export interface VaultReconcileWriterAdmissionsSkipped {
    principal_id: string;
    writer_key_id: string;
    reason: VaultReconcileWriterAdmissionsSkipReason;
}
/**
 * {@link Vault.reconcileWriterAdmissions}'s full per-candidate
 * breakdown (task 5.7). Structurally parallel to
 * {@link VaultReconcileEnvelopeRecipientsResult} (same repo/wrapped-or-
 * admitted/already-covered/skipped shape), but this reconcile submits a REAL
 * chain head per admission (one `add_writer_key{"writer"}` transition per
 * candidate — the protocol allows only one added writer per head) rather
 * than an out-of-band object upload, so it is never fully parallel: each
 * admission materializes fresh against the PRIOR admission's own updated
 * writer set.
 */
export interface VaultReconcileWriterAdmissionsResult {
    repo_id: string;
    org_id: string;
    /**
     * `false` exactly when THIS session's own key is not (or is no longer) an
     * active writer — the "writer" door's authorization is the carrying
     * head's own signer, so an ineligible session cannot admit anyone, and
     * this call returns immediately with every other field empty. Distinct
     * from a genuinely empty `pending_writers[]` (still `eligible: true`,
     * there was simply nothing to do) — otherwise both cases produce the
     * SAME all-empty shape and a caller could not tell "nothing needed
     * doing" from "I have no authority to do anything here."
     */
    eligible: boolean;
    /** `pending_writers[]` entries this call admitted this call, each via its own head. */
    admitted: VaultReconcileWriterAdmissionsAdmitted[];
    /** `writer_key_id`s already active before (or, for a raced admission, as of) this call — no action taken. */
    already_covered: string[];
    /** `pending_writers[]` entries this call could not admit. */
    skipped: VaultReconcileWriterAdmissionsSkipped[];
}
export interface VaultPushOptions {
    transaction: VaultRefTransaction;
    /** New `HEAD` target for the published ref_state; carried forward when omitted. */
    head_target?: VaultHeadTarget;
    protocol_refs?: "refuse" | "allow";
    /** Force the checkpoint-bearing form (purpose `ordinary_push`) regardless of delta size. */
    checkpoint?: boolean;
    /** Bind a fresh `retention_cutoff` ticket so expired roots may leave the map (checkpoint-bearing heads only). */
    cutoff?: VaultCutoffOptions;
    /** Built lazily at head-sign time — the deploy lane may still be computing the plan digest. */
    capture_binding?: VaultCaptureBinding | (() => Promise<VaultCaptureBinding | null> | VaultCaptureBinding | null);
    /**
     * A caller-supplied base (vault-client-round-trips design D1) — used
     * VERBATIM for the first attempt instead of calling {@link Vault.materialize}
     * again. Only meaningful for a base the caller JUST materialized from this
     * same vault instance (e.g. one `list → push` protocol exchange sharing
     * one snapshot for both `expected_old` derivation and the push itself) —
     * supplying a stale base is always SAFE (admission is CAS on generation;
     * a stale base only makes a conflict more likely, never an incorrect
     * admission), just not the round-trip win. A conflict retry re-materializes
     * from storage exactly as when no base is supplied at all.
     */
    base?: VaultMaterializedState;
}
export interface VaultPublishResult {
    generation: string;
    head_sha256: string;
    head: VaultHead;
    admission_record_sha256: string;
    capture_receipt: VaultCaptureReceipt | null;
    /** `wal` = direct WAL receipts; `checkpoint` = the delta shipped as a checkpoint set. */
    form: "wal" | "checkpoint";
    conflicts_retried: number;
    refs: VaultRefMap;
    /**
     * vault-clone-scaling (P3): generations-since-checkpoint against the
     * coverage this checkout has locally learned (`{0, false}` when unknown
     * — see the keystore field's doc). Advisory data only; consumers echo,
     * never gate.
     */
    checkpoint_staleness: VaultCheckpointStaleness;
}
/** {@link Vault.rotateEpoch}'s result (D193-D203, rev 42). */
export interface VaultRotationResult {
    outcome: "admitted";
    generation: string;
    head_sha256: string;
    new_epoch: string;
    rotation_id: string;
    reason: VaultRotationReason;
    included: {
        principal_id: string;
        ek_fingerprint: string;
    }[];
    excluded_keyless_principal_ids: string[];
    excluded_unconfirmed_principal_ids: string[];
    admission_record_sha256: string;
    capture_receipt: VaultCaptureReceipt | null;
    /**
     * `"passed"` — this principal is itself an included recipient and its own
     * new-epoch envelope opened (via {@link import("../namespaces/vault.crypto.js").openEpochRotationForRecipient},
     * the real reader entry point) to reproduce the sealed `K_e` +
     * `epoch_key_commitment` — the SAME round-trip that fed
     * `payload.self_open_attestation` (D209), run BEFORE this head was ever
     * submitted; a failure THROWS rather than returning here — there is no
     * `"failed"` value, and the head is never even built when it happens.
     * `"not_a_recipient"` — this principal (the vault's writer) is not itself
     * in `included` (e.g. it was excluded, or holds no local encryption key)
     * — there is nothing for this machine to self-check, and
     * `payload.self_open_attestation.outcome` is `"writer_not_recipient"`.
     * This is NOT a confidentiality gap: the writer sampled `kE` itself (it
     * never needs a `key_envelope` to learn its own secret) and
     * `keystore.recordEpochRotation` below advances the LOCAL pointer
     * unconditionally once admission succeeds, regardless of `self_check` —
     * an agent/CI writer that is deliberately never a directory envelope
     * recipient (design D1 of `services/vault/desired-recipients.ts`:
     * "agents hold their own vault keys in their CLI keystore") keeps
     * read/write access to its own vault through every rotation it itself
     * drives, with or without an envelope.
     */
    self_check: "passed" | "not_a_recipient";
    /**
     * Present iff `options.pending_confirmations` was non-empty and its
     * receipted entries were folded into THIS SAME head's `pin_manifest`
     * field (D197 conservation; schema-legal per §4.3 — `transition` and
     * `pin_manifest` are independent optional fields on one `head`). `null`
     * when no fold was requested.
     *
     * **D196 boundary, load-bearing:** folding does NOT make these principals
     * `included` in THIS rotation's envelope set — "for a non-genesis
     * rotation, `confirmed(h)` reads the NEAREST PREDECESSOR admitted pin
     * manifest — a manifest update riding the SAME head never self-authorizes
     * its own recipients" (protocol-v0.md D196). They land in
     * `excluded_unconfirmed_principal_ids` for THIS rotation exactly as they
     * would without folding. What folding buys: the manifest becomes DURABLY
     * ADMITTED in the same atomic submission that is itself EXEMPT from
     * `EPOCH_ROTATION_REQUIRED` (a `rotate_epoch` admission is the gate's own
     * escape valve — it must be, or the flag it clears could never clear) —
     * closing the standalone `publishPinManifestUpdate` deadlock where the
     * manifest-only push is ITSELF an ordinary admission and therefore
     * ITSELF gated while migration/revocation/exposure is outstanding. The
     * newly-published principals become eligible starting from the NEXT
     * rotation (or the next ordinary push, once the flag clears).
     */
    pin_manifest_published: {
        pin_manifest_version: string;
        stored_bytes_sha256: string;
        principal_ids: string[];
    } | null;
    /**
     * vault-multi-writer (task 5.9, D6/D227) — writer keys this SAME head
     * removed via a folded `writer_set_update`, each with the reason it was
     * removed for. Empty (never omitted) when nothing needed removing — the
     * common case, matching `excluded_keyless_principal_ids`' own
     * always-present-possibly-empty shape rather than `pin_manifest_published`'s
     * `| null`.
     */
    writers_removed: {
        writer_key_id: string;
        principal_id: string;
        reason: "member_removed" | "writer_key_revoked" | "epoch_secret_exposed";
    }[];
}
/**
 * What {@link Vault.planPush} reports — a REAL
 * local computation, not an estimate: every number here comes from actually
 * building the packs (or checkpoint set) and actually sealing/encrypting
 * them, exactly as a real `push` would. `would_admit_generation` is what this
 * push would claim AT THE OBSERVED BASE — a concurrent publisher can still
 * win the race before a real push runs (see the method doc for why that is
 * not a defect: `git push --dry-run` carries the identical caveat).
 */
export interface VaultPushPlan {
    /** The base this plan was computed against — `materialize()`'s generation at call time. */
    base_generation: string;
    /** The generation a real push would claim, computed the same way a real push computes it (`nextGeneration(base_generation)`). */
    would_admit_generation: string;
    /** `would_admit_generation` as a plain decimal string — the hex generation is a wire format, not a human one. */
    would_admit_generation_decimal: string;
    /** `wal` = direct WAL receipts; `checkpoint` = the delta would ship as a checkpoint set — the SAME threshold real `push` uses. */
    form: "wal" | "checkpoint";
    /** The ref map this push would publish (after evaluating the transaction against the base). */
    refs: VaultRefMap;
    head_target: VaultHeadTarget;
    /** Every object that would be uploaded — `ref_state`, `retention_roots`, and the WAL/checkpoint pack(s) — with their REAL sealed (encrypted) sizes. */
    objects: Array<{
        object_kind: VaultUploadObject["object_kind"];
        size_bytes: string;
    }>;
    object_count: number;
    /** Sum of `objects[].size_bytes` — the REAL ciphertext byte count a real push would upload. */
    encrypted_bytes: string;
    /** Sum of the plaintext pack bytes BEFORE sealing — what "objects that would publish" weigh on the wire before encryption overhead. */
    raw_bytes: string;
}
/**
 * What {@link Vault.verifyStoredCheckpoint} observed. Every boolean is
 * a FINDING, not a promise: a `false` here is what makes a truthful negative
 * `verifier_receipt` possible.
 */
export interface VaultStoredCheckpointAttestation {
    checkpoint_head_sha256: string;
    checkpoint_generation: string;
    claim_set_sha256: string;
    /** `null` in the no-removal checkpoint form — a prune needs one, so that is a refusal upstream. */
    cutoff_ticket_sha256: string | null;
    cutoff_at: string | null;
    covered_tips: string[];
    /** Covered tips that did NOT resolve from the restored set. Non-empty ⇒ the checkpoint does not verify. */
    missing_tips: string[];
    restored_object_set_hmac: string;
    object_set_matches: boolean;
    ref_state_matches: boolean;
    retention_roots_matches: boolean;
    /** The `rootset` commitment over this generation's roots carrier — the intent core's `retention_state_hmac`. */
    retention_state_hmac: string;
}
export interface VaultBuiltCheckpoint {
    manifest: VaultCheckpointManifest;
    claim_set: VaultCheckpointClaimSet;
    claim_set_receipt: VaultCheckpointBlock["claim_set"];
    objects: VaultUploadObject[];
    /** Plaintext packs in order (for the acceptance self-check). */
    packs: Uint8Array[];
    covered_tips: string[];
}
/** A transport-agnostic view of git ops the publication needs (the local repository). */
export declare class Vault {
    #private;
    readonly keystore: VaultKeystore;
    readonly transport: VaultTransport;
    readonly repoId: string;
    readonly repoDir: string | null;
    private readonly now;
    private readonly budget;
    /**
     * vault-clone-scaling (bench P2): the CURRENT walk window's prefetched
     * bytes — head bytes (bounded-concurrent direct reads) and carrier frames
     * (one batched getObjects), keyed by storage path — filled by
     * `verifyToNewest`'s per-page prefetch and `restoreObjectsInto`'s
     * backward-window prefetch, consulted by {@link readCachedHeadBytes} /
     * {@link openMaterializeCarriers} before they pay a network read. Transient (REPLACED each page, so memory is
     * bounded by one listing page) and UNTRUSTED: every consumer sha-checks
     * an entry against the exact value it would check network bytes against
     * (the listing's `stored_bytes_sha256`, a receipt's `ciphertext_sha256`),
     * the same discipline as the keystore object cache — a wrong or stale
     * entry is a MISS, never a verification bypass. Deliberately NOT the
     * keystore cache: that cache's eviction window is a handful of newest
     * generations by design, so routing a whole page through it would evict
     * the very bytes the ordered walk is about to read.
     */
    private walkPrefetch;
    /**
     * WAL pack bytes carried by the state read's delta (vault-delta-fetch),
     * keyed by object_id — the walkPrefetch discipline exactly: UNTRUSTED
     * until a consumer's own hash check passes, a mismatch is a plain miss,
     * and the buffer is transient (stashed by {@link tryStateFastPath},
     * consumed and cleared by the next restore).
     */
    private stateDeltaPacks;
    /**
     * The state read's restore plan (vault-restore-recipe design D1-D5),
     * stashed RAW — never verified at stash time, unlike `stateDeltaPacks`
     * (whose heads self-check before entering the shared keystore cache): a
     * plan's heads/checkpoint/packs are only ever verified by {@link
     * restoreObjectsInto}'s own full obligation set, and a partially-checked
     * plan sitting here would be a foot-gun for a future caller that forgot
     * the difference. Consumed and cleared exactly once, by the next
     * `restoreObjectsInto` call (either its own materialize, or a dedicated
     * plan-only read it issues when the incremental walk it started aborts).
     */
    private stateRestorePlan;
    private readonly retries;
    private readonly servicePublicKey;
    private readonly allowUnauthenticatedSalvage;
    private genesisCache;
    /**
     * vault-byo-primary-bucket task 3.2 — this vault's resolved BYO write
     * target, cached after the first resolution (storage_profile/
     * byo_destination are immutable-at-allocation in v1, so a single
     * read-once-per-instance is safe). `undefined` = not yet resolved;
     * `null` = this machine has no LOCAL BYO write config for this repo
     * (either an ordinary managed vault, or a BYO vault this machine has not
     * been configured to write — see {@link resolveByoWriteTarget}).
     */
    private byoResolution;
    constructor(options: VaultOptions);
    static open(options: VaultOptions): Vault;
    repoFile(): VaultRepoFile;
    /**
     * vault-agent-envelopes D4 — the COLD OPEN. A keystore that holds an
     * identity but no repo file for this vault (a member joining from a fresh
     * machine, or a creator whose repo file was lost) restores `K_repo` from
     * its OWN `key_envelope` instead of dying `VAULT_REPO_STATE_MISSING`:
     *
     *   1. genesis (the writer-key source) is fetched and signature-verified;
     *   2. its creator fingerprints are compared against the control plane's
     *      SIGNED allocation record — `platform_attested`, never `receipt`
     *      (the platform serves both sides of that comparison; a substituted
     *      genesis needs a substituted allocation, which the org's owners can
     *      see — TOFU + audit, human-envelopes D4's tier);
     *   3. the envelope-recipients read says whether THIS fingerprint is
     *      covered — if not, `VAULT_ENVELOPE_PENDING` names the key-holders
     *      who can fulfil and the exact next actions (never a terminal error:
     *      the desired state already records this member; any key-holder's
     *      next vault operation wraps);
     *   4. the base envelope is fetched, opened, and the repo file written
     *      `restored_from_envelope` with the genesis hash PINNED (a later open
     *      seeing a different genesis for this repo_id refuses
     *      `VAULT_CREATION_CONFLICT`).
     *
     * Rotation epochs are NOT opened here — `verifyToNewest` walks them and
     * opens each rotation-scoped envelope this identity is included in, exactly
     * as it does for every other reader (`openEpochRotationForRecipient`).
     *
     * Returns `null` when the repo file already existed (nothing restored).
     */
    /**
     * vault-multi-writer (rev 47) — the vault's CURRENT admitted writer set
     * for a cold open, computed from the chain itself: every admitted head is
     * hash-checked against the listing, signature-verified under the writer
     * the chain admits at that point, and its `add_writer_key` /
     * `writer_set_update` transitions applied in order — the same rules the
     * full verifying walk enforces, minus decryption (a cold open holds no
     * K_repo yet). Fails closed on any defect: a non-genesis wrapper is only
     * trusted when the chain vouches for it.
     */
    private resolveAdmittedWritersForColdOpen;
    ensureRepoState(): Promise<VaultColdOpenResult | null>;
    /** {@link open}, but a missing repo file triggers {@link ensureRepoState} first. */
    static openOrRestore(options: VaultOptions): Promise<{
        vault: Vault;
        restored: VaultColdOpenResult | null;
    }>;
    private kRepo;
    private epoch;
    /**
     * vault-clone-scaling (P3): staleness of the newest checkpoint coverage
     * this checkout has locally learned, measured at `newestGeneration`. Reads
     * the keystore AFTER the caller's own persist (a checkpoint-form push has
     * already recorded its fresh coverage by the time its result is built), so
     * a compacting push reports itself current. Pure + never-throwing by way of
     * the helper; unknown coverage reads as `{0, advised: false}` — silent.
     */
    private checkpointStalenessNow;
    /**
     * Fetch many generation-addressed heads' bytes ahead of the ordered walk,
     * keyed by head path.
     *
     * BATCH-FIRST (vault-batched-head-reads task 4.2): one
     * `POST …/head-reads` carries a whole page's bytes. Heads deliberately do
     * NOT ride `getObjects` — the `object-reads` presign batch is CARRIER-ONLY
     * by wire design (see `getObjectsBytes`'s fail-closed doc comment; the live
     * probe that caught this recorded `getObjects paths=67 FAILED 1ms` followed
     * by 25 serial singles), which is exactly why the batch route had to be its
     * own thing.
     *
     * FALLBACK (the shipped vault-clone-scaling P2 shape): on ANY
     * unsupported answer — an older gateway, a refusal, a fault — head bytes
     * parallelize as the SAME direct GETs the ordered walk itself would issue,
     * just early and overlapped at {@link VAULT_TRANSPORT_CONCURRENCY}. The
     * transport remembers a route-absent verdict, so the probe is paid once per
     * client, not once per window.
     *
     * Either way a per-head failure simply leaves that slot EMPTY — the walk's
     * own read owns that failure and its envelope — and results are raw and
     * UNTRUSTED: callers sha-check before use, per `walkPrefetch`'s contract.
     * That is what keeps this a transport change and never a trust change.
     */
    private prefetchHeadsConcurrent;
    private git;
    /** Fetch + pin-check the genesis (the writer key source). */
    genesis(): Promise<{
        genesis: VaultGenesis;
        sha256: string;
    }>;
    /**
     * vault-composite-state-read design D1 — the pin-current fast path
     * `verifyToNewest` tries FIRST: one `GET …/state` in place of BOTH the
     * live "server still holds the pin" read {@link readHead} would otherwise
     * perform AND, when eligible, the `listHeads` walk that would follow it.
     *
     * `null` means ineligible — the caller falls straight through to the
     * UNCHANGED `readHead` + `listHeads` flow, so a `null` here never weakens
     * verification, it only declines the shortcut:
     *   - the vault is genuinely more than one generation ahead of `pin`
     *     (the listing-walk shape this change does not touch, per design D1:
     *     "a client whose pin is >1 behind newest_generation falls back to
     *     the existing paginated listing + per-head walk");
     *   - OR (one-generation-ahead only) the D194 epoch-continuity check the
     *     ONE new head needs `pin`'s own `.epoch` for, and this call declines
     *     to fetch `pin`'s own head bytes over the network — the entire point
     *     of the shortcut — so it needs a LOCAL source for that epoch: either
     *     `pin` is genesis (a fixed, known epoch), or `pin`'s own head bytes
     *     are already cache-warm (from an earlier call, or from `admit()`'s
     *     own post-push cache write). A cold cache here is not a correctness
     *     problem, only a missed optimization.
     *
     * On a non-`null` return, `entries` is exactly what ONE real `listHeads`
     * page's `heads[]` would have been for this pin (0 items when the pin is
     * already current, 1 when it is exactly one generation behind — chain
     * link, gaplessness, and signature all still verified by the UNCHANGED
     * per-entry loop body {@link verifyToNewest} feeds them through), and
     * `pinnedHead` is `lastHead`'s INITIAL value: the real, verified head at
     * `pin.generation` when nothing new needs walking (so it is also the
     * FINAL value — the loop never runs), or a throwaway placeholder when one
     * new head is coming (the loop overwrites `lastHead` before anything else
     * ever reads it again — see `verifyToNewest`'s own `prevEpoch` line,
     * which is the ONLY thing that reads `lastHead`'s pre-loop value).
     *
     * Every byte this method reads from `getState` is cache-WARMED (head +
     * both carriers, keyed exactly as {@link readCachedHeadBytes}/
     * {@link openCarrier} already key their own writes) but NEVER trusted
     * here — every reader downstream re-verifies a cache hit against the hash
     * it would check network bytes against before using it (this file's own
     * established cache discipline; see `VaultKeystore`'s class doc
     * comment). A wrong or absent byte this method wrote is therefore just a
     * cache MISS on the next read, never a verification bypass.
     */
    private tryStateFastPath;
    /**
     * Absorb a state read's delta (vault-delta-fetch): heads blind-warm the
     * SAME keystore head cache every walk already re-verifies on read (the
     * established cache discipline — a wrong byte is a miss, never a bypass),
     * gated only on each entry's self-consistency; packs stash into the
     * transient {@link stateDeltaPacks} buffer for the next restore, which
     * hash-checks each against its carrying head's receipt before use.
     */
    private consumeStateDelta;
    /**
     * Warm the D3 carrier cache from a `GET …/state` response's two carriers,
     * keyed by the SAME `(object_id, ciphertext_sha256)` the carrying head's
     * own receipts name — a BLIND write (see {@link tryStateFastPath}'s doc
     * comment: every reader re-verifies a cache hit before trusting it, so
     * this is safe by construction). Skips a `null` carrier (absent stored
     * bytes) entirely rather than caching an absence — the existing
     * `openCarrier`/`decodeCarrierFrame` machinery already has its own
     * "frame absent" handling (`CHAIN_UNUSABLE`) via a genuine cache miss.
     */
    private warmStateCarrierCache;
    /**
     * List from the authenticated pin and verify every link upward. Persists
     * the verified prefix after each page, so a `VERIFICATION_BUDGET_EXCEEDED`
     * continues rather than restarts. Returns the newest verified state.
     *
     * `options.persist` (default `true`, repo-surface-consolidation task 3.3 —
     * `repos fsck --no-write`'s audit mode): when `false`, the chain is walked
     * and verified exactly the same way, but every `keystore.updateRepo(...)`
     * write below is skipped — no local trust pin advances. A
     * `VERIFICATION_BUDGET_EXCEEDED` pause in this mode persists nothing, so a
     * retry restarts from the ORIGINAL pin rather than resuming — the honest
     * consequence of asking for a no-write audit and then walking off the end
     * of one call's budget.
     *
     * The chain walk itself (`checkChainLink`, `assertNoTransition`, collecting
     * `rotations[]`) is ALWAYS keyless — an admitted `rotate_epoch` transition
     * never stops it (D193, rev 42), so `generation`/`head` here are the
     * genuinely chain-verified newest, independent of whether this principal
     * can decrypt anything past a rotation it cannot open.
     *
     * `options.decryptValidate` (default `false`, Part C — `repos fsck`'s
     * decrypt-validation pass): additionally opens every `rotate_epoch`
     * envelope needed and decrypts each walked generation's OWN
     * `ref_state`/`retention_roots` as it goes — the "main object" restoration
     * needs per generation — persisting newly-opened epoch keys via
     * `keystore.recordEpochRotation` exactly like an ordinary rotation
     * producer/consumer would, and counting each decrypt attempt as an EXTRA
     * unit against the same `this.budget` (decryption is the expensive step).
     * `options.strict` (default `true`) throws immediately, fail-closed, on the
     * first `VAULT_EPOCH_NOT_OPENABLE` / AEAD failure it hits — the ordinary
     * `materialize()` read path's behavior. `strict: false` (fsck's tolerant
     * mode) instead records `decrypt.failure` and stops attempting further
     * decrypts while the pure chain walk keeps going — this is exactly what
     * makes `chain_verified_to` (this call's `generation`) and `decryptable_to`
     * (`decrypt.decryptable_to_generation`) able to differ honestly.
     */
    verifyToNewest(options?: {
        persist?: boolean;
        decryptValidate?: boolean;
        strict?: boolean;
        deltaSince?: string;
        restore?: boolean;
    }): Promise<VaultVerifiedState>;
    /**
     * Read one NEWLY-LISTED head's raw bytes, trying the local cache first
     * (design D3), re-verified against `expectedSha256` — the SAME check
     * network bytes get. Safe to cache-serve ONLY because a caller here
     * always supplies a hash a FRESH `listHeads` call just reported as
     * current — the cache never substitutes for that freshness check, it just
     * avoids re-downloading bytes a live listing already vouched for. A cache
     * miss or a hash mismatch (never trusted, always falls through) fetches
     * from the network and, on a match, refreshes the cache entry. Returns
     * whatever the network returned on a final miss too (including `null`) —
     * callers keep their own existing absent/mismatch handling unchanged.
     *
     * Deliberately NOT used by {@link readHead}: that call verifies the
     * PINNED generation is STILL held by the server, with no fresh listing
     * involved — its entire purpose is detecting server-side loss/rollback,
     * which a cache read can never observe. That call always goes live.
     */
    private readCachedHeadBytes;
    /**
     * Confirm the server STILL holds the pinned generation, unchanged —
     * ALWAYS a live network read (see {@link readCachedHeadBytes}'s doc
     * comment for why this specific check is not cacheable: it exists to
     * detect server-side loss, which a cache can never observe). A
     * successful read still WARMS the cache afterward — later reads of this
     * SAME generation via the chain walk or restore benefit from it; only
     * THIS call's own read is exempt.
     */
    private readHead;
    /**
     * Decrypt + verify one carrier's already-fetched ciphertext frame; any
     * failure is `CHAIN_UNUSABLE`. Split out of {@link openCarrier} so a
     * caller that fetched the frame itself (a cache hit, or one leg of a
     * batched read) can reuse the same decode + identity/signature checks.
     *
     * `keyOverride` supplies the exact `(epoch, k_repo)` this carrier was
     * sealed under — REQUIRED for any generation that is not necessarily
     * under `this.epoch()`/`this.kRepo()` (this principal's CURRENT
     * pointer), which is exactly the case across an epoch rotation; omitted
     * call sites (checkpoint/prune paths untouched by this fold) keep the
     * prior CURRENT-pointer behavior unchanged.
     */
    private decodeCarrierFrame;
    /**
     * Fetch (network) + decrypt one carrier object by its receipt; any
     * failure is `CHAIN_UNUSABLE`. Design D3: `ref_state`/`retention_roots`
     * ciphertext is cached beside the keystore's per-repo state, re-verified
     * against `receipt.ciphertext_sha256` on every use — a hit skips the
     * network read entirely. `checkpoint_manifest` is never cached (outside
     * D3's table). The cache write derives its generation tag from the
     * DECODED object's own `generation` field, so no caller needs to thread
     * one through by hand.
     */
    private openCarrier;
    /**
     * `materialize`'s ref_state + retention_roots read, batched (design D2):
     * cache-check both first, then ONE `getObjects` call (one presigned batch
     * + concurrent GETs) for whichever missed, instead of two independent
     * presign-then-GET round trips. Falls through to zero network calls when
     * both are cache-warm. `keyOverride` is the epoch/k_repo the CARRYING
     * HEAD sealed both carriers under (D194) — both always share one head, so
     * one override serves both, unlike the WAL/checkpoint pack loops in
     * {@link restoreObjectsInto} which span many heads and many epochs.
     */
    private openMaterializeCarriers;
    /**
     * Verify to newest, then decrypt + apply its carriers — advancing the
     * materialized pin. `options.persist` (default `true`) is forwarded to
     * {@link verifyToNewest} and gates this method's OWN `materialized_pin`
     * write the same way — `repos fsck --no-write` computes and returns the
     * real ref map and generation without moving either local pin.
     *
     * Runs `verifyToNewest({..., decryptValidate: true, strict: true})`
     * internally (Part A: an ordinary read across an admitted `rotate_epoch`
     * transition now opens the rotation's own envelope and decrypts under the
     * NEW epoch, chaining through multiple sequential rotations; a keystore
     * with no envelope for a new epoch fails CLOSED with
     * `VAULT_EPOCH_NOT_OPENABLE`, never a bare `VAULT_AEAD_AUTH_FAILURE`)
     * — `strict: true` means this call throws exactly where the OLD
     * (pre-fix) `materialize()` silently produced a wrong `k_obj` instead.
     */
    materialize(options?: {
        persist?: boolean;
        deltaSince?: string;
        restore?: boolean;
    }): Promise<VaultMaterializedState>;
    /**
     * Wrap the vault's CURRENT epoch key to every org member the directory
     * lists but the vault does not yet have a `key_envelope` for.
     *
     * **This is task 1.1's residual WORKAROUND, not the design D5 ideal.** D5
     * describes a recipient-set change as an epoch rotation — "history epochs
     * stay wrapped as they were; a new member reads from their first covered
     * epoch forward" — which needs a protocol revision: V0 pins `epoch` to
     * the single constant `VAULT_GENESIS_EPOCH` on every head, so there
     * is no "forward" to speak of. What this method actually does, legally, without any
     * protocol change: `key_envelope` objects are never head-referenced (not
     * even genesis's own envelope is), so uploading an ADDITIONAL one at
     * `envelopes/<current epoch>/<recipient fingerprint>` for a missing
     * recipient is accepted by the existing generic create-only upload route
     * as-is. The honest consequence: a newly-wrapped member gets the SAME
     * single epoch every existing member already has, which in V0 means the
     * vault's ENTIRE history — not "from here forward." True forward-only
     * semantics wait on task 1's protocol revision; this method does not
     * pretend otherwise.
     *
     * **TOFU pinning (design D4 point 3).** The first time this repo wraps a
     * given `principal_id`, its CURRENT `ek_fingerprint` is pinned in the
     * keystore repo file (`envelope_recipient_pins`). On a later call, a
     * directory entry whose fingerprint no longer matches its pin is a
     * REFUSAL for that recipient ONLY — reported under `skipped` with reason
     * `pinned_key_mismatch` and both fingerprints in `details`, never wrapped
     * under the new key, and never a thrown error that would abort the whole
     * call (other recipients still get processed). Whether that mismatch is a
     * legitimate key rotation or a substitution is a product/human decision
     * this SDK does not make unattended.
     *
     * **The gateway directory route carries `public_key` on every row**
     * (`GET /orgs/v1/:org_id/encryption-keys` — see the
     * doc comment on {@link VaultOrgEncryptionKeyEntry}), so against a
     * current gateway entries actually wrap. A directory entry that arrives
     * WITHOUT the field (an older/rolling-deploy gateway) is still tolerated
     * per-entry — reported under `skipped` with reason `missing_public_key`,
     * never a thrown error that would abort the other recipients.
     *
     * Best-effort by design at the call site, not here: this method itself
     * either completes (returning a full per-recipient breakdown) or throws
     * (e.g. `VAULT_READ_ONLY` when this principal holds no signing key).
     * Callers that want "never block on this" (the deploy hook) wrap the call
     * themselves — see `Repos.capture`'s `#tryReconcileEnvelopeRecipients`.
     */
    reconcileEnvelopeRecipients(): Promise<VaultReconcileEnvelopeRecipientsResult>;
    /**
     * `keyOverride` (D194, rev 42): a `rotate_epoch` head's OWN `ref_state`/
     * `retention_roots` must be sealed under the NEWLY-sampled `K_e` at the
     * NEW epoch, never under `this.kRepo()`/`this.epoch()` (the about-to-be-
     * superseded current key) — every other call site keeps calling `seal`
     * with no override, unaffected.
     */
    private seal;
    /**
     * The owner signing seed, or `VAULT_READ_ONLY`.
     *
     * Public so the prune lane signs its intent core, wrapper, and verifier
     * receipt through the SAME refusal path every other signed object uses — a
     * second "get the seed" helper is a second place for a read-only principal to
     * slip through. The vault is already open by the time this is reachable, so
     * `ensureIdentity` never MINTS here (it would refuse at `repoFile()` first).
     */
    signer(): Uint8Array;
    private writerKeyId;
    /**
     * vault-multi-writer (task 5.8) — the push pre-check: before signing an
     * ORDINARY head (any operation that expects this session to already be an
     * active writer), refuse EARLY and LOCALLY when this session's own key is
     * not (or is no longer) an active writer, rather than let real
     * crypto/upload work run only to be refused by the gateway with a less
     * specific message once the head finally reaches it. Reads the FRESHLY-
     * pinned `writer_set_pin`; every call site below calls this immediately
     * after its own `materialize()`/`verifyToNewest()` (same generation the
     * caller is about to build against), so this performs no verification of
     * its own — it only reads what the caller already froze.
     *
     * `removedMidRace` (design D8/D10) distinguishes the two shapes this
     * refusal takes: `false` (the default) is the ORDINARY pre-check — this
     * session was never (or is not currently) an admitted writer, thrown as
     * `VAULT_WRITER_NOT_ADMITTED`. `true` is the CAS-LOSER path
     * specifically: a retry, after re-materializing from the winner, that
     * discovers THIS session's own key was removed by whatever won the race —
     * "stop if removed" in D8's loser-rule sequence (fetch winner → verify →
     * apply writer transition → stop if removed → rebase → rebuild → bounded
     * backoff) — thrown as the more specific, client-local
     * `VAULT_WRITER_REMOVED` (D10) instead: a real prior attempt just lost
     * to a removal, not a caller who was never eligible.
     *
     * Deliberately NOT called for a "handoff"-door `add_writer_key` head
     * ({@link submitWriterActivationHead}): there the signer is BY DESIGN not
     * yet a writer — becoming one is what that exact head does.
     */
    private assertCallerIsWriter;
    /** The owner's full signing keypair, or `VAULT_READ_ONLY` — same refusal path as {@link signer}, which returns only the seed; {@link sealKeyEnvelope} needs both halves. */
    private signingKeypair;
    private buildRefState;
    private buildRetentionRoots;
    /** Plaintext, independently non-thin packs covering `reachable(tips) ∖ reachable(base)`, split at the multi-object target. */
    buildPacks(tips: string[], base: string[]): Promise<Uint8Array[]>;
    /** Sorted unique object ids reachable from `tips` (the `"objectset"` content). */
    objectSet(tips: string[]): Promise<string[]>;
    /**
     * The same `"objectset"` content computed in an ARBITRARY repository.
     *
     * The prune lane's restore-and-verify pass runs against a scratch clone-back,
     * not the working tree, and must recompute the digest there with the same
     * canonicalization the manifest was built with — hence one implementation,
     * parameterized by directory, rather than a second rev-list at the call site.
     */
    objectSetIn(dir: string, tips: string[]): Promise<string[]>;
    /**
     * Decrypt one generation's `retention_roots` carrier by its head receipt.
     *
     * `materialize()` opens only the NEWEST carrier; the prune lane must compare
     * consecutive generations to see which roots LEFT the map, so it needs any
     * generation's. Same `openCarrier` path, same `CHAIN_UNUSABLE` semantics — a
     * carrier that cannot be opened is never silently treated as empty.
     */
    openRetentionRootsAt(receipt: VaultRetentionRootsReceipt): Promise<VaultRetentionRoots>;
    private digest;
    /**
     * The §1 keyed commitment under one of the five `K_digest` labels.
     *
     * Public because the prune lane needs `gcrootset` (over the GC root set's
     * sorted receipts) and `rootset` (over the retention-roots carrier) and must
     * compute them with the SAME key derivation the checkpoint manifest uses —
     * two derivations for one commitment is how a verifier and a publisher stop
     * agreeing. Keyed by design (§7.3): a server-comparable plaintext digest
     * would be a confirmation oracle.
     */
    keyedDigest(label: VaultDigestLabel, content: unknown): string;
    /** Strip the single top-level signature — the commitment preimage shape carriers use. */
    digestPreimage<T extends {
        signature: string;
    }>(o: T): Omit<T, "signature">;
    private withoutSignature;
    /** Coverage tips (§4.7): canonical refs ∪ unexpired roots ∪ the HEAD target (detached commit; an unborn symref contributes nothing). */
    static coverageTips(refs: VaultRefMap, roots: VaultRetentionRoot[], headTarget: VaultHeadTarget): string[];
    /**
     * Build a checkpoint set (§4.7): manifest + packs + the owner-signed claim
     * set, with the acceptance self-check (restore into an empty scratch, every
     * covered tip resolves, full connectivity, all three keyed commitments
     * recomputed). Coverage above the V0 maximum → `CHECKPOINT_SET_LIMIT_EXCEEDED`.
     */
    buildCheckpoint(input: {
        generation: string;
        ref_state: VaultRefState;
        retention_roots: VaultRetentionRoots;
    }): Promise<VaultBuiltCheckpoint>;
    /** §4.7 acceptance: restore from the set ALONE into an empty scratch; every covered ref resolves; fsck connectivity; recompute the three commitments. */
    acceptCheckpoint(built: VaultBuiltCheckpoint, refState: VaultRefState, roots: VaultRetentionRoots): Promise<void>;
    /**
     * The §4.7 acceptance run against a checkpoint ALREADY IN STORAGE — the
     * restore-and-verify pass a `verifier_receipt` attests (§7.3).
     *
     * `acceptCheckpoint` above proves a checkpoint the client just BUILT; this
     * proves one the client is about to make a claim about, from the stored bytes
     * alone. It reports the observed facts rather than throwing on a mismatch,
     * because "the checkpoint does not verify" is exactly the finding a receipt
     * must be able to carry as `false` — turning it into an exception would make
     * an honest negative attestation impossible to produce.
     */
    verifyStoredCheckpoint(head: VaultHead, headSha256: string): Promise<VaultStoredCheckpointAttestation>;
    /**
     * The whole verified chain, newest-first walk returned oldest-first, each
     * head paired with its checkpoint claim set (`null` when it bears none).
     *
     * The prune lane needs EVERY generation, not just the newest: a candidate is
     * an object some head once named and no surviving head still needs, and that
     * is only computable over the whole chain. Reuses {@link chainFrom}, so the
     * bytes are re-read and hash-checked against the verified chain rather than
     * trusted from a listing.
     */
    chainEntries(): Promise<Array<{
        head: VaultHead;
        head_sha256: string;
        claim_set: VaultCheckpointClaimSet | null;
    }>>;
    /**
     * vault-byo-primary-bucket task 3.2 — resolve (once, cached) this
     * machine's local BYO write config for THIS vault. `null` when none is
     * configured locally — either because this is an ordinary managed vault
     * (the common case; deliberately never confirmed with a network call, so
     * a managed vault's push pays zero extra round trips), or because this
     * machine has not been configured to write a BYO vault it nonetheless
     * belongs to (surfaced downstream as `VAULT_BYO_BUCKET_WRITE_REFUSED`
     * when the session actually names a `put: null` object with no target).
     */
    private resolveByoWriteTarget;
    private uploadAll;
    /**
     * Build + upload + sign + admit ONE generation over `base`. Shared by the ref
     * transaction path (`push`) and the checkpoint-only path (`publishCheckpoint`); the
     * caller owns the conflict loop because only it knows how to re-derive the
     * next state from the winner.
     */
    private publishGeneration;
    /**
     * The complete push: verify → materialize → evaluate → pack → upload → head → admit (409: re-apply to the winner, retry) → read back → advance pins.
     *
     * `options.base` (design D1), when supplied, is used VERBATIM for the
     * first attempt instead of a fresh {@link materialize} call — a conflict
     * retry always re-materializes from storage, exactly as when no base is
     * supplied.
     */
    push(options: VaultPushOptions): Promise<VaultPublishResult>;
    /**
     * A REAL preview of what {@link push} would publish
     * — runs the SAME local pipeline `push` runs (materialize → evaluate →
     * evolve retention roots → build refState/retentionRoots → build packs or a
     * checkpoint set → seal/encrypt) and stops BEFORE the two network
     * mutations `push` performs (`uploadAll`, `admit`). One shot, no conflict
     * retry: there is nothing to retry against, since no generation is ever
     * admitted. `would_admit_generation` is therefore the generation this push
     * WOULD claim over the CURRENTLY OBSERVED base — a concurrent publisher can
     * still take it first before a real push runs, exactly as `git push
     * --dry-run` never promises a fast-forward will still hold by the time a
     * real push executes.
     *
     * Never retries and never allocates: an unallocated vault has no `repo_id`
     * (hence no encryption key) to preview a push against at all — callers
     * resolve the vault READ-ONLY first (see `Repos.planCapture` in
     * `../namespaces/repos.js`, which reports `allocation_needed: true` in
     * that case instead of calling this method).
     */
    planPush(options: VaultPushOptions): Promise<VaultPushPlan>;
    /**
     * Publish an `ordinary_push` checkpoint-bearing head that changes NO ref (the
     * canonical map and HEAD target are carried forward). With a cutoff the head
     * binds a fresh `retention_cutoff` ticket and roots past their ≥90-day lane
     * may leave the map; without one it is the no-removal form (§4.5a) and every
     * root is carried.
     *
     * Root expiry is PERMISSIVE: a root whose `effective_admitted_at` this client
     * cannot resolve is RETAINED. That is deliberate — `effective_admitted_at =
     * max(prepared_at, the admission record's storage creation time)`, and a client
     * reading only object bytes cannot see the second term. Resolving it from
     * `prepared_at` alone would shorten the lane, which the protocol's own
     * delayed-PUT vector calls out as the naive implementation.
     *
     * This is NOT `run402 repos gc`: the §7.2 maintenance CYCLE (purpose
     * `maintenance_cycle`, C1/C2 roles, stage claim sets, prune intents, `R2_cap`
     * accounting) is a separate protocol under compact authority, and gets its own
     * method when it ships.
     */
    publishCheckpoint(options?: {
        cutoff?: VaultCutoffOptions | false;
    }): Promise<VaultPublishResult>;
    /** Request a `retention_cutoff` ticket and check it binds THIS base head (and the service key, when one is pinned). */
    private issueRetentionCutoff;
    /**
     * `overrides.epoch` (D194, rev 42 fix): defaults to `this.epoch()` — the
     * LOCALLY KNOWN current epoch — rather than the fixed genesis constant.
     * This is load-bearing, not cosmetic: once ANY rotation has landed, every
     * ORDINARY (non-`rotate_epoch`) head this principal signs must still
     * claim the vault's CURRENT epoch (protocol §4.3's chain-link rule —
     * "every head's epoch equals its predecessor's UNLESS this head admits a
     * `rotate_epoch` transition"); hard-coding the generation-1 constant here
     * would make EVERY ordinary push after a vault's first rotation refuse
     * `CHAIN_BROKEN` forever. `rotateEpoch` passes `overrides.epoch = new_epoch`
     * explicitly for the ONE head that legitimately claims a DIFFERENT epoch
     * than `this.epoch()` currently reads (the local pointer only advances
     * AFTER a successful admit, via `keystore.recordEpochRotation`).
     */
    private signHead;
    /** Admit a signed head; on success read it back from storage and compare BEFORE any pin advances. */
    private admit;
    /**
     * Walk the chain BACKWARD from the current tip to find the nearest
     * admitted `recipient_pin_manifest` receipt (D197: "a fresh client... reads
     * the LATEST admitted manifest, walking `prev_sha256` back to the nearest
     * head carrying one"). Heads are plaintext-structured/signed (never
     * encrypted) so this needs no key material — only signature verification
     * against the creator's own pubkey. Falls back to `vault_genesis.pin_manifest`
     * (D198 N-recipient genesis — this SDK does not BUILD one, but reads one
     * correctly for interop), then to the zero-value sentinel (no manifest has
     * ever been admitted — every principal starts `excluded_unconfirmed`).
     *
     * Cost is O(distance to the nearest pin-manifest-bearing head) — for a
     * vault that has never published one, that is every generation back to
     * genesis. There is no index that avoids this in protocol v0 (the same
     * cost class `verifyToNewest`'s own chain walk already has); a vault with
     * a long, pin-manifest-free history pays it once per rotation.
     */
    private loadEffectivePinManifest;
    /**
     * Local-cache short-circuit (see {@link VaultRepoFile.known_pin_manifest}'s
     * own doc comment for why this is safe): a manifest THIS keystore itself
     * just built, signed, and admitted is resolved from the on-disk cache
     * instead of a network `object-reads` round trip — same return shape,
     * skipped only on an exact `(pin_manifest_version, stored_bytes_sha256)`
     * match against `receipt`. A miss (cache absent, or naming a DIFFERENT
     * manifest — e.g. one another principal/machine published) falls through
     * to the unchanged network path below.
     *
     * History: this cache originally also routed around a gateway gap —
     * `POST …/object-reads` rejected every `recipient_pin_manifest` read
     * (its null-`idScalar` validation was hardcoded to `key_envelope`'s
     * `{epoch, recipient_fingerprint}` shape, never generalized when D197
     * shipped the second path-addressed kind), which 400'd the network
     * fallback below. That gateway bug is fixed: the network path works for
     * any keystore, including
     * §4.11's fresh-client "SEEDS its local pin file from it" onboarding.
     * The cache stays purely as the round-trip saver described above.
     */
    private readPinManifestObject;
    /**
     * Build the successor `recipient_pin_manifest` object from the currently-
     * effective PREDECESSOR manifest plus a batch of receipted updates (D197
     * full-map conservation: `next_map = prior_map + receipt-authorized
     * additions/replacements`). Shared by {@link publishPinManifestUpdate} (a
     * single-entry ordinary-push publish) and {@link rotateEpoch}'s
     * `pending_confirmations` fold (a multi-entry publish riding the SAME
     * head as the rotation, D196). Validates EVERY update's receipt against
     * `prior` before building — a receipt issued against a stale predecessor
     * fails closed here (`VALIDATION_FAILED`), never silently overwritten,
     * matching the gateway's own admission-time field-by-field check.
     *
     * Does NOT admit anything — the caller uploads `.upload` and attaches the
     * returned `{object_kind, pin_manifest_version, stored_bytes_sha256,
     * size_bytes}` receipt shape to whichever head it is publishing on.
     */
    private buildPinManifestUpdate;
    /**
     * Build a `recipient_pin_manifest` update (D197 full-map conservation) and
     * publish it via an ORDINARY head (`gitvault.writer`-sufficient — the
     * OWNER-GATED half of the ceremony already happened at `/confirm`/`/repin`,
     * which is what produced `receipt`; PUBLISHING the resulting entry is
     * ordinary-writer authority, same split as envelope-wrap authority
     * always had). Carries the SAME refs/roots forward unchanged, at the
     * CURRENT epoch (this is not a rotation — no new epoch, no new K_e).
     *
     * **This is an ORDINARY admission (`transition: null`) and is therefore
     * itself refused `EPOCH_ROTATION_REQUIRED` while a migration/revocation/
     * exposure condition is outstanding on this vault (D193) — the exact
     * deadlock the incident behind {@link Vault.rotateEpoch}'s
     * `pending_confirmations` parameter closes.** When this vault is in that
     * state, fold the receipt into `rotateEpoch({..., pending_confirmations:
     * [{principal_id, ek_fingerprint, receipt}]})` instead of calling this
     * method directly — that submission carries a `rotate_epoch` transition,
     * which IS the gate's own escape valve.
     */
    publishPinManifestUpdate(input: {
        principal_id: string;
        ek_fingerprint: string;
        confirmed_by: "operator_confirmation";
        receipt: VaultRecipientConfirmationReceipt;
    }): Promise<VaultPublishResult>;
    /**
     * Drive one epoch rotation to a committed head (D193-D203). This is the
     * producer's obligations in full: sample a FRESH `K_e` independent of
     * every prior epoch key this principal has locally held; compute the H
     * bijection from the live desired-recipient state + the effective pin
     * manifest; seal one `key_envelope` per included recipient from the SAME
     * `K_e`; submit the create-only `rotation_attempt_descriptor` BEFORE any
     * envelope upload; upload the envelopes; submit the `rotate_epoch` head;
     * on a CAS conflict, retry from a fresh `materialize()` (the SAME
     * conflict-retry shape `push()` uses); after commit, verify this
     * principal's OWN envelope (when it is itself a recipient) opens to
     * exactly the committed `K_e` and reproduces `epoch_key_commitment`
     * (D200's narrowed per-recipient self-check — never a global proof);
     * advance the local keystore's current epoch/key pointer AND retain the
     * prior key in `epoch_keys`.
     *
     * **`recipient_state_version`/`recipient_revocation_version` are REQUIRED
     * inputs, not discovered here.** The gateway exposes NO general read route
     * for `internal.vault_recipient_state_counters` (D194) — the ONLY
     * client-visible read of these two org-scoped counters today is the
     * response of `POST …/recipients/:principal_id/key-revocation`
     * ({@link VaultTransport.declareRecipientKeyRevoked}), which is why
     * {@link rotateEpochForKeyRevocation} (below) is the one fully
     * self-contained entry point. For `member_removed` / `elective_rekey` /
     * `epoch_secret_exposed`, a caller that does not already know the current
     * counter pair cannot discover it from any shipped gateway route — this
     * is a confirmed gap in the live gateway (verified against
     * `packages/gateway/src/services/vault/reads.ts:getVaultRecord` and
     * every `routes/vault*.ts` handler, not inferred), not a client
     * limitation this SDK can work around. Passing a stale/guessed pair fails
     * safely: the admission fence's D194 frozen-counter comparison refuses
     * `RECIPIENT_SET_MISMATCH` rather than silently canonizing against wrong
     * evidence.
     *
     * **`options.pending_confirmations` (the manifest-publish deadlock fix).**
     * `publishPinManifestUpdate` is an ORDINARY admission (`transition:
     * null`) and is therefore itself refused `EPOCH_ROTATION_REQUIRED` while
     * this vault has an urgent/migration condition outstanding — the exact
     * state a `rotateEpoch` call is being made to clear. On an
     * `epoch_secret_exposed` rekey `/confirm` mints a receipt server-side,
     * but the ordinary push that would publish it never admits. Pass the
     * pending receipted updates
     * here instead — they ride the SAME head as this rotation's `transition`,
     * which IS `EPOCH_ROTATION_REQUIRED`'s own escape valve, so the publish
     * is no longer blocked. **This does NOT include these principals in
     * THIS rotation's `envelopes[]`** — protocol-v0.md D196 is explicit: "a
     * manifest update riding the SAME head never self-authorizes its own
     * recipients"; `confirmed(h)` for THIS rotation still reads only the
     * PREDECESSOR manifest, unchanged. They land in
     * `excluded_unconfirmed_principal_ids` here (same as without folding) and
     * become eligible starting at the NEXT rotation, once this manifest is
     * the admitted predecessor. See {@link VaultRotationResult.pin_manifest_published}.
     *
     * **Honest residual — this does not rescue a vault with ZERO ever-
     * confirmed principals.** If `included` would be empty even with the
     * fold (no predecessor-confirmed principal exists at all — e.g. a
     * grandfathered pre-rev-42 vault whose bare genesis never published a
     * pin manifest and which was never bootstrapped before its
     * `migration_rotation_required` flag was set), the gateway refuses
     * `EPOCH_ROTATION_WOULD_LEAVE_VAULT_UNCOVERED` regardless of what rides
     * along on `pin_manifest` — D196's same-head exclusion makes THIS
     * impossible to route around from the client. That is a genuine,
     * currently-open protocol gap (not something this parameter can paper
     * over) and needs an operator-side decision, not a client workaround.
     */
    rotateEpoch(options: {
        reason: VaultRotationReason;
        recipient_state_version: string;
        recipient_revocation_version: string;
        client_idempotency_key?: string;
        ikm_e?: Uint8Array;
        pending_confirmations?: {
            principal_id: string;
            ek_fingerprint: string;
            receipt: VaultRecipientConfirmationReceipt;
        }[];
        /**
         * vault-multi-writer (task 5.9, D7/D228) — an outstanding
         * gateway-blocked writer set (`ineligible_members`, checked fresh on
         * every call — see the `writer_set_update` fold-in below) is ALWAYS
         * folded into this rotation automatically; this flag is the EXPLICIT
         * owner + step-up acknowledgment D7 requires ONLY when that fold-in
         * would empty the vault's writer set entirely (the sole surviving
         * writer was itself gateway-blocked) — the declared read-only terminal.
         * Every OTHER case that would empty the writer set is refused
         * `EPOCH_ROTATION_WOULD_LEAVE_VAULT_UNCOVERED` regardless of this flag;
         * it widens nothing beyond that one specific, named exception.
         */
        force_empty_writer_set?: boolean;
    }): Promise<VaultRotationResult>;
    /**
     * The writer-capable rotation that completes an org membership removal
     * (vault-multi-writer D6: "`member_removed` keeps its automatic
     * writer-capable path"). The removal itself already advanced the org's
     * D194 counters and flipped the member to `pending_removal`, so there is
     * nothing to declare: read the counters off the envelope-recipients read
     * (the same read the H-partition uses) and rotate under
     * `reason:"member_removed"`, which needs `gitvault.writer` only — any
     * surviving writer can run it, no owner step-up. Refuses
     * `VAULT_ROTATION_COUNTERS_UNAVAILABLE` on a gateway that does not yet
     * carry the counters on that read.
     */
    rotateEpochForMemberRemoval(options?: {
        client_idempotency_key?: string;
    }): Promise<VaultRotationResult>;
    /**
     * The ONE fully self-contained rotation entry point: declares
     * `reason:"recipient_key_revoked"` (owner + step-up) for `principal_id`,
     * takes the D194 counters straight off that call's OWN response, and
     * drives the rotation with them — no external counter source needed.
     */
    rotateEpochForKeyRevocation(principalId: string, options?: {
        client_idempotency_key?: string;
    }): Promise<VaultRotationResult>;
    /**
     * Submit a REF-NEUTRAL `add_writer_key{authorization.kind:"handoff"}`
     * activation head — no ref/checkpoint/repair changes ride it (protocol
     * §4.17's own "ref-neutral head shape... for the activation head" rule),
     * built by `publishGeneration` the SAME way every ordinary no-op push
     * would be (`refs`/`head_target` carried forward from `base` UNCHANGED),
     * just with `transition` set. Retries `HEAD_CAS_CONFLICT` like every
     * other publish path here. The caller (`resume()`) has already built and
     * locally sanity-checked the grant + acceptance; this method only reads
     * the FRESHLY-verified `writer_set_pin` (set by the `materialize()` this
     * same call performs, mirroring `push()`'s own base-then-retry shape) to
     * compute `base_writer_set`/`next_writer_set`, signs the head under
     * `this.signer()` (the ADDED key itself — protocol §4.17's "head signed
     * by the added key" — since `signer()` reads straight from THIS
     * keystore's identity, and `resume()` mints/loads that identity before
     * ever reaching this call), and advances the local `writer_set_pin` on
     * success so a subsequent read reflects the new writer without a second
     * chain walk.
     *
     * Crash-resumable (task 5.6): the chain burns `handoff_id` single-use, so
     * a naive resubmission after a crash between a prior attempt's successful
     * admission and this checkout learning about it would be refused
     * VALIDATION_FAILED. Returns `{outcome:"already_admitted", generation}`
     * instead of submitting anything whenever the freshly-verified writer set
     * ALREADY contains `input.addedWriterKeyId` — the caller treats both
     * outcomes as success (the writer IS active either way) and proceeds.
     */
    submitWriterActivationHead(input: {
        addedWriterKeyId: string;
        addedSigningPubkeyB64u: string;
        addedPrincipalId: string;
        handoffId: string;
        grant: Record<string, unknown>;
        acceptance: Record<string, unknown>;
    }): Promise<{
        outcome: "activated";
        result: VaultPublishResult;
    } | {
        outcome: "already_admitted";
        generation: string;
    }>;
    /**
     * vault-multi-writer (task 5.7) — the "writer"-door twin of {@link
     * submitWriterActivationHead}: an ALREADY-ACTIVE writer (this vault
     * session's own key) admits `input`, an eligible org member with a
     * published signing key but no writer standing yet, via a fresh
     * `add_writer_key{"writer"}` head. No grant/acceptance — the carrying
     * head's own signer being an active writer at the predecessor generation
     * IS the authorization (`validateAddWriterKeyPayload`'s `"writer"`
     * branch), re-verified admission-side by `checkTransitionAdmissible`.
     * Task 5.8 adds the SAME local pre-check every other head-signing path
     * gets: if THIS session's own key is not (or is no longer) an active
     * writer, refuse EARLY and LOCALLY (`VAULT_WRITER_NOT_ADMITTED` /
     * `VAULT_WRITER_REMOVED`) rather than let the head reach the gateway
     * only to be refused there — redundant with `reconcileWriterAdmissions`'s
     * own upfront gate for that caller, but this method is public and a
     * direct caller should get the same fast, specific refusal.
     */
    admitPendingWriter(input: {
        addedWriterKeyId: string;
        addedSigningPubkeyB64u: string;
        addedPrincipalId: string;
    }): Promise<{
        outcome: "activated";
        result: VaultPublishResult;
    } | {
        outcome: "already_admitted";
        generation: string;
    }>;
    /**
     * vault-multi-writer (task 5.7) — the vault's OWN writer-admission
     * reconcile: reads `pending_writers[]` off the vault record (eligible org
     * members with no writer standing yet), resolves each candidate's
     * published signing key off the org's encryption-key directory (the SAME
     * `/orgs/v1/:org_id/encryption-keys` row {@link
     * reconcileEnvelopeRecipients} reads, widened D9 to also carry the
     * signing half), and admits each via {@link admitPendingWriter} — one
     * `add_writer_key{"writer"}` head per candidate, sequentially: each
     * admission materializes fresh against the PRIOR one's own updated writer
     * set, so there is no batched/parallel form.
     *
     * Requires THIS session's own key to already be an active writer (the
     * chain's own authorization rule for the "writer" door) — checked ONCE,
     * upfront, off the locally-pinned `writer_set_pin` (no network call),
     * rather than discovered N times over from N identical gateway refusals:
     * a session that reached this call already pushed successfully as an
     * active writer in every wired call site (push/snapshot/deploy) OR is a
     * member that simply is not one yet (session-start/read) — the SAME
     * "not yet admitted" case {@link Repos.capture}'s own pre-push check
     * (task 5.8) names, not a new refusal shape. Returns `{eligible: false}`
     * with every other field empty (never throws) for that case: every OTHER
     * `pending_writers[]` entry would fail identically, so there is nothing
     * this call can usefully do, and "I am not a writer" is not this vault's
     * fault — but it is distinguishable from "there was nothing pending"
     * (`eligible: true`, still all empty), which matters to a caller trying
     * to explain an all-empty result to a human.
     */
    reconcileWriterAdmissions(): Promise<VaultReconcileWriterAdmissionsResult>;
    /**
     * Publish a repair head over `base_generation` (§4.3): superseded tips that
     * the repaired state no longer reaches enter the retention-root map with
     * `dropped_at_generation = the repair generation`; the head carries the
     * mandatory self-contained checkpoint. Coverage that cannot be built →
     * `REPAIR_TARGET_UNPRESERVABLE`. A repair never crosses an admitted transition.
     */
    repair(input: {
        base_generation: string;
        reason: VaultRepairDescriptor["reason"];
    }): Promise<VaultPublishResult>;
    /** Heads `base..newest` (already chain-verified by `verifyToNewest`) re-read + hash-checked from storage. */
    /**
     * Re-read heads `baseGeneration..newest` and hash-check each against the
     * verified chain. `transitions` says what an admitted transition head on
     * the walk means: a repair walks with `"refuse"` (a repair never crosses
     * one), a whole-chain read such as compaction planning walks with
     * `"activated"` (`verifyToNewest` already validated every head, so the
     * only rule left is {@link assertNoTransition}'s fail-closed one — a
     * `rotate_epoch` or `add_writer_key` head is ordinary chain state a
     * rev-47 vault with any handoff or member change carries).
     */
    private chainFrom;
    /**
     * Pull the newest checkpoint (if any) and every later WAL pack into
     * `targetRepoDir` (an initialized repository), then verify every canonical
     * ref + the HEAD target resolves. Returns the materialized refs.
     *
     * Design D5 (vault-client-round-trips): incremental above the local
     * `restored_through` marker (this target directory's own local git
     * config, read/written by {@link readVaultRestoreMarker}/{@link
     * writeVaultRestoreMarker}) — replaying only the WAL packs of
     * generations above it — WHEN the walk from newest back to the marker is
     * plain WAL the entire way. The moment that walk crosses a
     * checkpoint-bearing, repair, or transition head (checked BEFORE
     * including a head, so the marker-boundary and wholesale-boundary checks
     * share one pass), this falls back to the ORIGINAL wholesale walk below —
     * re-fetching any head already visited during the aborted attempt is a
     * cache hit (design D3), never a second network round trip. Coverage
     * verification and retained-refs reconciliation run UNCHANGED on both
     * paths; the marker only advances after they both succeed.
     */
    restoreObjectsInto(targetRepoDir: string, reuse?: {
        marker: VaultRestoreMarker | null;
        state: VaultMaterializedState;
    }): Promise<{
        refs: VaultRefMap;
        head_target: VaultHeadTarget;
        generation: string;
        retained_refs: VaultRetainedRefsReconcileResult;
    }>;
    /**
     * The shared restore tail (vault-restore-recipe): apply `heads`'
     * checkpoint (if any) + WAL packs into `targetRepoDir`, verify §4.7
     * coverage, reconcile retained refs, and advance the marker — IDENTICALLY
     * whether `heads` came from {@link restoreObjectsInto}'s own backward walk
     * or a verified restore plan ({@link tryConsumeRestorePlan}). `incremental`
     * only affects the checkpoint-coverage LEARNING step (an incremental walk
     * stops at the marker and learns nothing; a plan-derived call always
     * passes `false` — a plan is the wholesale shape by construction).
     *
     * `precheckedCheckpoint`, when non-null, is a claim set + manifest the
     * caller ALREADY verified (signature, cross-equality — {@link
     * verifyRestorePlan}) — skips the network fetch + re-verify this method
     * would otherwise run for `heads[0].checkpoint`. `planPacks`, when
     * non-null, is a map of pack bytes the caller already has (untrusted —
     * only used when its OWN hash matches the carrying head's/manifest's
     * receipt, exactly `stateDeltaPacks`'s discipline), keyed
     * `${object_kind}:${object_id}` so it covers BOTH WAL and checkpoint
     * packs (`stateDeltaPacks` never carries checkpoint packs — a delta span
     * never crosses a checkpoint boundary). Both are `null` on the ordinary
     * walk path, in which case every line below is byte-identical to the
     * pre-vault-restore-recipe shape.
     */
    /**
     * vault-multi-writer (rev 47): the key a head's own checkpoint claim set
     * and carriers were signed under — the head's writer, resolved against the
     * keystore's persisted `writer_set_pin` (maintained by the chain walk that
     * always precedes a restore), falling back to `fallback` (the genesis
     * creator) for a pre-rev-47 keystore or a writer the pin no longer lists.
     * Before this, a restore verified every checkpoint under the creator key
     * and a clone of any vault whose newest checkpoint was minted by a handoff
     * recipient died CHECKPOINT_INCOMPLETE "claim set signature fails".
     */
    private writerKeyForHead;
    private applyRestoreHeads;
    /**
     * `newest.epoch_keys_hex[epoch]`, decoded — the D194 per-carrying-head key
     * lookup shared by {@link applyRestoreHeads} and {@link verifyRestorePlan}.
     * `newest.epoch_keys_hex` is the FULL map `materialize()` resolved
     * (throwing `VAULT_EPOCH_NOT_OPENABLE` fail-closed if any needed epoch
     * could not be opened), so every lookup through this helper is guaranteed
     * present.
     */
    private epochKeyFor;
    /**
     * Verify a restore plan's heads (self-consistency + backward chain-link +
     * cross-check against the caller's own already-verified `newest`, plus
     * genesis/boundary linkage) and, when the boundary carries a checkpoint,
     * its claim set + manifest (signature, cross-equality) — the SAME
     * obligations {@link applyRestoreHeads}'s ordinary path runs, against
     * plan-supplied bytes instead of network-fetched ones (design D5: "the
     * plan is transport, never trust"). Returns `null` on ANY failure — a
     * self-inconsistent head, a broken link, a wrong newest, a bad signature,
     * a cross-equality mismatch, anything the reused `decodeCarrierFrame`/
     * `checkClaimSetEquality` reject — rather than throwing, so the caller's
     * fallback to the ordinary walk is unconditional and silent, mirroring
     * `assembleVaultStateDelta`'s own disqualification posture server-side.
     *
     * Heads that verify their OWN chain link (Stage A) are cache-warmed into
     * {@link walkPrefetch} BEFORE Stage B (the checkpoint) runs, so a
     * checkpoint failure still leaves the fallback walk with every head this
     * call already proved — the D3 cache-hit discipline, extended to a failed
     * plan.
     */
    private verifyRestorePlan;
    /**
     * Consume a stashed restore plan (vault-restore-recipe design D1-D6):
     * verify it ({@link verifyRestorePlan}) and, on success, apply it via the
     * SAME shared tail the backward walk uses ({@link applyRestoreHeads}) —
     * `null` on absence or ANY verification failure, the caller's cue to run
     * (or continue) the ordinary walk. Clears `this.stateRestorePlan`
     * unconditionally: a plan is single-use whether it verified or not (a
     * failed plan's USABLE heads already rode into `walkPrefetch` inside
     * `verifyRestorePlan` itself).
     */
    private tryConsumeRestorePlan;
    /** Thin passthrough to the transport — see {@link VaultTransport.openCompactionGrant}. */
    openCompactionGrant(): Promise<VaultCompactionGrant>;
    /** Thin passthrough to the transport — see {@link VaultTransport.closeCompactionGrant}. Always safe best-effort; never throws by construction of the route (idempotent). */
    closeCompactionGrant(): Promise<{
        closed: boolean;
    }>;
}
/** §4.7 cross-field equality: covers_through agree; the claim set's ordered pack ids/hashes/sizes/total equal the manifest's (shared stored fields only). */
export declare function checkClaimSetEquality(claimSet: VaultCheckpointClaimSet, manifest: VaultCheckpointManifest, headCoversThrough: string): void;
/** Convenience for the deploy lane: the §6.5 capture binding. */
export declare function captureBinding(captureId: string, applyPlanSha256: string | null, snapshotOidHmac: string): VaultCaptureBinding;
export type { VaultRefUpdate };
//# sourceMappingURL=vault-publication.d.ts.map