import { SecretValue } from "../secrets/secret-value.js"; import { StoredAuditEntry } from "./types.js"; //#region src/audit/audit-db.d.ts /** * Stored binding identifier. The framework default is the * `better-sqlite3-multiple-ciphers` package - registered by * `@graphorin/store-sqlite` at startup. Custom enterprise deployments * can register their own binding here without touching the secrets * layer. * * @stable */ type AuditDbBindingId = 'better-sqlite3-multiple-ciphers' | (string & {}); /** * Options forwarded to a binding factory when `openAuditDb(...)` runs. * * @stable */ interface OpenAuditDbOptions { /** Filesystem path to the audit database file. */ readonly path: string; /** Passphrase used to derive the cipher key. */ readonly passphrase: SecretValue; /** Cipher name. Defaults to the binding's preferred cipher. */ readonly cipher?: string; /** Cipher-specific options. Forwarded verbatim to the binding. */ readonly cipherOptions?: Readonly>; /** Identifier of the binding to use. Defaults to the default binding. */ readonly binding?: AuditDbBindingId; /** Optional logger for warnings emitted during open. */ readonly warn?: (message: string) => void; } /** * Shape of a registered binding. The factory is asynchronous so it * can perform the file-mode check and run the cipher self-test before * returning. (It does NOT write an `audit:db-opened` chain entry - the * database is not yet ready to record its own opening; an operator that * wants one appends it after `openAuditDb(...)` resolves.) * * @stable */ interface AuditDbBinding { /** Identifier of the binding. */ readonly id: AuditDbBindingId; /** Human-readable description for diagnostics. */ readonly description: string; /** Open the audit database. */ readonly open: (options: OpenAuditDbOptions) => Promise; } /** * Minimal audit-database surface consumed by the chain operations. * Concrete bindings can expose more, but the contract is intentionally * small so the verifier remains binding-agnostic. * * The methods are deliberately synchronous on the read path - the * single-file SQLite default is already in-process, and asynchrony * would add no I/O parallelism but would force every audit consumer * to plumb promise-state through hot loops. * * @stable */ interface AuditDb { /** Stable identifier of the binding that produced this handle. */ readonly binding: AuditDbBindingId; /** Path on disk. */ readonly path: string; /** Append a new audit entry. The binding is responsible for atomicity. */ readonly insert: (entry: StoredAuditEntry) => Promise; /** Read the most-recent entry, used by `appendAudit` to compute `prev_hash`. */ readonly latest: () => Promise; /** Iterate stored entries in `seq` order. The optional bounds are inclusive. */ readonly iterate: (bounds?: { readonly fromSeq?: number; readonly toSeq?: number; }) => AsyncIterable; /** Total number of stored entries. */ readonly count: () => Promise; /** Delete entries with `seq <= threshold`. Used by `pruneAudit`. */ readonly deleteUpTo: (threshold: number) => Promise; /** * Replace a stored entry. The replacement preserves the `seq` * primary key and overwrites `prevHash` and `hash`. Used by * `pruneAudit` to root the surviving chain at the genesis prev-hash * and recompute the rolling chain hashes. */ readonly replaceEntry: (entry: StoredAuditEntry) => Promise; /** * OPTIONAL cross-process fence: run `fn` inside one write * transaction on the underlying handle (`BEGIN IMMEDIATE` semantics - * the write lock is held from entry to commit, and a failure rolls * back). When present, `appendAudit` wraps its `latest()`+`insert()` * read-modify-write in it so two PROCESSES sharing one audit file * cannot both hash against the same tip, and `pruneAudit` runs its * delete+rewrite atomically so a concurrent append never chains to a * pre-prune tip. Additive: bindings without it keep compiling - * `appendAudit` falls back to a bounded seq-conflict retry, and * `pruneAudit` fails closed. */ readonly transact?: (fn: () => Promise) => Promise; /** Close the underlying handle. */ readonly close: () => Promise; } /** * Register a concrete binding. The framework default * (`better-sqlite3-multiple-ciphers`) is registered by * `@graphorin/store-sqlite`; downstream consumers can register a * custom binding before calling `openAuditDb(...)`. * * @stable */ declare function registerAuditDbBinding(binding: AuditDbBinding, opts?: { readonly setAsDefault?: boolean; }): () => void; /** * Snapshot of the binding registry. Used by `graphorin doctor` once * the CLI ships. * * @stable */ declare function listAuditDbBindings(): ReadonlyArray<{ readonly id: AuditDbBindingId; readonly description: string; readonly isDefault: boolean; }>; /** * Read the identifier of the active default binding. Returns * `undefined` if no binding has been registered. * * @stable */ declare function getDefaultAuditDbBinding(): AuditDbBindingId | undefined; /** * Reset the registry. Tests use this between cases. * * @experimental */ declare function _resetAuditDbBindingsForTesting(): void; /** * Open an audit database. The function fails fast with * `AuditDbCipherUnavailableError` when no binding is registered, or * when the requested binding identifier is unknown. * * @stable */ declare function openAuditDb(options: OpenAuditDbOptions): Promise; //#endregion export { AuditDb, AuditDbBinding, AuditDbBindingId, OpenAuditDbOptions, _resetAuditDbBindingsForTesting, getDefaultAuditDbBinding, listAuditDbBindings, openAuditDb, registerAuditDbBinding }; //# sourceMappingURL=audit-db.d.ts.map