/**
 * vault — `r402s/v0` crypto core (protocol rev 40, §1 + §2; task 5.1).
 *
 * Pure, isomorphic (WebCrypto + noble + `@hpke/core`), no I/O. Everything
 * byte-level the protocol pins lives here ONCE and is replayed against the
 * frozen vector set (`vault.crypto.test.ts`):
 *
 *   - JCS (RFC 8785) for the no-JSON-numbers profile + the strict-parse profile
 *   - the two hash rules: stored bytes (JCS INCLUDING `signature`) and the
 *     signature preimage `"r402s/v0/" + object_kind + "\n" + JCS(object minus signature)`
 *   - Ed25519 strict RFC 8032 — noble with `zip215: false` pinned; a
 *     ZIP215-only signature is REJECTED (vector `zip215-002`)
 *   - `lp` / `lp_opt` length prefixes + the open-id / open-binding preimages
 *   - HKDF-SHA-256 `k_obj` / `K_digest` derivations and HMAC keyed commitments
 *   - the XChaCha20-Poly1305 frame (`"R402S0"` ‖ suite byte ‖ 24-byte nonce ‖ ct‖tag)
 *     with its seven-member JCS AAD
 *   - the `key_envelope` HPKE seal/open (Base mode, DHKEM(X25519,HKDF-SHA256) /
 *     HKDF-SHA256 / ChaCha20-Poly1305) with the EXACT D188 `info` / AAD bytes
 *   - genesis key-binding + recovery-receipt checks
 *
 * Design rules carried from the protocol: HPKE is a NAMED implementation
 * (`@hpke/core` + `@hpke/chacha20poly1305`), never assembled from primitives
 * (D38); an envelope's signature is verified BEFORE any open; plain hashes of
 * plaintext content never leave the client (every server-comparable digest is
 * keyed); object ids are single-use key labels — callers MUST NOT re-encrypt
 * under an id (retries read-and-compare).
 */
import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
import { CipherSuite } from "@hpke/core";
import type { VaultAddWriterKeyPayload, VaultAllocation, VaultDigestLabel, VaultEncryptedObjectKind, VaultEncryptionKeypair, VaultEnvelopeAad, VaultFormat, VaultFrameAad, VaultKeyEnvelope, VaultKeyEnvelopeReceipt, VaultObjectKind, VaultRecoveryReceipt, VaultRotateEpochPayload, VaultSealedFrame, VaultSealedKeyEnvelope, VaultSignedObject, VaultSigningKeypair, VaultStrictParseReason, VaultSuite, VaultGenesis } from "./vault.types.js";
/** Wire format tag (retained by decision D181 across the product rename). */
export declare const VAULT_FORMAT: "r402s/v0";
/** The one V0 cryptographic suite. */
export declare const VAULT_SUITE: "r402s-1";
/** Frame magic, bytes 0–5 of every encrypted object. */
export declare const VAULT_FRAME_MAGIC: "R402S0";
/** Frame suite byte (byte 6) and its two-char AAD spelling. */
export declare const VAULT_FRAME_SUITE_BYTE: 1;
export declare const VAULT_FRAME_SUITE_ID: "01";
/** Protocol §2: max plaintext per encrypted object (256 MiB). */
export declare const VAULT_MAX_PLAINTEXT_BYTES: number;
/** The genesis generation / epoch constants (schema consts). */
export declare const VAULT_GENESIS_GENERATION: "0000000000000000";
export declare const VAULT_GENESIS_EPOCH: "0000000000000001";
/** HPKE `info` label for `key_envelope` (D188). */
export declare const VAULT_ENVELOPE_INFO_LABEL: "r402s/v0/envelope";
/**
 * The V0-A terminal-loss sentence — protocol §0 / client-surface spec. Doctor
 * and status surfaces MUST state it verbatim while V0-A is current. Do not
 * paraphrase; the wording is a reviewed product commitment.
 */
export declare const VAULT_TERMINAL_LOSS_STATEMENT: "whole-machine or whole-keystore loss is terminal for vault history until human envelopes ship";
/**
 * The protocol's durability sentence — the keystore-qualified half of the
 * terminal-loss statement, reusable on its own. It is TRUE regardless of how
 * many principals cover a vault (unlike {@link VAULT_TERMINAL_LOSS_STATEMENT},
 * which is specifically the single-principal V0-A claim), so a caller that has
 * locally proven a vault carries >= 2 covering recipients prints this sentence
 * instead of the terminal-loss one — see `Repos.status()`'s `covering_recipients`
 * field. Never paraphrase; the wording is a reviewed product commitment.
 */
export declare const VAULT_DURABILITY_STATEMENT: "The vault protects source history from host-side loss while a principal keystore survives.";
/** Protocol §0 sentence in full, as `doctor`/`status` print it. */
export declare const VAULT_TERMINAL_LOSS_DOCTOR_TEXT: string;
/**
 * vault-mirror-and-recover (design D8) — the two honesty statements. Both
 * appear VERBATIM wherever mirror status or recovery success is shown (mirror
 * `status`/`sync`, `recover`, keyless `mirror verify`), same voice and
 * mechanism as {@link VAULT_TERMINAL_LOSS_STATEMENT} above. Never
 * paraphrased — the §6.4 honesty posture is a reviewed product commitment.
 */
export declare const VAULT_MIRROR_VALIDITY_NOT_FRESHNESS_STATEMENT: "this recovery proves validity, never freshness \u2014 a mirror (or the vault itself) can only tell you the newest generation it happens to hold, never that a newer one does not exist elsewhere";
/** The mirror does not change the V0 terminal-loss sentence: it is durability, not a second key. */
export declare const VAULT_MIRROR_KEYSTORE_STILL_REQUIRED_STATEMENT: "a mirror without the principal keystore (or an equivalent key) recovers nothing \u2014 mirroring ciphertext does not create a second key, and the V0 terminal-loss sentence is unchanged";
/**
 * vault-mirror-default — the recommended-default framing, stated the same
 * way everywhere (the copy gate pins the doc surfaces to it). "Replicated"
 * describes the platform's storage substrate (S3 multi-AZ redundancy), never
 * a second run402 service; the customer-owned mirror is the only copy outside
 * run402's custody, and — per the two honesty statements above — it proves
 * validity, never freshness, and recovers nothing without the keystore.
 */
export declare const VAULT_MIRROR_THREE_COPIES_STATEMENT: "the recommended shape is three copies: your working clone, the platform's replicated vault, and a mirror in storage you own";
/**
 * vault-byo-primary-bucket (design D4) — the degraded-read mechanism
 * sentence: the invariant part of the ONE stderr line a degraded chain/
 * payload read prints (`formatVaultDegradedReadNote`, `vault-node-
 * degraded-read.ts`). Mechanism-only, per the copy rules — never a
 * confidentiality claim: it names WHY (run402 is unreachable), restates
 * validity-not-freshness in brief, and states that a write still needs the
 * gateway (D194 — admission is irreducibly live-server, so writes are never
 * rerouted). The caller prepends the fallback's own destination (never a
 * credential); that is data, not part of this claim, so it lives outside
 * the constant. Never paraphrase — same reviewed-product-commitment voice
 * as the other statements in this file.
 */
export declare const VAULT_DEGRADED_READ_STATEMENT: "run402 is unreachable \u2014 this read is served from your mirror; it proves validity, not freshness; a later push still requires the gateway";
/**
 * vault-mirror-default — the `vault_unmirrored` standing finding, worded to
 * stay true in BOTH pre-clear states (no mirror configured at all, and a
 * configured mirror with no successful write/sync yet — the finding clears on
 * the first successful mirror write or sync, never on configuration alone).
 * Informational, never blocking; computed client-side only, so the gateway
 * never learns whether a mirror exists.
 */
export declare const VAULT_UNMIRRORED_FINDING_STATEMENT: "this vault has no customer-held mirror copy yet — the recommended shape is three copies: your working clone, the platform's replicated vault, and a mirror in storage you own; until a first mirror write or sync succeeds, the third copy does not exist";
/**
 * vault-mirror-default — the one-liner `repos create` prints beside the
 * recovery receipt: the two things worth doing in the first minute, stated in
 * the first minute. Deliberately custody-scoped ("stays in your custody"),
 * never a recoverability claim — the terminal-loss statement printed beside it
 * carries the key half.
 */
export declare const VAULT_MIRROR_SETUP_HINT: "recommended: 'run402 repos mirror <destination>' (an s3:// bucket or a local directory) starts the customer-owned mirror \u2014 every later capture is mirrored to it automatically, and it is the copy that stays in your custody";
/**
 * vault-byo-primary-bucket (proposal, the ratified D + chain-copy rung-3
 * headline; design D10 — verified from network topology once the client's
 * BYO write path ships, not by this sentence alone). Structurally scoped to
 * `storage_profile:"byo"` ONLY — the sentence is FALSE for a managed vault
 * (payload ciphertext IS client-written to run402's own bucket there), so
 * every caller MUST gate it behind a BYO-profile check before printing it
 * and MUST NEVER use it as a blanket claim describing vault in general.
 * INERT as of this fold (Phase 1, protocol coordination only) — nothing
 * emits it yet; Phase 2/3 wire the allocation/doctor/`repos view` surfaces
 * that print it once `storage_profile:"byo"` is a real, allocatable vault
 * state. Never paraphrase — the wording is a reviewed product commitment.
 */
export declare const VAULT_BYO_HEADLINE_STATEMENT: "your source ciphertext never touches our infrastructure \u2014 not even encrypted";
/**
 * vault-byo-primary-bucket (design D7) — the allocation-time no-payload-copy
 * disclosure a BYO `repos create` prints, unconditionally and independent of
 * mirror status: unlike a managed vault (where the platform's own bucket is
 * an extra copy beside the customer's clone), a BYO vault's primary bucket is
 * the ONLY payload copy from the moment it is allocated — run402 holds the
 * small signed chain only, never the payload. Distinct from
 * {@link VAULT_BYO_UNMIRRORED_REMEDY_STATEMENT} below, which is about a
 * missing SECOND customer-held copy on top of this primary one. INERT as of
 * this fold — Phase 2/3 wire the surfaces that print it. Never paraphrase.
 */
export declare const VAULT_BYO_NO_PAYLOAD_COPY_STATEMENT: "run402 holds no payload copy of a BYO vault \u2014 only the small signed chain; your primary bucket is the sole copy of your source until you add a second customer-held location";
/**
 * vault-byo-primary-bucket (design D7) — the BYO variant of the
 * `vault_unmirrored` finding's remedy (vault-mirror-default): for a
 * managed vault the remedy is an ADDITIONAL mirror beside the platform's own
 * replicated copy ({@link VAULT_MIRROR_SETUP_HINT}); for a BYO vault the
 * platform holds no payload copy to fall back on
 * ({@link VAULT_BYO_NO_PAYLOAD_COPY_STATEMENT}), so the remedy names a
 * SECOND customer-held location as the only additional copy available — the
 * ordinary `repos mirror` machinery works unchanged against a different
 * destination (design D7). The `vault_unmirrored` finding itself still
 * applies to BYO vaults; only this remedy half is BYO-specific. INERT as of
 * this fold — Phase 2/3 wire the surfaces that print it. Never paraphrase.
 */
export declare const VAULT_BYO_UNMIRRORED_REMEDY_STATEMENT: "add a second customer-held location: 'run402 repos mirror <destination>' works unchanged against a different destination \u2014 for a BYO vault this is your only additional copy, since run402 holds no payload copy of its own";
export declare const VAULT_HEX16_RE: RegExp;
export declare const VAULT_HEX32_RE: RegExp;
export declare const VAULT_SHA256_RE: RegExp;
export declare const VAULT_OID40_RE: RegExp;
export declare const VAULT_VK_RE: RegExp;
export declare const VAULT_EK_RE: RegExp;
export declare const VAULT_SRC_RE: RegExp;
export declare const VAULT_SERVICE_KEY_ID_RE: RegExp;
export declare const VAULT_SIZE_BYTES_RE: RegExp;
/** 32 bytes base64url: 43 chars, final char in the 16-value padding class. */
export declare const VAULT_B64U_32_RE: RegExp;
/** 64 bytes base64url: 86 chars, final char ∈ {A,Q,g,w}. */
export declare const VAULT_B64U_64_RE: RegExp;
/** The HPKE-sealed K_repo: 48 bytes = exactly 64 base64url chars. */
export declare const VAULT_HPKE_CT_RE: RegExp;
export declare const VAULT_TIMESTAMP_RE: RegExp;
/** Canonical base64url (no padding, length mod 4 ≠ 1, zero trailing bits): decode→encode round-trips. */
export declare function isCanonicalBase64url(value: string): boolean;
/** Decode canonical base64url or throw (`VAULT_NONCANONICAL_BASE64URL`). */
export declare function fromBase64url(value: string, field?: string): Uint8Array;
export declare function toBase64url(bytes: Uint8Array): string;
/** RFC 3339 UTC ms `Z` with semantic calendar validation (D187: `2026-02-31` and a non-leap Feb 29 are rejects). */
export declare function isValidVaultTimestamp(value: string): boolean;
/** Format a Date as the protocol timestamp grammar (RFC 3339 UTC, milliseconds, `Z`). */
export declare function formatVaultTimestamp(date?: Date): string;
/** `lp(x)` = 4-byte big-endian UTF-8 byte length ‖ UTF-8 bytes (protocol §1). */
export declare function lp(value: string): Uint8Array;
/** `lp_opt(null)` = `0x00`; `lp_opt(x)` = `0x01` ‖ `lp(x)`. */
export declare function lpOpt(value: string | null): Uint8Array;
/**
 * The protocol-hash seam (vault-native-hash) — the second narrow slot
 * beside the bulk-AEAD one below, same doctrine: the core owns a slot and the
 * Node entry fills it (capability is INJECTED at the entry point, never
 * sniffed in the core), and with nothing registered the `@noble/hashes` path
 * runs byte-for-byte as before. `sha256Hex` is the single chokepoint every
 * protocol hash flows through — frame receipt hashes, plaintext pack checks,
 * stored-bytes preimages, fingerprints, ledger ids — so this one dispatch
 * converts them all. `hkdf`/`hmac` deliberately stay on `@noble`: they
 * consume the hash CONSTRUCTOR, not the digest, and are small-input.
 */
export interface VaultHashBackend {
    /** SHA-256 digest — 32 bytes, byte-identical to `@noble/hashes`'. */
    sha256(bytes: Uint8Array): Uint8Array;
}
/**
 * SDK-INTERNAL (underscore-exported so the byte-equality suite can swap
 * backends). Installing `null` restores the `@noble` default.
 */
export declare function _setVaultHashBackend(backend: VaultHashBackend | null): void;
/** SDK-INTERNAL: which hash backend is live. The isomorphic-entry test asserts `null`. */
export declare function _vaultHashBackend(): VaultHashBackend | null;
export declare function sha256Hex(bytes: Uint8Array): string;
/** CSPRNG bytes from the platform WebCrypto (protocol §2: all keys/nonces/ephemerals from the OS CSPRNG). */
export declare function randomBytes(length: number): Uint8Array;
/** A fresh `<prefix>_<32 lowercase hex>` id (protocol §1: ids are CSPRNG). */
export declare function newVaultId(prefix: string): string;
/** A fresh 32-lowercase-hex scalar (client_creation_id, capture_id, …). */
export declare function newHex32(): string;
/** RFC 8785 canonical JSON (no-numbers profile) as UTF-8 bytes. */
export declare function jcs(value: unknown): Uint8Array;
/** RFC 8785 canonical JSON as a string. */
export declare function jcsString(value: unknown): string;
/**
 * The §1 strict-parse profile: reject ANY JSON number, duplicate members,
 * invalid JSON, and any text that is not byte-for-byte the JCS of its value
 * (member order, whitespace, escapes). Schema-awareness (unknown members,
 * hex case, scalar lengths) is the caller's job.
 */
export declare function parseVaultStrict(text: string): unknown;
/** Classify a strict-parse failure (for vector replay + diagnostics). */
export declare function vaultStrictParseReason(error: unknown): VaultStrictParseReason | null;
/** Stored bytes of a signed object = JCS of the COMPLETE object including `signature`. */
export declare function storedBytes(object: VaultSignedObject): Uint8Array;
/** SHA-256 over stored bytes — what every `*_sha256` naming a signed object means. */
export declare function storedBytesSha256(object: VaultSignedObject): string;
/**
 * Exported so the rotation producer (D195) can build the SAME
 * signature-stripped shape `rotation_id`'s own derivation needs
 * (`sha256(JCS(descriptor minus signature))`) without reaching into this
 * module's private helper twice under two different names.
 */
export declare function vaultWithoutSignature<T extends {
    signature?: unknown;
}>(object: T): Omit<T, "signature">;
/**
 * Signature preimage = `"r402s/v0/" + domain + "\n" + JCS(object without its
 * single top-level signature)`. `domain` is USUALLY a `VaultObjectKind`
 * (an actual `object_kind`-carrying signed object), but the type accepts any
 * string: vault-multi-writer rev 47's `writer_acceptance.statement` reuses
 * this exact byte-construction under the ad-hoc domain
 * `"handoff-writer-accept/v1"` for a sub-object that is NOT itself a
 * `VaultSignedObject` (no `object_kind`/`signature` fields of its own —
 * both signatures over it live on the PARENT `writer_acceptance`). Matches
 * the gateway's own `signaturePreimage(objectKind: string, ...)`, which was
 * never narrower than `string` to begin with.
 */
export declare function signaturePreimage(domain: VaultObjectKind | string, objectWithoutSignature: Record<string, unknown>): Uint8Array;
export declare function ed25519PublicKey(seed: Uint8Array): Uint8Array;
export declare function ed25519Sign(message: Uint8Array, seed: Uint8Array): Uint8Array;
/**
 * Strict RFC 8032 verification: `zip215: false` is PINNED. Non-canonical R or A
 * encodings, non-canonical S, and wrong-length inputs are all `false` — never
 * an exception a caller could mistake for a transport error.
 */
export declare function ed25519VerifyStrict(signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array): boolean;
/** Sign an object under its `object_kind` domain; returns the complete signed object. */
export declare function signVaultObject<T extends {
    object_kind: VaultObjectKind;
}>(objectWithoutSignature: T & {
    signature?: never;
}, seed: Uint8Array): T & {
    signature: string;
};
/**
 * Verify a signed object against a raw public key. Exact scalar lengths are
 * checked FIRST (an 85-char signature or a 42-char key is a schema reject,
 * never a decode-time surprise), then strict Ed25519 under the object's domain.
 */
export declare function verifyVaultObject(object: VaultSignedObject, publicKey: Uint8Array | string): boolean;
/** Verify or throw `VAULT_SIGNATURE_INVALID`. */
export declare function assertVaultSignature(object: VaultSignedObject, publicKey: Uint8Array | string, context?: string): void;
/** First 16 bytes of SHA-256(raw pubkey), lowercase hex (32 chars). */
export declare function keyFingerprintHex(rawPublicKey: Uint8Array): string;
/** `vk_` fingerprint of a raw Ed25519 public key. */
export declare function vkFingerprint(rawPublicKey: Uint8Array): string;
/** `ek_` fingerprint of a raw X25519 public key. */
export declare function ekFingerprint(rawPublicKey: Uint8Array): string;
export declare function generateSigningKeypair(seed?: Uint8Array): VaultSigningKeypair;
export declare function generateEncryptionKeypair(privateKey?: Uint8Array): VaultEncryptionKeypair;
/** `k_obj` info = lp("r402s/v0") ‖ lp(suite) ‖ lp(repo_id) ‖ lp(epoch) ‖ lp(object_kind) ‖ lp(object_id) ‖ lp("L=32"). */
export declare function objectKeyInfo(repoId: string, epoch: string, objectKind: VaultEncryptedObjectKind, objectId: string): Uint8Array;
/** `k_obj = HKDF-SHA-256(ikm=K_repo, salt=∅, info, L=32)`. */
export declare function deriveObjectKey(kRepo: Uint8Array, repoId: string, epoch: string, objectKind: VaultEncryptedObjectKind, objectId: string): Uint8Array;
/** `K_digest` info = lp("r402s/v0") ‖ lp("r402s-1") ‖ lp(repo_id) ‖ lp(epoch) ‖ lp("digest") ‖ lp(label) ‖ lp("L=32"). */
export declare function digestKeyInfo(repoId: string, epoch: string, label: VaultDigestLabel): Uint8Array;
export declare function deriveDigestKey(kRepo: Uint8Array, repoId: string, epoch: string, label: VaultDigestLabel): Uint8Array;
/**
 * The general form `deriveDigestKey` is a fixed-`ikm=K_repo` specialization
 * of (D195/D198/D200, rev 42): every rev-42 rotation label is keyed by the
 * SAMPLED epoch key (`K_e` for a rotation, `K_1` for genesis) instead of
 * `K_repo` — "the value being committed IS the key being distributed"
 * (protocol-v0.md S1). Same HKDF info construction either way.
 */
export declare function deriveDigestKeyFrom(ikm: Uint8Array, repoId: string, epoch: string, label: VaultDigestLabel): Uint8Array;
/** Commitment = HMAC-SHA-256(K_digest(label), JCS(content)), lowercase hex. */
export declare function keyedCommitment(kDigest: Uint8Array, content: unknown): string;
/** The `"objectset"` content shape: `{oids:[sorted unique lowercase 40-hex]}`. */
export declare function objectsetContent(oids: Iterable<string>): {
    oids: string[];
};
/** True iff an `objectset` content is already in canonical (sorted-unique) order. */
export declare function isCanonicalObjectset(content: {
    oids: string[];
}): boolean;
/** The `"snapshot_oid"` content shape. */
export declare function snapshotOidContent(oid: string): {
    oid: string;
    format: "sha1";
};
/** `"r402s/v0/open-id" ‖ lp(org_id) ‖ lp(repo_id) ‖ lp(client_open_id)`. */
export declare function openIdPreimage(orgId: string, repoId: string, clientOpenId: string): Uint8Array;
/** `"r402s/v0/open-binding" ‖ lp(client_open_id) ‖ lp(base_head_sha256) ‖ lp_opt(prior) ‖ lp(requested_r2_cap_size_bytes)`. */
export declare function openBindingPreimage(clientOpenId: string, baseHeadSha256: string, priorCheckpointClaimSetSha256: string | null, requestedR2CapSizeBytes: string): Uint8Array;
/** The seven-member frame AAD, as the object JCS serializes. */
export declare function frameAad(repoId: string, objectKind: VaultEncryptedObjectKind, objectId: string, epoch: string): VaultFrameAad;
/**
 * The bulk-AEAD seam (vault-native-bulk-crypto, design D1/D3).
 *
 * `sealFrame`/`openFrame`/`openFrameWithAad` are the BULK path — every WAL
 * pack, checkpoint pack, `ref_state` and `retention_roots` frame runs through
 * them — and they are SYNC and ISOMORPHIC, so a faster runtime-specific
 * implementation can be neither statically imported (the core must load
 * outside Node) nor lazily awaited (the signatures are sync). So the core owns
 * a slot and the Node entry fills it, exactly as that entry already layers on
 * the keystore and wallet: capability is INJECTED at the entry point, never
 * sniffed in the core.
 *
 * The contract is narrow on purpose (D3): the FRAME primitives only. Envelope
 * scalar unwrap, HPKE, HKDF, HMAC, signing and hashing keep their current
 * implementations — they are microseconds, and a small seam is a small review
 * surface. With nothing registered (every non-Node runtime, and the
 * independent `r402s-verify` lineage, which deliberately registers nothing so
 * it stays a SECOND implementation of the wire format) the `@noble/ciphers`
 * path below runs byte-for-byte as before.
 */
export interface VaultAeadBackend {
    /** XChaCha20-Poly1305 seal → `ct‖tag`, byte-identical to `@noble`'s. */
    seal(key32: Uint8Array, nonce24: Uint8Array, aad: Uint8Array, plaintext: Uint8Array): Uint8Array;
    /** Open `ct‖tag`; `null` on authentication failure — never a throw of its own shape (D4). */
    open(key32: Uint8Array, nonce24: Uint8Array, aad: Uint8Array, ciphertextAndTag: Uint8Array): Uint8Array | null;
}
/**
 * SDK-INTERNAL (underscore-exported so the byte-equality suite can swap
 * backends). Installing `null` restores the `@noble` default — which is what
 * every runtime without a registration already has.
 */
export declare function _setVaultAeadBackend(backend: VaultAeadBackend | null): void;
/** SDK-INTERNAL: which backend is live. The verifier-lineage test asserts `null`. */
export declare function _vaultAeadBackend(): VaultAeadBackend | null;
export interface VaultSealFrameInput {
    /** The per-object key — derive with {@link deriveObjectKey}. */
    k_obj: Uint8Array;
    repo_id: string;
    object_kind: VaultEncryptedObjectKind;
    object_id: string;
    epoch: string;
    plaintext: Uint8Array;
    /** 24-byte nonce; CSPRNG when omitted. Vectors inject it for determinism. */
    nonce?: Uint8Array;
}
/**
 * Seal a plaintext into the §2 frame: `"R402S0"` ‖ `0x01` ‖ nonce(24) ‖ ct‖tag.
 * `ciphertext_sha256` covers the COMPLETE frame. Callers own the single-use
 * rule: one object id, one seal — never re-encrypt under an id.
 */
export declare function sealFrame(input: VaultSealFrameInput): VaultSealedFrame;
export interface VaultOpenFrameInput {
    k_obj: Uint8Array;
    repo_id: string;
    object_kind: VaultEncryptedObjectKind;
    object_id: string;
    epoch: string;
    frame: Uint8Array;
    /** When given, the frame's SHA-256 must equal the receipt BEFORE any decryption (the header is authenticated twice: by the receipt and by the AAD). */
    expected_ciphertext_sha256?: string;
}
/**
 * Open a §2 frame: header checked (magic, suite byte, length), receipt
 * compared when supplied, then the AEAD opened under the seven-member AAD.
 * No unverified streaming release — the whole plaintext or nothing.
 */
export declare function openFrame(input: VaultOpenFrameInput): Uint8Array;
/**
 * The AEAD half of {@link openFrame} with the AAD supplied verbatim — the
 * vector replay uses it to prove every one of the seven AAD members is
 * authenticated (including the constant `suite`/`magic`/`suite_id` members
 * that {@link openFrame}'s typed input cannot vary). Header already checked.
 */
export declare function openFrameWithAad(kObj: Uint8Array, frame: Uint8Array, aad: Uint8Array, objectId?: string): Uint8Array;
/** The one `r402s-1` HPKE suite: Base mode, DHKEM(X25519,HKDF-SHA256) 0x0020 / HKDF-SHA256 0x0001 / ChaCha20-Poly1305 0x0003. */
export declare function vaultHpkeSuite(): CipherSuite;
/** HPKE `info` (D188): lp("r402s/v0/envelope") ‖ lp("r402s-1") ‖ lp(lowerhex(SHA-256(recipient raw X25519 pubkey))) ‖ lp(created_by). */
export declare function envelopeInfo(recipientPublicKeyRaw: Uint8Array, createdBy: string): Uint8Array;
/**
 * HPKE AAD (D188, discriminated by `rotation_id` presence — D203, rev 42):
 * `rotation_id` ABSENT (genesis / ADD-workaround envelope) -> the rev-41
 * four-field form, BYTE-IDENTICAL to every existing golden HPKE digest;
 * `rotation_id` PRESENT (a rotation-attempt envelope) -> the new five-field
 * form. Field ORDER in the returned object does not matter (JCS sorts
 * object members), but `rotation_id` must never be included as an explicit
 * `null` — the two shapes are genuinely different objects, not one object
 * with an optional-null member (protocol-v0.md S2).
 */
export declare function envelopeAad(repoId: string, epoch: string, recipientFingerprint: string, rotationId?: string | null): VaultEnvelopeAad;
export interface VaultHpkeSealInput {
    recipient_public_key: Uint8Array;
    info: Uint8Array;
    aad: Uint8Array;
    plaintext: Uint8Array;
    /** RFC 9180 §7.1.3 `DeriveKeyPair(ikmE)` injection — TEST/vector determinism only; production seals use the OS CSPRNG. */
    ikm_e?: Uint8Array;
}
/** Single-shot HPKE seal (Base mode) returning `{enc, ct}`. */
export declare function hpkeSeal(input: VaultHpkeSealInput): Promise<{
    enc: Uint8Array;
    ct: Uint8Array;
}>;
export interface VaultHpkeOpenInput {
    recipient_private_key: Uint8Array;
    enc: Uint8Array;
    info: Uint8Array;
    aad: Uint8Array;
    ct: Uint8Array;
}
/** Single-shot HPKE open; any failure is `VAULT_HPKE_OPEN_FAILED` (never garbage plaintext). */
export declare function hpkeOpen(input: VaultHpkeOpenInput): Promise<Uint8Array>;
export interface VaultSealKeyEnvelopeInput {
    /** The raw 32-byte secret being distributed — `K_repo`/`K_1` at genesis, `K_e` for a rotation. */
    k_repo: Uint8Array;
    repo_id: string;
    epoch: string;
    /** The recipient's raw X25519 public key. */
    recipient_public_key: Uint8Array;
    /** The creator's signing keypair; its `vk_` fingerprint becomes `created_by`. */
    signer: VaultSigningKeypair;
    created_at: string;
    /**
     * PRESENT for a rotation-attempt envelope (D195/D196, rev 42): widens the
     * path to `(repo_id, epoch, rotation_id, recipient_fingerprint)` and the
     * HPKE AAD to the five-field form. ABSENT/omitted for a genesis/ADD-
     * workaround envelope — byte-identical to rev 41.
     */
    rotation_id?: string | null;
    /** Vector determinism only. */
    ikm_e?: Uint8Array;
}
/** Seal + sign a `key_envelope` for one principal; returns the object with its stored-bytes identity + genesis receipt. */
export declare function sealKeyEnvelope(input: VaultSealKeyEnvelopeInput): Promise<VaultSealedKeyEnvelope>;
export interface VaultOpenKeyEnvelopeInput {
    envelope: VaultKeyEnvelope;
    /** The recipient's raw X25519 keypair (the public half feeds `info`; the fingerprint must match the path identity). */
    recipient: VaultEncryptionKeypair;
    /** The creator's raw Ed25519 public key — the envelope signature is verified BEFORE any open. */
    signer_public_key: Uint8Array | string;
}
/**
 * Open a `key_envelope`: exact-scalar checks → signature (strict, domain
 * `key_envelope`) → path identity (recipient fingerprint = ours) → HPKE open
 * with the D188 info/AAD (widened to the five-field form when the envelope
 * itself carries `rotation_id` — D195/D203). Returns the raw 32-byte secret
 * (`K_repo`/`K_1` at genesis, `K_e` for a rotation).
 */
export declare function openKeyEnvelope(input: VaultOpenKeyEnvelopeInput): Promise<Uint8Array>;
/**
 * Decode + validate one head's `transition.payload` as a `rotate_epoch_payload`
 * (D193-D203, rev 42): base64url-jcs decode, `payload_sha256` self-check,
 * strict JSON parse, and the payload's OWN `new_epoch` agreeing with the
 * carrying head's `epoch` field (a second, independent check over the SIGNED
 * payload bytes — separate from {@link checkChainLink}'s epoch-continuity
 * check on the head's wire `epoch` field itself, protocol-v0.md S4.3). Pure
 * and keyless: this never opens an envelope, so every reader can call it
 * (including the fully keyless chain walk) before any key material is needed.
 */
export declare function parseRotateEpochPayload(head: {
    generation: string;
    epoch: string;
    transition: {
        kind: string;
        payload_format: string;
        payload: string;
        payload_sha256: string;
    } | null;
}): VaultRotateEpochPayload;
/**
 * Decode + validate one head's `transition.payload` as an
 * `add_writer_key_payload` (vault-multi-writer rev 47, protocol §4.16):
 * base64url-jcs decode, `payload_sha256` self-check, strict JSON parse, and
 * the payload's OWN `repo_id` agreeing with the carrying head's — mirrors
 * {@link parseRotateEpochPayload} exactly, `repo_id` in place of `epoch`
 * (this transition never changes the epoch). Pure and keyless: the FULL
 * cryptographic + structural validation (signatures, base/next writer-set
 * commitments, handoff-grant binding) is `validateAddWriterKeyPayload`'s job
 * (`../node/vault-writer-state.js`) — this function only gets the bytes
 * off the wire safely, so every reader can call it before any key material
 * is needed.
 */
export declare function parseAddWriterKeyPayload(head: {
    generation: string;
    repo_id: string;
    transition: {
        kind: string;
        payload_format: string;
        payload: string;
        payload_sha256: string;
    } | null;
}): VaultAddWriterKeyPayload;
/**
 * Open ONE `rotate_epoch` transition's own `key_envelope` for
 * `own_fingerprint` and return the derived `K_e`, after verifying it
 * reproduces the payload's `epoch_key_commitment` (D200's per-recipient
 * self-check — never a global set-coherence proof, §14ao/§2). Fails CLOSED
 * with `VAULT_EPOCH_NOT_OPENABLE` — never a bare `VAULT_AEAD_AUTH_FAILURE`
 * — either when this recipient is not among the rotation's `envelopes[]` at
 * all, or when the addressed envelope's stored bytes are absent or altered.
 *
 * Transport-agnostic by design (`get_envelope_bytes` + `envelope_path`
 * callbacks): this is the ONE place a reader opens a rotation envelope, and
 * both the live publication read path (`Vault.verifyToNewest`, over
 * the gateway transport) and the offline mirror recovery path
 * (`recoverVaultMirror`, over a `VaultMirrorBackend`) call it —
 * differing only in HOW they fetch bytes, never in the open/verify logic.
 */
export declare function openEpochRotationForRecipient(input: {
    repo_id: string;
    payload: VaultRotateEpochPayload;
    own_fingerprint: string;
    own_encryption_keypair: VaultEncryptionKeypair;
    writer_signing_public_key: Uint8Array | string;
    get_envelope_bytes: (path: string) => Promise<Uint8Array | null | undefined>;
    envelope_path: (epoch: string, recipient_fingerprint: string, rotation_id: string) => string;
}): Promise<Uint8Array>;
export interface VaultBuildGenesisInput {
    repo_id: string;
    org_id: string;
    project_id: string;
    allocation_nonce: string;
    creator_signing: VaultSigningKeypair;
    creator_encryption_public_key: Uint8Array;
    envelope_receipt: VaultKeyEnvelopeReceipt;
    created_at: string;
}
/** Build + sign a `vault_genesis` (generation 0, epoch 1, the creator's single envelope). */
export declare function buildVaultGenesis(input: VaultBuildGenesisInput): VaultGenesis;
/** A named genesis key-binding failure (semantic constraints `genesis-key-bindings` / `envelope-receipt-binding`). */
export type VaultGenesisBindingProblem = "envelope_count" | "envelope_epoch" | "envelope_recipient_fingerprint" | "writer_key_id" | "stored_envelope_hash" | "stored_envelope_size" | "stored_envelope_repo_id" | "stored_envelope_epoch" | "stored_envelope_recipient_fingerprint" | "stored_envelope_created_by";
/**
 * The genesis key bindings (round-10 A6): exactly one envelope at epoch 1 whose
 * `recipient_fingerprint` is the creator's `ek_`; `writer_key_id` is the
 * creator's `vk_`; and, when the stored envelope is supplied, the receipt's
 * hash/size name its stored bytes and its path identity + `created_by` equal
 * the genesis values. Schema-valid-but-protocol-invalid objects surface here.
 */
export declare function checkGenesisKeyBindings(genesis: VaultGenesis, storedEnvelope?: VaultKeyEnvelope): VaultGenesisBindingProblem[];
export interface VaultBuildRecoveryReceiptInput {
    repo_id: string;
    org_id: string;
    project_id: string;
    genesis_sha256: string;
    creator_signing: VaultSigningKeypair;
    creator_encryption_public_key: Uint8Array;
}
/** Build + sign the creator's `recovery_receipt` (emitted at ACTIVE; integrity data, not a secret). */
export declare function buildRecoveryReceipt(input: VaultBuildRecoveryReceiptInput): VaultRecoveryReceipt;
export type VaultRecoveryReceiptProblem = "signature" | "genesis_sha256" | "repo_id" | "org_id" | "project_id" | "creator_signing_fingerprint" | "creator_encryption_fingerprint";
/**
 * Check a recovery receipt against a genesis it claims to pin: the receipt's
 * signature (under the genesis's creator key), the genesis stored-bytes hash,
 * and every identity field. A substituted, internally consistent vault fails
 * on `genesis_sha256` — exactly the fabricated-vault scenario.
 */
export declare function checkRecoveryReceipt(receipt: VaultRecoveryReceipt, genesis: VaultGenesis): VaultRecoveryReceiptProblem[];
/**
 * Field-level checks on an `allocation` for THIS creation attempt (the
 * control-plane signature itself is verified through the service-key registry,
 * which task 5.4 wires; pass `service_public_key` once it is pinned).
 *
 * `expected.org_id` / `expected.project_id` are OPTIONAL (repo-first-onramp
 * task 4.5's push-to-create addition): a push-to-create attempt does not know
 * its org/project ahead of the allocation response (only `org_slug`/
 * `repo_name`), so those two fields are trust-on-first-use — the caller skips
 * the comparison on the FIRST allocation of a fresh push-to-create journal and
 * pins whatever the (owner-authorized, signature-verified) allocation says,
 * then compares against that pin on every later call for the SAME journal.
 * Every other field is checked unconditionally, same as before.
 */
export declare function checkAllocation(allocation: VaultAllocation, expected: {
    client_creation_id: string;
    creator_signing_fingerprint: string;
    creator_encryption_fingerprint: string;
    org_id?: string;
    project_id?: string;
}, servicePublicKey?: Uint8Array | string): string[];
/** 16-hex epochs compare/increment as unsigned integers (chain.ts's `nextGeneration`, mirrored). */
export declare function epochValue(hex16: string): bigint;
export declare function nextEpoch(hex16: string): string;
export interface VaultIncludedPair {
    principal_id: string;
    ek_fingerprint: string;
}
/**
 * D196: `target_partition_digest = SHA-256(JCS({recipient_state_version,
 * recipient_revocation_version, pin_manifest_version, pin_manifest_sha256,
 * included: [sorted {principal_id, ek_fingerprint}], excluded_keyless_principal_ids:
 * [sorted], excluded_unconfirmed_principal_ids: [sorted]}))`. The producer
 * calls this ONCE to build the value it embeds in both the descriptor
 * (D195) and the payload (D196); the gateway recomputes the SAME formula
 * twice server-side and refuses on any disagreement — so a bug here is a
 * `RECIPIENT_SET_MISMATCH` at admission time, not a silent forgery.
 */
export declare function computeTargetPartitionDigest(input: {
    recipient_state_version: string;
    recipient_revocation_version: string;
    pin_manifest_version: string;
    pin_manifest_sha256: string;
    included: readonly VaultIncludedPair[];
    excluded_keyless_principal_ids: readonly string[];
    excluded_unconfirmed_principal_ids: readonly string[];
}): string;
/**
 * D195: `rotation_id = lowerhex(SHA-256(JCS(the complete descriptor object,
 * MINUS signature)))` — the full 64-hex digest, computed over the descriptor
 * INCLUDING `attempt_key_commitment` (a field ON the descriptor). Callers
 * pass the SIGNED descriptor (or the unsigned one — the signature member is
 * stripped either way, matching the gateway's `computeRotationId`).
 */
export declare function computeRotationId(descriptor: Record<string, unknown> & {
    signature?: unknown;
}): string;
/**
 * D195's `attempt_key_commitment` preimage: the descriptor's OWN fields
 * minus `attempt_key_commitment` itself (and minus `signature`, which does
 * not exist yet at this point in the build) — `rotation_id` is never a
 * member of the descriptor object at all (it is the object's derived path
 * key), so there is nothing to additionally strip for it. Noncircular by
 * construction: `attempt_key_commitment` must exist before `rotation_id`
 * can be computed FROM the complete descriptor.
 */
export declare function attemptKeyCommitmentPreimage(descriptorFields: {
    format: VaultFormat;
    object_kind: "rotation_attempt_descriptor";
    suite: VaultSuite;
    repo_id: string;
    base_head_sha256: string;
    new_epoch: string;
    recipient_state_version: string;
    recipient_revocation_version: string;
    pin_manifest_sha256: string;
    target_partition_digest: string;
    client_idempotency_key: string;
    writer_key_id: string;
}): Record<string, unknown>;
/**
 * D195/§1's `"epoch_rotation_attempt"` commitment: `HMAC-SHA-256(K_digest(
 * "epoch_rotation_attempt", repo_id, new_epoch), JCS(the descriptor's own
 * fields minus attempt_key_commitment), ikm=K_e)` — keyed by the SAMPLED
 * epoch key being distributed, not `K_repo` (D195/D200/§1: "every one keyed
 * by the SAMPLED epoch key... since the value being committed IS the key
 * being distributed"). `K_digest`'s own `epoch` parameter is `new_epoch`
 * (this rotation's OWN epoch) — the natural, and only internally-consistent,
 * reading of §1's generic formula for a value that is a property of the new
 * epoch's key; there is no gateway-side computation of this value to cross-
 * check against (the gateway never sees `K_e`), so this is the SDK's own
 * considered wire-shape decision, stated here rather than guessed silently.
 */
export declare function attemptKeyCommitment(kE: Uint8Array, repoId: string, newEpoch: string, descriptorFields: Parameters<typeof attemptKeyCommitmentPreimage>[0]): string;
/**
 * D200/§1's `"epoch_rotation"` commitment: `HMAC-SHA-256(K_digest(
 * "epoch_rotation", repo_id, new_epoch), JCS({rotation_id, fingerprints:
 * [sorted pairwise-distinct]}), ikm=K_e)`. Proves a PER-RECIPIENT self-check
 * only (D200's narrowed claim) — never global set-coherence. A recipient
 * who opens their own envelope recovers `K_e` + `epoch` (== `new_epoch`)
 * directly from the envelope itself, so they can recompute this independent
 * of the descriptor/payload — confirming `new_epoch` is the right `K_digest`
 * epoch parameter (the same value both the producer and every recipient
 * arrive at without coordination).
 */
export declare function epochRotationKeyCommitment(kE: Uint8Array, repoId: string, newEpoch: string, rotationId: string, fingerprints: readonly string[]): string;
/**
 * D198's genesis-specific `"vault_genesis_epoch_key"` commitment — the same
 * per-recipient-self-check discipline applied to an N-recipient genesis,
 * with its own noncircular preimage (no `rotation_id` exists at generation
 * zero). Not built by this SDK's genesis path today (single-recipient
 * genesis only); exported for vector generation / future N-recipient
 * genesis support.
 */
export declare function genesisEpochKeyCommitment(k1: Uint8Array, repoId: string, fingerprints: readonly string[]): string;
/**
 * D195's producer obligation the round-3 review's own decisive witness
 * forces: "a producer that cannot prove it is resuming its OWN prior
 * in-flight attempt... MUST mint a fresh `client_idempotency_key`... before
 * uploading any envelope." The corollary — and what actually PREVENTS the
 * complementary-path mosaic client-side, before the descriptor's create-only
 * CAS even gets a chance to catch it server-side — is that a fresh K_e must
 * never equal any epoch key this client has ever locally held (constant-time
 * comparison; a match is evidence of a broken CSPRNG or a reused seed, not a
 * legitimate resume, since a genuine resume reuses the SAME
 * client_idempotency_key AND therefore builds the identical descriptor, not
 * merely the same K_e in isolation).
 */
export declare function checkFreshEpochKeyAgainstPriorKeys(kE: Uint8Array, priorKeys: Iterable<Uint8Array>): void;
export type VaultHPartitionVerdict = {
    ok: true;
} | {
    ok: false;
    detail: string;
};
/**
 * D196's pair-level bijection, mirrored client-side as a PRE-SUBMIT
 * self-check (the gateway's own recomputation under its live desired-state
 * lock remains the authoritative check — this exists so a client-side
 * partition bug surfaces as a clear local error instead of a round trip
 * that ends in `RECIPIENT_SET_MISMATCH`).
 */
export declare function checkHPartition(input: {
    desiredPrincipalIds: ReadonlySet<string>;
    keyedPrincipalIds: ReadonlySet<string>;
    pinnedFingerprintOf: ReadonlyMap<string, string>;
    included: readonly VaultIncludedPair[];
    excludedKeylessPrincipalIds: readonly string[];
    excludedUnconfirmedPrincipalIds: readonly string[];
    /**
     * Rotation inclusion by writer set (kygit-handoff's member-removal
     * decision, mirrored from the gateway's own check): a keyed principal in
     * H whose signing key is an active writer on the chain and survives this
     * rotation is included on its CURRENT directory fingerprint, pin or no
     * pin, and may never sit in `excluded_unconfirmed`. A live pin still
     * governs a pinned principal exactly as before. Absent means the pin-only
     * rule.
     */
    survivingWriterPrincipalIds?: ReadonlySet<string>;
    directoryFingerprintOf?: ReadonlyMap<string, string>;
}): VaultHPartitionVerdict;
/** D197's full-map conservation rule: `next_map = prior_map + receipt-authorized additions/replacements`, deletion-by-omission forbidden. */
export declare function checkPinManifestConservation(prior: readonly {
    principal_id: string;
}[], next: readonly {
    principal_id: string;
}[]): VaultHPartitionVerdict;
/** The synthetic ledger id a path-addressed `key_envelope` gets — mirrors the gateway's `keyEnvelopeLedgerId` exactly (drift here breaks receipt pairing at upload finalize). */
export declare function keyEnvelopeLedgerId(epoch: string, fingerprint: string, rotationId: string | null): string;
export declare function pinManifestLedgerId(pinManifestVersion: string): string;
export declare const SOURCE_WRAP_BLOB_PREFIX = "swrap2_";
export declare const SOURCE_RC_DISPLAY_PREFIX = "SRC1";
export type SourceWrapperKind = "webauthn_prf" | "recovery_code";
export interface SourceWrapperContextFields {
    /** The seal-time ceremony host (`location.hostname` of the sealing page — `console.run402.com` for every wrapper sealed today). */
    rp_id: string;
    principal_id: string;
    encryption_key_id: string;
    wrapper_id: string;
    kind: SourceWrapperKind;
    /** WebAuthn `public_subject` for `webauthn_prf` wrappers; always null in the JCS for `recovery_code`. */
    credential_subject: string | null;
    /** The raw 32-byte member public key — the context binds its FULL SHA-256, never the truncated `ek_` fingerprint. */
    member_public_key: Uint8Array;
}
/** The pinned canonical context for one wrapper — BOTH the KEK HKDF-info suffix AND the AEAD AAD. */
export declare function buildSourceWrapperContext(fields: SourceWrapperContextFields): Uint8Array;
/**
 * Open a `swrap2_...` wrapper blob back into the 32-byte member scalar.
 * `ikm` is the UTF-8 bytes of the normalized code CORE (`recovery_code`) or
 * the raw PRF output (`webauthn_prf` — only ever exercised by browser
 * surfaces; offline recovery refuses raw PRF as an input by policy, at the
 * recover layer). A failed AEAD is `WRAPPER_DID_NOT_OPEN` — the truthful
 * cause-neutral error: wrong code, corrupt blob, and wrong context (rp_id
 * included) are indistinguishable here. The caller MUST compare the derived
 * FULL public key against the published one before trusting the scalar.
 */
export declare function openSourceWrapper(input: {
    kind: SourceWrapperKind;
    ikm: Uint8Array;
    blob: string;
    context: Uint8Array;
}): Uint8Array;
/**
 * Pinned normalization (ONE canonical accepted form): uppercase; strip every
 * char outside [0-9A-Z]; map I→1, L→1, O→0; drop a leading "SRC1" when the
 * result is 37 chars; require exactly 33 chars; validate the check character
 * (`RECOVERY_CODE_CHECKSUM_INVALID` — a local typo, caught before any KEK
 * derivation or wrapper read). Returns the 32-char CORE (the KEK ikm).
 */
export declare function normalizeSourceRecoveryCode(input: string): string;
export { bytesToHex, hexToBytes };
/**
 * The client half of the gateway's ECDH key-confirmation challenge for a
 * `keystore_v1` enrollment (`POST /agent/v1/whoami/encryption-key` →
 * `activation.{challenge_id, epk}`; `POST …/encryption-key/activate`
 * `{challenge_id, proof}`). The server minted an ephemeral X25519 pair,
 * computed HMAC-SHA256(ECDH(esk, our public key), message) and stored only
 * its SHA-256; a holder of the keystore's private scalar reproduces the HMAC
 * from `epk`. Byte-identical to the gateway's `keyPossessionMessage` /
 * `mintKeyPossessionChallenge` (services/principal-encryption-keys.ts) —
 * the prefix + JCS message are pinned here so the two lineages cannot drift.
 * Client convention, not r402s/v0 wire protocol.
 */
export declare const VAULT_KEYSTORE_POSSESSION_PREFIX = "r402s/v0/keystore-key-possession/v1\n";
export declare function computeKeystorePossessionProof(input: {
    private_key: Uint8Array;
    epk_b64u: string;
    challenge_id: string;
    encryption_key_id: string;
    public_key_b64u: string;
}): string;
/**
 * vault-multi-writer rev 47 (design D9) — the writer-signing-key
 * possession message. Byte-identical to the gateway's own
 * `signingKeyPossessionMessage` (services/principal-encryption-keys.ts) —
 * `JSON.stringify({domain, principal_id, signing_pubkey, encryption_pubkey})`
 * in EXACTLY this field order, verified by reading that function's source
 * directly rather than trusting its own comment (which claims "sorted
 * keys" — this order is NOT alphabetically sorted; only the byte-for-byte
 * match to what the gateway actually re-derives at verify time matters, and
 * `JSON.stringify` is deterministic for a fixed insertion order regardless
 * of whether that order happens to be alphabetical). Deliberately plain
 * `JSON.stringify`, not `jcs()` — the RFC 8785 canonicalizer would SORT
 * these four keys into a DIFFERENT order (`domain, encryption_pubkey,
 * principal_id, signing_pubkey`) and silently produce a signature that
 * never verifies.
 */
export declare const VAULT_SIGNING_KEY_POSSESSION_DOMAIN = "r402s/v0/signing-key-possession/v1";
export declare function signingKeyPossessionMessage(fields: {
    principal_id: string;
    signing_pubkey: string;
    encryption_pubkey: string;
}): Uint8Array;
/** Ed25519 signature over {@link signingKeyPossessionMessage} — proves possession of the signing half's private key over this specific (principal, signing key, encryption key) binding. */
export declare function computeSigningKeyPossessionSignature(input: {
    signing_seed: Uint8Array;
    principal_id: string;
    signing_pubkey: string;
    encryption_pubkey: string;
}): string;
//# sourceMappingURL=vault.crypto.d.ts.map