/**
 * Encrypted secret storage and forward-secret sync protocol.
 *
 * At-rest encryption:
 *   - Master key stored in OS keychain (macOS Keychain, Linux Secret Service)
 *   - File fallback for headless servers (.instar/machine/secrets-master.key, 0600)
 *   - AES-256-GCM encryption of secret store
 *
 * Wire encryption (for sync between machines):
 *   - Ephemeral X25519 ECDH key exchange
 *   - HKDF-SHA256 key derivation
 *   - AES-256-GCM authenticated encryption
 *   - Forward secrecy: ephemeral keys discarded after each transfer
 *
 * Part of Phase 4 (secret sync via tunnel).
 */
import crypto from 'node:crypto';
/** Per-agent keychain account: scoped by the absolute stateDir so two agents
 *  on one machine can never clobber each other's master key. Readable in
 *  Keychain Access (the path IS the identity — debuggable, collision-free). */
export declare function perAgentKeychainAccount(stateDir: string): string;
/** The 8-byte key fingerprint stored in the v2 header. */
export declare function keyIdOf(key: Buffer): Buffer;
/** Injectable keychain operations (per service+account). Production uses the
 *  real OS keychain (macOS `security` / Linux `secret-tool`); tests inject a
 *  fake so the per-agent/adoption logic is unit-testable WITHOUT touching a
 *  real keychain (the VITEST file-key guard stays in force when this is not
 *  injected — an injected fake is, by definition, not the real keychain). */
export interface KeychainOps {
    read: (service: string, account: string) => Buffer | null;
    /** Returns true on success. */
    write: (service: string, account: string, key: Buffer) => boolean;
}
export interface SecretStoreConfig {
    /** State directory (.instar) */
    stateDir: string;
    /** Force file-based key storage (skip keychain) */
    forceFileKey?: boolean;
    /** Test seam — see KeychainOps. Absent in production (real keychain used). */
    keychainOps?: KeychainOps;
}
/** The decrypted secrets object (flat key-value or nested) */
export type Secrets = Record<string, unknown>;
/** Encrypted payload for wire transfer */
export interface EncryptedSecretPayload {
    /** Ephemeral X25519 public key (base64) */
    ephemeralPublicKey: string;
    /** AES-256-GCM initialization vector (base64) */
    iv: string;
    /** Encrypted ciphertext (base64) */
    ciphertext: string;
    /** AES-GCM authentication tag (base64) */
    tag: string;
}
/**
 * Store/retrieve the master key from the OS keychain.
 * Falls back to file-based storage if keychain is unavailable.
 */
export declare class MasterKeyManager {
    private stateDir;
    private forceFile;
    private keyFilePath;
    private kc;
    constructor(stateDir: string, forceFile?: boolean, keychainOps?: KeychainOps);
    /**
     * Retrieve or generate the master key. Resolution order (vault-key-coherence,
     * CMT-1038 — the per-agent fix for the machine-global-slot bifurcation):
     *   1. the PER-AGENT keychain entry (`master-key:<stateDir>`);
     *   2. the LEGACY global entry (`master-key`) — ADOPTED on sight: copied into
     *      the per-agent account (the global entry is left in place for other
     *      not-yet-migrated agents; this code never writes it again);
     *   3. the per-agent file key (generating one — and writing the PER-AGENT
     *      keychain entry, never the global one — when none exists anywhere).
     */
    getMasterKey(): Buffer;
    /**
     * Every key this agent could plausibly have encrypted its store with, primary
     * first: [resolved primary, the OTHER source's key if it exists and differs].
     * Read-only — alternates NEVER generate. Backs the dual-key read fallback
     * (a v1 store written before the sources diverged stays readable, loudly).
     */
    getCandidateKeys(): Array<{
        key: Buffer;
        source: 'keychain' | 'file';
    }>;
    /** Whether the master key is stored in the OS keychain (vs file fallback). */
    get isKeychainBacked(): boolean;
    private tryRead;
    private readOsKeychain;
    private writeOsKeychain;
    private getFileKey;
}
export declare class SecretStore {
    private stateDir;
    private keyManager;
    private encryptedPath;
    /** Which key source decrypted the LAST successful read() — observability for
     *  the dual-key fallback ('keychain' | 'file'; null before any read). */
    lastReadKeySource: 'keychain' | 'file' | null;
    constructor(config: SecretStoreConfig);
    /**
     * Read and decrypt secrets from the encrypted store. Returns empty object if
     * no secrets exist.
     *
     * Vault-key-coherence (CMT-1038): tries every candidate key (primary first,
     * then the OTHER source — keychain↔file) so a store written before the key
     * sources diverged stays READABLE instead of looking "empty" to half the
     * readers (the 2026-06-05 bifurcation incident). A v2 file's keyId header
     * names the required key, so wrong-key is a precise error distinct from
     * corruption. Success via a non-primary key emits a loud degradation report;
     * the next write() converges the store back to the primary key.
     */
    read(): Secrets;
    /** Record the source that decrypted; loudly report a non-primary success. */
    private noteReadKeySource;
    /** Encrypt and write secrets to the store (v2 format — keyId header). */
    write(secrets: Secrets): void;
    /** Get a specific secret by dot-notation path (e.g., 'telegram.token'). */
    get(keyPath: string): unknown;
    /** Set a specific secret by dot-notation path. */
    set(keyPath: string, value: unknown): void;
    /** Delete a specific secret by dot-notation path. */
    delete(keyPath: string): void;
    /** Whether the secret store file exists. */
    get exists(): boolean;
    /** Whether the master key is in the OS keychain. */
    get isKeychainBacked(): boolean;
    /** Delete the encrypted store file. */
    destroy(): void;
    private encryptAES;
    private decryptAES;
}
/**
 * Encrypt secrets for wire transfer using forward-secret ECDH.
 *
 * Protocol:
 * 1. Generate ephemeral X25519 key pair
 * 2. ECDH: ephemeral private + recipient's long-term public = shared secret
 * 3. HKDF-SHA256 to derive AES-256 key
 * 4. AES-256-GCM encrypt
 * 5. Return: { ephemeralPublicKey, iv, ciphertext, tag }
 *
 * The ephemeral private key is not retained — forward secrecy.
 */
export declare function encryptForSync(secrets: Secrets, recipientPublicKeyBase64: string): EncryptedSecretPayload;
/**
 * Decrypt secrets received via wire transfer.
 *
 * Protocol:
 * 1. ECDH: own private key + sender's ephemeral public = shared secret
 * 2. HKDF-SHA256 to derive AES-256 key
 * 3. AES-256-GCM decrypt
 */
export declare function decryptFromSync(payload: EncryptedSecretPayload, ownPrivateKey: crypto.KeyObject): Secrets;
/**
 * Additional-authenticated-data binding a sealed credential to exactly one
 * (recipient machine, account, mandate, single-use grant, key-rotation epoch).
 * Every field is load-bearing: a payload sealed with one AAD cannot be consumed
 * under any other (recipient swap, account swap, replay, post-rotation reuse).
 */
export interface AccountCredentialAAD {
    /** Routing fingerprint of the machine this blob is sealed FOR (recipient binding, S1/S2). */
    recipientFingerprint: string;
    /** SubscriptionAccount.id the credential belongs to (account binding). */
    accountId: string;
    /** Authorizing coordination-mandate id (R1). */
    mandateId: string;
    /** Single-use grant id — a consumed grant must be rejected on replay (R3, §8.1). */
    grantId: string;
    /** Recipient X25519 key-rotation generation (R4b) — old blobs die on de-pair rotation. */
    pairingEpoch: number;
}
/** Encrypted account-credential payload (AAD carried in clear, authenticated by GCM). */
export interface EncryptedAccountCredentialPayload {
    /** Ephemeral X25519 public key (base64). */
    ephemeralPublicKey: string;
    /** AES-256-GCM initialization vector (base64). */
    iv: string;
    /** Encrypted ciphertext (base64). */
    ciphertext: string;
    /** AES-GCM authentication tag (base64). */
    tag: string;
    /** The AAD this blob was sealed with — authenticated, not confidential. */
    aad: AccountCredentialAAD;
}
/**
 * Seal a credential to a single recipient, bound to an AAD (WS5.2 R3 / I2).
 * NOT `encryptForSync` — that function cannot carry the AAD binding.
 */
export declare function encryptAccountCredential(secrets: Secrets, recipientPublicKeyBase64: string, aad: AccountCredentialAAD): EncryptedAccountCredentialPayload;
/**
 * Decrypt an account credential, verifying the AAD matches expectation.
 * FAILS CLOSED (throws) when: the payload AAD or `expectedAAD` is absent or
 * malformed; the two AADs differ; the recipient X25519 key has rotated since
 * sealing (R4b); or the GCM tag does not authenticate. NOT `decryptFromSync`,
 * which has no AAD concept and would decrypt a replayed blob.
 */
export declare function decryptAccountCredential(payload: EncryptedAccountCredentialPayload, ownPrivateKey: crypto.KeyObject, expectedAAD: AccountCredentialAAD): Secrets;
//# sourceMappingURL=SecretStore.d.ts.map