import type { Logger } from '../types/logger.js'; import type { RotatableSecretVault, SecretVault } from '../types/secret-vault.js'; export interface SecretVaultOptions { /** Absolute path to the key file. Created with mode 0o600 if missing. */ keyFile: string; /** Logger for structured warnings. Falls back to console.warn when omitted. */ logger?: Logger | undefined; } /** * Default vault: AES-256-GCM with a key stored at `keyFile` (mode 0o600). * The key is loaded lazily on first encrypt/decrypt; if it does not exist, * a fresh one is generated. Decryption of plaintext values is a no-op so * legacy configs continue to work. * * Key file format: * - Legacy (v1): exactly 32 raw bytes * - Versioned (v2+): 4-byte magic `WSKV` + 1-byte version + 32-byte key (37 bytes) * * Encrypted value format: `enc:v:::` where N is the * key version. After rotation, encrypt() emits the new version prefix. */ export declare class DefaultSecretVault implements RotatableSecretVault { private readonly keyFile; private readonly logger; private key?; private _keyVersion; constructor(opts: SecretVaultOptions); /** * Emit a structured warning. Uses the configured Logger when available; * falls back to console.warn(JSON) so warnings are never silently dropped * during early boot. */ private logWarn; /** Current key version. Starts at 1; incremented by rotateKey(). */ get keyVersion(): number; isEncrypted(value: string): boolean; encrypt(plaintext: string): string; decrypt(value: string): string; /** * Generate a new encryption key, write it to disk, and increment the key version. * After rotation, encrypt() emits the new version prefix (e.g. enc:v2:). * The caller must re-encrypt existing config values (see rotateConfigKeys()). */ rotateKey(): { oldVersion: number; newVersion: number; }; /** * If WRONGSTACK_VAULT_PASSPHRASE is set but the key on disk is still stored * unwrapped (legacy v1 / versioned v2), re-write it in passphrase-wrapped (v3) * form. The data key is preserved, so all existing ciphertext keeps * decrypting. Best-effort: a write failure leaves the working unwrapped file * in place and is not fatal to load. */ private migrateToWrappedIfPassphrase; private loadOrCreateKey; } export { decryptConfigSecrets, encryptConfigSecrets, isSecretField } from './config-secrets.js'; /** * Re-write a profile config (or any path) with all secret-bearing * fields encrypted. Used by the `wstack auth` subcommand. */ export declare function rewriteConfigEncrypted(configPath: string, vault: SecretVault, patch?: Record): Promise; /** * Scan a config file on disk for plaintext secret-bearing fields and * rewrite the file with them encrypted in place. Returns a count of how * many fields were migrated. Idempotent — calling on a fully-encrypted * file is a no-op and writes nothing. Used by the CLI on every boot so * users who had plaintext keys before the vault landed are upgraded * transparently. */ export declare function migratePlaintextSecrets(configPath: string, vault: SecretVault, logger?: Pick): Promise<{ migrated: number; file: string; }>; /** * Rotate the vault's encryption key and re-encrypt all secret-bearing * fields in a config file. This is the atomic key rotation operation: * * 1. Read the config file * 2. Decrypt all encrypted values with the old key * 3. Generate a new key (vault.rotateKey()) * 4. Re-encrypt all values with the new key (new version prefix) * 5. Write the config file atomically * * Returns the number of fields re-encrypted and the version transition. * If the config file doesn't exist or has no encrypted fields, returns * { rotated: 0 } without modifying the key. */ export declare function rotateConfigKeys(configPath: string, vault: RotatableSecretVault, logger?: Pick): Promise<{ rotated: number; oldVersion: number; newVersion: number; file: string; }>; //# sourceMappingURL=secret-vault.d.ts.map