/** * Per-subject sealing of a scope dump on its way into a platform-retained copy (#37). * * The sibling of [`mask.ts`](./mask.ts), and the contrast is the point. Masking is for a * copy a HUMAN will read: lossy, heuristic, irreversible, and applied when data leaves the * governed environment. Sealing is for a copy the PLATFORM keeps: lossless, keyed, and * reversible — right up until the subject's key is destroyed, after which it is more * irreversible than masking ever was. * * **Why a backup needs this at all.** Reap backups and stored dumps are full-fidelity on * purpose (`backups.ts`: *"a backup that cannot restore is a false promise"*), which is * exactly why an erasure cannot reach one. `UPDATE … SET payload = NULL` erases the live * scope; it does nothing to the copy sitting in R2 from last Tuesday. Sealing each * subject's payloads under their own key on the way out means destroying that one key * reaches backwards into every copy already taken — which is the only mechanism that makes * "erased" true of an immutable store. * * **Scope is narrow and deliberate.** Only `_substrat_outbox`, only rows the kernel has * already classified (`pii_class != 'none'` with a `subject_id`), only the `payload` cell. * The envelope stays readable so a restored copy is still navigable, and vertical-owned * tables are untouched — a vertical's own PII is a documented limit of this mechanism, not * something quietly half-covered here (see kernel-design.md, open question 17). */ import type { ScopeDumpTable } from '@substrat-run/contracts'; import type { SealedSecret } from '@substrat-run/kernel'; /** * The key operations both directions need, as `HostAdmin` exposes them. Narrowed to an * interface so this module can be tested without a host, and so the seal path cannot reach * for any other admin capability. */ export interface SubjectSealer { seal(items: readonly { subjectId: string; plaintext: string; }[]): Promise<(SealedSecret | null)[]>; open(items: readonly { subjectId: string; sealed: SealedSecret; }[]): Promise<(string | null)[]>; } /** * Seal every classified payload in a dump. Returns new arrays; never mutates the input. * * A payload whose subject has been shredded seals to `null` — the sealer refuses to mint a * key for a tombstoned subject, and the copy records the same absence a live redaction * leaves. A cell that is ALREADY sealed passes through untouched, so re-sealing a dump * (a backup of a backup, a retried capture) is a no-op rather than double encryption. */ export declare function sealDump(tables: ScopeDumpTable[], sealer: SubjectSealer): Promise; /** * Open every sealed payload in a dump — the restore half. * * A payload whose key is gone opens to `null`. That is where an erasure actually bites: the * ciphertext was in the backup all along, and after the shred nothing turns it back into a * person. The restored scope then looks exactly like a live scope that was redacted, which * is the property worth having — two copies of the same tenant that disagree about who was * erased is the failure this shape avoids. * * Unsealed cells pass through, so a dump taken before sealing existed still restores. */ export declare function openDump(tables: ScopeDumpTable[], sealer: SubjectSealer): Promise; //# sourceMappingURL=seal.d.ts.map