/**
 * Passphrase-encrypted file store for secrets — platform-neutral.
 *
 * An AES-256-GCM encrypted-file store under `~/.agents/.cache/secrets/`. The
 * encryption key is scrypt-derived from a passphrase read from
 * `AGENTS_SECRETS_PASSPHRASE` (preferred) or a machine-local key the store
 * auto-provisions on first use. One `<item>.enc` JSON file per item, mode 0600.
 *
 * Two callers, one policy: the store silently auto-provisions a stable
 * machine-local key (a 0600 file under `~/.agents/.secrets-key/`) on EVERY
 * platform, so it works out of the box with no passphrase to set or remember and
 * never pops a prompt or Touch ID sheet.
 *  - Linux (src/lib/secrets/linux.ts): the headless fallback when the default
 *    Secret Service collection is locked.
 *  - macOS/Windows file-backed bundles (src/lib/secrets/bundles.ts): an explicit,
 *    opt-in non-biometry backend for headless/remote runs.
 * Set AGENTS_SECRETS_PASSPHRASE to opt into a key held off disk instead (e.g. to
 * share one bundle's ciphertext across boxes under a common key).
 *
 * The item-name scheme is shared with the keychain backend so a file-backed
 * item and its keychain twin carry identical names:
 *   `agents-cli.bundles.<name>` and `agents-cli.secrets.<bundle>.<key>`.
 */
import type { KeychainBackend } from './index.js';
export declare function fileDir(): string;
/** True if a machine-local passphrase has already been provisioned. */
export declare function machinePassphraseExists(): boolean;
/**
 * Resolve the passphrase for the encrypted file store.
 *
 * Order: AGENTS_SECRETS_PASSPHRASE > previously-provisioned machine-local key >
 * legacy co-located key > a freshly auto-provisioned machine-local key. It NEVER
 * prompts, and never fails for WANT of a passphrase — the file store must work on
 * every platform (macOS included) without the user setting, typing, or
 * remembering one. (It can still throw if provisioning cannot write the key file
 * at all; that is a disk/permissions failure, not a missing passphrase.) Provisioning
 * writes a 0600 key file (encryption-at-rest, same posture as an SSH key); set
 * AGENTS_SECRETS_PASSPHRASE to opt into an off-disk key.
 */
export declare function getPassphrase(): string;
/** Encrypted-file on-disk shape. Exported for tests. */
export interface EncFile {
    salt: string;
    iv: string;
    authTag: string;
    ciphertext: string;
}
/** Encrypt plaintext under a passphrase using AES-256-GCM with a random
 *  scrypt salt and a random 96-bit IV. Exported for tests. */
export declare function encryptForFallback(plaintext: string, passphrase: string): EncFile;
/** Decrypt an EncFile under a passphrase. Throws on wrong key or tampered
 *  ciphertext (auth-tag mismatch). Exported for tests. */
export declare function decryptForFallback(enc: EncFile, passphrase: string): string;
/** Absolute path of one encrypted file-store item. No read or decrypt occurs. */
export declare function fileStoreItemPath(item: string): string;
declare function fileHas(item: string): boolean;
declare function fileGet(item: string): string;
declare function fileGetBatch(items: string[]): Map<string, string>;
declare function fileSet(item: string, value: string): void;
declare function fileDelete(item: string): boolean;
declare function fileList(prefix: string): string[];
/** True if the fallback dir has any committed encrypted items. */
export declare function fileStoreHasItems(): boolean;
/** Low-level file-store ops, exported so callers (linux fallback, macOS
 *  file-backed bundles) can opt into or out of passphrase auto-provision. */
export declare const fileStore: {
    has: typeof fileHas;
    get: typeof fileGet;
    getBatch: typeof fileGetBatch;
    set: typeof fileSet;
    delete: typeof fileDelete;
    list: typeof fileList;
};
/** File-only KeychainBackend (exported for tests; the Linux backend uses these
 *  ops with auto-provision allowed). */
export declare const fileBackend: KeychainBackend;
/** Resolved passphrase directory (exported for integration tests). */
export declare function resolvePassphraseDir(): string;
/**
 * Path of the machine-local passphrase file that currently holds the file-store
 * key, or null if none is provisioned. Prefers the canonical #479 location and
 * falls back to the legacy co-located path, mirroring `readMachinePassphrase`.
 */
export declare function machinePassphraseSourcePath(): string | null;
/** Outcome of a `rotatePassphrase` run. Carries no secret material. */
export interface RotatePassphraseReport {
    /** True when nothing was written (report-only). */
    dryRun: boolean;
    /** True when the store was re-encrypted and the key file swapped. */
    committed: boolean;
    /** Encrypted items that decrypt under the current key and were (or would be) re-keyed. */
    bundleCount: number;
    /** `.enc` files that do NOT decrypt under the current key — left untouched, never re-keyed. */
    skipped: string[];
    /** Every re-keyed item round-tripped (decrypt) under the new key before any swap. */
    roundTripOk: boolean;
    /** The machine-local passphrase file that was (or would be) rewritten in place. */
    keyFilePath: string;
    /**
     * A previous rotation had left artifacts on disk and they were healed before
     * this rotation proceeded. This happens under `--dry-run` too — recovery is how
     * a crashed store becomes readable again without re-keying it — and is the only
     * thing a dry run writes.
     */
    recoveredInterruptedRotation: boolean;
}
/**
 * Recover from a rotation that was interrupted mid-swap on a prior run, so the
 * store is always left in a single, self-consistent, readable state.
 *
 * Recovery is CONTENT-aware, not presence-aware, and it classifies the WHOLE
 * store, not just one item. The mere existence of the store dir and the key file
 * does not prove they match (RUSH-1975 data-loss window): on the non-co-located
 * key path the swap is four renames, and a crash after the store swap
 * (`stageDir`->`dir`) but before the key swap (`keyTmp`->`keyPath`) finishes
 * leaves a NEW-key store next to the OLD key file, both present. A presence check
 * would see "both here" and wrongly sweep the only copies of the old ciphertext
 * (`<dir>.rotate-old-*`) and the new key (`<key>.rotate-new`), permanently
 * orphaning every secret. So we probe the actual ciphertext with `classifyStore`,
 * which distinguishes a store that opens fully under one key ('all') from one that
 * is MIXED — some items under the live key, others under the incoming key ('some',
 * e.g. after a mid-swap crash contaminated by a later `secrets set`):
 *
 *  1. The live key opens EVERY non-orphan item ('all') → rotation complete and
 *     consistent (or never interrupted); sweeping the `.rotate-*` artifacts is safe —
 *     unless a `<dir>.rotate-old-*` backup still holds `.enc` items absent from the
 *     live dir. That means the live dir is not the post-swap store but a fresh dir an
 *     interstitial `secrets set` created after a crash in the move-aside window left
 *     the store dir absent, so the backup is the only copy of those items → REFUSE.
 *  2. Else, if `<key>.rotate-new` opens every non-orphan item ('all'), the crash
 *     landed after the store swap but before the key swap finished → finish the
 *     rotation forward by installing `.rotate-new` as the live key, then sweep.
 *  3. Else, if neither key opens any item, roll back: restore the
 *     `<dir>.rotate-old-*` backup over `dir` and `<key>.rotate-oldkey` over the key
 *     file — but only once the backup is proven to open fully under the old key.
 *  4. If a key opens SOME but not all items ('some'), the store is MIXED — an
 *     interrupted rotation contaminated by a later write, with live data under two
 *     keys at once. Sweeping would delete the only copy of one of those keys, so we
 *     REFUSE: throw an actionable error and preserve every recovery artifact for
 *     out-of-band repair. Likewise, if neither forward nor rollback can be proven,
 *     leave every artifact in place — a leftover temp dir is recoverable, deleting
 *     the only copy of a key or ciphertext is not.
 *
 * A phase-marker / journal file was considered and deliberately skipped: the
 * AES-256-GCM auth tag already makes the decrypt probe an authoritative,
 * self-validating record of which key matches the store. A separate marker would
 * be a second source of truth that can disagree with reality — its own write has
 * crash windows, and a stale marker misleads — so it would weaken, not strengthen,
 * this guarantee. Idempotent; a no-op when no rotation artifacts are present.
 * Callers run this under the store lock (see `withStoreLock`).
 */
/**
 * Whether a previous rotation left artifacts on disk — i.e. whether
 * {@link recoverInterruptedRotation} would do any work. Read-only, so `--dry-run`
 * can report a pending recovery without performing (and thus writing) one.
 */
export declare function hasInterruptedRotationArtifacts(keyPath: string): boolean;
/**
 * Rotate the machine-local file-store passphrase: decrypt every `.enc` item
 * under the current key and re-encrypt it under a freshly generated one, then
 * swap both the ciphertext and the key file atomically.
 *
 * Safety contract (RUSH-1975):
 *  - Verify before writing: every re-keyed item must round-trip decrypt under
 *    the new key, and the re-keyed count must reconcile with the source, or the
 *    run aborts having written nothing.
 *  - Atomic: the new store is staged in a sibling temp dir, fsync'd, then swapped
 *    into place by directory rename; the new key file is fsync'd and swapped the
 *    same way. A crash before the swap leaves the old store and old key fully
 *    intact and readable; a crash inside the swap self-heals on the next run
 *    (see `recoverInterruptedRotation`). No half-re-keyed store is ever exposed.
 *  - No plaintext (secret value or passphrase) is ever written to disk, argv, or
 *    a log — only ciphertext is staged, and the new key lands only in the 0600
 *    key file.
 *  - Items that do not decrypt under the current key (orphan caches, stale test
 *    artifacts written under another key) are copied through verbatim, never
 *    re-keyed, and reported in `skipped`.
 *
 * `newPassphrase` and the `on*` callbacks are test seams. `newPassphrase` pins the
 * generated key so a test can assert the swap. `onStagedBeforeCommit` fires after
 * staging but before any swap (a crash here leaves the old store fully intact).
 * `onStoreSwappedBeforeKeySwap` fires after the store swap but before the key swap
 * begins (Window A: NEW-key store beside the OLD key file). `onKeyBackedUpBeforeNewKey`
 * fires after the old key is moved aside but before the new key lands (Window B: NEW
 * store, key file absent). Each throws to simulate a mid-swap crash at that exact
 * point, and next-run recovery must heal it without data loss. `tamperStaged` forces
 * a staged item to fail its round-trip check, exercising the verify-before-swap abort.
 */
export interface RotatePassphraseOpts {
    dryRun?: boolean;
    newPassphrase?: string;
    onStagedBeforeCommit?: () => void;
    onStoreMovedAsideBeforeSwap?: () => void;
    onStoreSwappedBeforeKeySwap?: (heartbeat: () => void) => void;
    onKeyBackedUpBeforeNewKey?: () => void;
    tamperStaged?: boolean;
}
/**
 * Rotate under the exclusive store lock, so no `secrets set`/`delete` and no
 * second rotation can interleave with the swap (see `withStoreLock`). The whole
 * run — recovery, verify, swap — holds the lock; it is released on return or throw.
 */
export declare function rotatePassphrase(opts?: RotatePassphraseOpts): RotatePassphraseReport;
/** Test-only: reset module state (file dir + cached passphrase). */
export declare function _resetFileStoreForTest(opts?: {
    fileDir?: string | null;
    passphraseDir?: string | null;
    passphrase?: string | null;
}): void;
/** Test-only: flush the in-memory metadata cache to disk now. Production flushes
 *  once at process exit; a test needs the on-disk copy mid-run to simulate the
 *  next `agents` process reading it. */
export declare function _flushMetaCacheForTest(): void;
/** Test-only: shorten the store-lock acquire timeout so a contended-lock assertion
 *  fails fast instead of waiting out the 30s production budget. */
export declare function _setFileStoreLockTimeoutForTest(ms: number | null): void;
/** Test-only: shrink the store-lock stale window so a heartbeat/steal assertion runs
 *  in milliseconds instead of the 5s production window. */
export declare function _setFileStoreLockStaleMsForTest(ms: number | null): void;
/** Test-only: the cross-process store-lock target (sibling of the store dir). */
export declare function _fileStoreLockPathForTest(): string;
export {};
