/** * The vault's connection and keys: a read-write device connection, the * account master key, this device's certificate, and the stream data keys. * * Owner decision (2026-10-01): adding a device needs only `cleo login` and * the one-time browser approval; that approval also hands the device the * encryption key. So the account master key (MK) is escrowed on Cleo Nexus: * * 1. `GET /v1/account/keys/escrow` returns MK sealed to this device's X25519 * key (`sealTo`), released only to an approved, active device. The client * opens it with the device's private key and checks it against the * escrow's verifier. * 2. No escrow and no account keys: this is the account's first vault use. * The client mints MK and escrows it (`PUT`, insert-only). A concurrent * first device loses the insert with 409 and reads the winner's key. * Since T13100 `cleo login nexus` runs steps 1 to 4 right after enrolment * ({@link provisionNexusAccount}); `cleo cloud push` still does them for a * device that logged in before that. * 3. The device certifies itself under MK (a self-grant, `PUT * /v1/devices/:id/key`), so other devices can verify its snapshots. * 4. Signer trust comes from `certifiedSigners` over `GET /v1/devices/trust`, * with the trust state persisted in {@link NexusVaultState}. * * Threat model (cleo-nexus T082): the escrow `PUT` sends MK to the server * over TLS, and the server stores it encrypted under a server-held key * (`KEY_ESCROW_SECRET`). Snapshots are therefore encrypted at rest with keys * Cleo Nexus can recover: this is server-escrowed encryption, not * zero-knowledge end-to-end encryption. What the escrow does guarantee is * that MK is released only to an approved, active device of the account, * sealed to that device's key. * * A project's data key (PDK) is minted by its first pusher and stored * wrapped by MK (`PUT /v1/projects/:id/keys/:userId`); the home stream key * is derived from MK. * * No function here logs a key, and no error carries one. * * @task T12336 * @task T13100 * @epic T12322 */ import type { CloudWarning, NexusAccountSetup } from '@cleocode/contracts'; import { type KeyPair } from './crypto.js'; import { type FetchLike, Http, type ResponseSchema } from './http.js'; import { type TrustedSigners } from './keys.js'; import { resolveNexusApiUrl } from './nexus-auth.js'; import { type NexusCloudOptions } from './nexus-cloud.js'; import { type SealedNexusDevice } from './nexus-device.js'; import { NexusVaultState } from './nexus-vault-state.js'; /** Timeout of a snapshot blob upload or download. */ export declare const NEXUS_VAULT_BLOB_TIMEOUT_MS: number; /** The sealed-box context of an escrow release (must match the server, cleo-nexus T082). */ export declare const escrowContext: (userId: string, deviceId: string) => string; /** Options of every vault command. */ export interface NexusVaultOptions extends NexusCloudOptions { /** Vault state store (tests). */ vaultState?: NexusVaultState; /** Timeout of a blob transfer; default {@link NEXUS_VAULT_BLOB_TIMEOUT_MS}. */ blobTimeoutMs?: number; } /** A read-write connection with this machine's device credential and keys. */ export interface NexusVaultConnection { readonly apiUrl: string; readonly userId: string; readonly deviceId: string; readonly device: SealedNexusDevice; /** Device keys: X25519 for sealed boxes, Ed25519 for signatures. */ readonly keys: { encryption: KeyPair; signing: KeyPair; }; /** The authenticated client (sends `X-Cleo-Device-Id`). For the journal. */ readonly http: Http; /** `fetch` for presigned blob transfers (long timeout, no credential). */ readonly blobFetch: FetchLike; readonly state: NexusVaultState; readonly warnings: CloudWarning[]; /** A request whose failure stays a raw {@link NexusError} (callers handle server codes). */ raw(method: string, path: string, schema: ResponseSchema, body?: unknown): Promise; /** {@link NexusVaultConnection.raw}, with failures mapped to {@link NexusAccountError}. */ call(method: string, path: string, schema: ResponseSchema, body?: unknown): Promise; /** `GET`, mapped; a 404 answers `null`. */ find(path: string, schema: ResponseSchema): Promise; } /** * Open a read-write vault connection with this machine's device credential. * * @param opts - API URL, account, stores and test overrides. * @returns The connection. * @throws {NexusAccountError} Not signed in, device mode off, or no device keys. */ export declare function connectNexusVault(opts?: NexusVaultOptions): Promise; /** The unlocked account key and the signers this device trusts. */ export interface NexusAccountKey { masterKey: Buffer; keyVersion: number; signers: TrustedSigners; } /** * Get the account master key for this device, minting and escrowing it on * the account's first vault use, and certify this device under it. * * @param conn - A vault connection. * @param opts - `readOnly`: never mint, escrow or certify (GET only); a missing escrow is * `E_NEXUS_VAULT_EMPTY`. * @returns The key, its version and the trusted signers. * @throws {NexusAccountError} `E_NEXUS_VAULT_KEY_UNAVAILABLE` when the key * cannot be obtained or verified; `E_NEXUS_VAULT_UNSUPPORTED` when the server * has no key escrow route; a mapped API error otherwise. */ export declare function unlockNexusAccountKey(conn: NexusVaultConnection, opts?: { readOnly?: boolean; }): Promise; /** Options of {@link provisionNexusAccount}. */ export interface ProvisionNexusAccountOptions extends NexusVaultOptions { /** * The device entry to act as. `cleo login nexus` passes the entry it just * stored, so the setup runs on that new device credential (the escrow routes * accept only device credentials). Default: the stored credential, as * {@link connectNexusVault} finds it. */ device?: SealedNexusDevice; } /** * Make the account ready for encrypted backups on this device (onboarding A, * T13100): read the escrowed account master key, or mint and escrow it when * the account has none (a 409 means another device won: its key is read, * never re-minted), then certify this device under it and record the signer * trust state in {@link NexusVaultState}. It is {@link unlockNexusAccountKey} * with a report, so a later `cleo cloud push` finds everything in place and * mints nothing; it is idempotent. * * It never throws: a server without key escrow answers `unsupported`, any * other failure answers `failed` with the step and the remedy. No key is in * the result, and a failure message is redacted of the device credential. * * @param opts - API URL, stores, the device to act as, and test overrides. * @returns What the setup did. */ export declare function provisionNexusAccount(opts?: ProvisionNexusAccountOptions): Promise; /** * The data key of a project stream: unwrap the newest wrapped key, or mint * and store one when the project has none yet (its first push). * * @param conn - A vault connection. * @param mk - The unlocked master key. * @param projectId - The server's project id. * @param mint - Whether a missing key may be created (push: yes; restore: no). * @returns The project data key, or `null` when none exists and `mint` is false. */ export declare function nexusProjectDataKey(conn: NexusVaultConnection, mk: Buffer, projectId: string, mint: boolean): Promise; /** * The data key of the account's `home:` stream (derived from MK, never stored). * * @param mk - The unlocked master key. * @returns The home stream key. */ export declare function nexusHomeDataKey(mk: Buffer): Buffer; /** Re-export for callers that only need the URL resolver. */ export { resolveNexusApiUrl }; //# sourceMappingURL=nexus-vault-keys.d.ts.map