@loomwork/compliance 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,540 @@
1
+ import type { EventEnvelope, ExecutionState } from "@loomwork/core";
2
+ import { type ArchiveStore, type DestructionProof, type EventStore } from "@loomwork/event-runtime";
3
+ import { type SubjectIndex } from "./SubjectIndex";
4
+ /** Reserved control stream carrying deferred-erasure facts (ERASURE_PENDING). */
5
+ export declare const ERASURE_STREAM = "__loom_erasure__";
6
+ /**
7
+ * A compaction / inbox archive (ADR 0138 / 0139) erasure must reckon with. A PLAINTEXT archive
8
+ * (`sealsSegments` not `true`) holds the subject's moved envelopes in the clear, so a key's destruction
9
+ * does not reach them: erasure DELETES the subject's segments from it (`deleteExecution`). A SEALING
10
+ * archive (`EncryptingArchiveStore`, same KeyStore as the log) is erased by the shred itself.
11
+ */
12
+ export type ErasableArchive = Pick<ArchiveStore, "headers" | "listExecutions" | "read" | "append"> & Partial<Pick<ArchiveStore, "deleteExecution" | "replaceSegments">> & {
13
+ readonly sealsSegments?: unknown;
14
+ };
15
+ /**
16
+ * ADR 0140 - the content store the opt-in agent claim-check offloads tool outputs to. Over a PLAINTEXT store
17
+ * (`sealsContent` not `true`) a key's destruction does not reach a blob, so erasure DELETES the blobs the
18
+ * subject's envelopes cite (those no other, non-erased execution still cites: ADR 0120 owner-set
19
+ * semantics) and records it (`ERASURE_CONTENT_DELETE_INTENDED` then `ERASURE_CONTENT_DELETED`). A store
20
+ * that seals each blob under its owning subject's key (ADR 0143) is erased by the shred itself; the erasure
21
+ * still journals the blobs it covered and deletes the unreadable ciphertext where the store can.
22
+ */
23
+ export interface ErasableContent {
24
+ delete(ref: {
25
+ __loomContent: string;
26
+ }): Promise<void>;
27
+ /** Optional (ContentStore SPI 1.3.0): a conditional delete that spares a blob re-put since `cutoffMs`. */
28
+ deleteIfUntouchedSince?(ref: {
29
+ __loomContent: string;
30
+ }, cutoffMs: number): Promise<boolean>;
31
+ touchClock?(): Promise<number>;
32
+ readonly sealsContent?: unknown;
33
+ /**
34
+ * ADR 0143 D5 - the ownership proof a SEALING store offers: does the blob open under the subject of
35
+ * `access` (decrypt and discard)? Erasure deletes only blobs it proves the subject owns; a blob the log
36
+ * merely CITES (a resume payload, an input, another execution's result) is never deleted.
37
+ */
38
+ ownedBy?(ref: {
39
+ __loomContent: string;
40
+ }, access: {
41
+ executionId: string;
42
+ workflowId: string;
43
+ }): Promise<boolean>;
44
+ }
45
+ /** What an erasure removed from the content store (recorded durably as `ERASURE_CONTENT_DELETED`). */
46
+ export interface ContentCopiesDeleted {
47
+ /** Blobs deleted. */
48
+ deleted: number;
49
+ /** Blobs the subject's envelopes cited that another, non-erased execution still cites (kept). */
50
+ retained: number;
51
+ /**
52
+ * ADR 0143: over a store that SEALS per subject, how many blobs the shred made unreadable (all the
53
+ * subject's cited blobs; `deleted` of them were also physically removed). Absent over a plaintext store.
54
+ */
55
+ sealed?: number;
56
+ /**
57
+ * ADR 0143 D5: over a sealing store, how many cited blobs erasure could NOT prove the subject owns (another
58
+ * subject's blob the log merely cites, an absent one, or one whose key was already destroyed). They are
59
+ * counted in `retained`, never deleted, and never reported as the subject's data.
60
+ */
61
+ foreign?: number;
62
+ /**
63
+ * ADR 0143 D5: over a sealing store, how many cited blobs ownership could not be PROVED for because the
64
+ * proving key is already destroyed (a re-erasure after a direct shred, or a citation added after the
65
+ * intent). Counted in `retained`, never deleted: the ciphertext is unreadable but this erasure cannot say
66
+ * it is the subject's, so it does not claim it.
67
+ */
68
+ unprovable?: number;
69
+ }
70
+ /** What an erasure removed from the plaintext archives (recorded durably as `ERASURE_ARCHIVE_DELETED`). */
71
+ export interface ArchivedCopiesDeleted {
72
+ /** Executions whose ADR 0138 compaction segments were deleted. */
73
+ executions: string[];
74
+ /** Reserved streams (inbox, topic, registry, quarantine, late runs; ADR 0139) whose archived segments were deleted. */
75
+ reservedStreams: string[];
76
+ }
77
+ /** One archived reserved stream whose sender fields erasure redacted (ADR 0139 D12). */
78
+ export interface SenderRedaction {
79
+ streamId: string;
80
+ /** How many archived envelopes had the erased subject's identity replaced by `{ erased: true }`. */
81
+ envelopes: number;
82
+ }
83
+ /** Does `sender` identify the erased subject? */
84
+ export type SenderMatcher = (subjectId: string, tenant: string, sender: {
85
+ actor?: string;
86
+ tenant?: string;
87
+ onBehalfOf?: string;
88
+ }) => boolean;
89
+ /** The default: the sender's `actor` is the subject id (and, in a tenant partition, the tenant agrees). */
90
+ export declare const defaultSenderMatches: SenderMatcher;
91
+ /**
92
+ * Erasure cannot complete while a plaintext archive still holds the subject's segments and offers no
93
+ * way to delete them. Thrown BEFORE the key is destroyed (nothing was shredded).
94
+ */
95
+ export declare class ArchivedCopiesRemainError extends Error {
96
+ readonly streams: readonly string[];
97
+ readonly retryable = false;
98
+ constructor(streams: readonly string[]);
99
+ }
100
+ /**
101
+ * Erasure cannot complete: a plaintext archive holds other chains' archived inbox envelopes naming the
102
+ * subject as their sender, and it cannot rewrite a segment atomically (`replaceSegments`, ArchiveStore SPI
103
+ * 1.1.0), so the identity could not be redacted. Thrown BEFORE the key is destroyed (nothing was shredded).
104
+ */
105
+ export declare class SenderRedactionUnsupportedError extends Error {
106
+ readonly streams: readonly string[];
107
+ readonly retryable = false;
108
+ constructor(streams: readonly string[]);
109
+ }
110
+ /**
111
+ * Erasure cannot complete: the subject's logs cite claim-checked blobs (ADR 0140) in a content store that
112
+ * erasure cannot delete from, or cannot prove other executions do not cite. Thrown BEFORE the key is
113
+ * destroyed (nothing was shredded).
114
+ */
115
+ export declare class ContentCopiesRemainError extends Error {
116
+ readonly count: number;
117
+ readonly retryable = false;
118
+ constructor(count: number, why: string);
119
+ }
120
+ /** Every claim-check envelope ref `value` cites (iterative, cycle-safe; the `ref` leaf is not descended). */
121
+ export declare function claimCheckRefsOf(value: unknown, into?: Set<string>): Set<string>;
122
+ /**
123
+ * ADR 0140 D11 - the claim-check refs an execution cites at a TRUSTED position: the top-level `output`
124
+ * of a recorded `STEP_COMPLETED` (through a taint wrapper). An envelope the opt-in preset wrote sits
125
+ * exactly there; one NESTED inside a tool's result is tool-authored data and may be forged, so it is not
126
+ * evidence that anyone cites the blob.
127
+ */
128
+ export declare function trustedClaimCheckRefsOf(events: readonly {
129
+ eventType: string;
130
+ payload: unknown;
131
+ }[], into?: Set<string>): {
132
+ refs: Set<string>;
133
+ sawStep: boolean;
134
+ };
135
+ /**
136
+ * Destroy a subject's erasure key across the given executions. Wire this to the crypto
137
+ * plugin's `shredSubject` (bound to the KeyStore + raw store + snapshot store). MUST be
138
+ * idempotent — a re-invocation for an already-erased subject must not double-tombstone.
139
+ * `shredSubject` is (ADR 0106): it skips every log already carrying the subject's
140
+ * `PAYLOAD_SHREDDED` and writes the rest. Do NOT guard on key existence — a key can be gone
141
+ * while a tombstone append failed, and that guard would never retry it.
142
+ */
143
+ export type Shredder = (subjectId: string, executionIds: readonly string[],
144
+ /** The per-call operator assertion of {@link EraseSubjectOptions.force}, when given. */
145
+ options?: {
146
+ force?: "key-destroyed-elsewhere";
147
+ }) => Promise<void>;
148
+ export interface EraseCoordinatorOptions {
149
+ index: SubjectIndex;
150
+ /**
151
+ * `deleteExecution` (optional) lets erasure reach the RESERVED inbox streams of the subject's
152
+ * finished executions (ADR 0110 follow-up): see {@link EraseCoordinator}. A store without it
153
+ * cannot delete a stream, and a payload still in flight in one is then erased only by the key
154
+ * (the crypto plugin seals inbox payloads under the owner's subject).
155
+ */
156
+ eventStore: Pick<EventStore, "read" | "append"> & Partial<Pick<EventStore, "deleteExecution" | "listExecutionsPage" | "listExecutions">>;
157
+ shred: Shredder;
158
+ /**
159
+ * ADR 0138 / 0139 - the archives that may hold the subject's moved envelopes (`defineConfig` wires the
160
+ * compaction archive, which is also the inbox archive). Over a PLAINTEXT archive erasure DELETES the
161
+ * subject's compaction segments and their chains' reserved-stream segments after the shred and records
162
+ * what it deleted; it never reports `erased` while plaintext copies remain. A sealing archive needs
163
+ * nothing: the key's destruction makes its segments unreadable.
164
+ */
165
+ archives?: readonly ErasableArchive[];
166
+ /**
167
+ * ADR 0140 - the content store claim-checked tool outputs were offloaded to (`defineConfig` wires
168
+ * `execution.claimCheck.store`). Over a PLAINTEXT store erasure collects the envelope refs the subject's
169
+ * executions cite (live logs, archive segments, snapshots) BEFORE the shred, then deletes each one no other
170
+ * non-erased execution still cites, journaling the intent first. Refused before the shred when blobs are
171
+ * cited and this is absent, cannot delete, or the event store cannot be enumerated.
172
+ */
173
+ content?: ErasableContent;
174
+ /**
175
+ * ADR 0139 D12 - which archived inbox envelopes name the erased subject as their SENDER (default:
176
+ * `sender.actor === subjectId`, and `sender.tenant === tenant` in a tenant partition). Over a PLAINTEXT
177
+ * archive erasure rewrites those envelopes' `payload.sender` to `{ erased: true }` in the OTHER chains'
178
+ * archived reserved segments (their digest masks the sender, so it stays valid) and journals
179
+ * `ERASURE_SENDER_REDACTED`. A SEALING archive needs nothing: the sender is sealed under the SENDER's
180
+ * subject (`senderSubjectOf` on the crypto stores) and the shred erases it.
181
+ */
182
+ senderMatches?: SenderMatcher;
183
+ /**
184
+ * ADR 0139 N6 - erase the subject as a message SENDER in the crypto layer (destroy the key their identity
185
+ * is sealed under, and the seed their handle derives from). Called after `shred`, with the subject id as
186
+ * the actor and the tenant partition; idempotent. `defineConfig` wires it from the crypto event store's
187
+ * `eraseSender`. Without it a sealed sender is erased by whatever the app's `shred` destroys.
188
+ */
189
+ eraseSender?: (subjectId: string, tenant: string) => Promise<unknown>;
190
+ /**
191
+ * ADR 0139 D22 - reinstate an erased sender in the crypto layer (a fresh seed, so a new handle). Backs
192
+ * {@link EraseCoordinator.reinstateSender}; `defineConfig` wires it from the crypto event store's
193
+ * `reinstateSender`.
194
+ */
195
+ reinstateSender?: (actor: string, tenant: string) => Promise<{
196
+ reinstated: boolean;
197
+ ref?: string;
198
+ epoch?: number;
199
+ }>;
200
+ /** Where a recoverable, non-fatal failure is reported (default: `console.warn`). `defineConfig` wires `onWarn`. */
201
+ onWarn?: (message: string) => void;
202
+ /**
203
+ * ADR 0139 N1 - told after erasure deleted or rewrote an archive key, so a runtime that remembers the
204
+ * key's last archived sequence (`WorkflowRuntime.invalidateInboxArchive`) forgets it. `defineConfig`
205
+ * wires it.
206
+ */
207
+ onArchiveKeyChanged?: (key: string) => void;
208
+ /**
209
+ * ADR 0130 D7 (round 5) - proof that the key store destroyed a subject's key
210
+ * (`(s) => keys.wasDestroyed(s)`). A PROOF function only: returning `true` for a key that was
211
+ * NOT destroyed in the authoritative store produces a FALSE erasure record, so never stub it.
212
+ * `defineConfig` wires it from the crypto key store's `wasDestroyed` by default. Required to
213
+ * resume an erasure in the ShredIncomplete window: the log proves INTENT, not destruction, so
214
+ * without it a retry that reads `KeyUnavailableError` is refused rather than reported as erased.
215
+ * For a key store that cannot prove destruction, the operator asserts it per call with
216
+ * {@link EraseSubjectOptions.force}.
217
+ */
218
+ destroyed?: DestructionProof;
219
+ /**
220
+ * Cancel one PARKED execution under the coordinator's own authority (a system / compliance
221
+ * grant, never a caller's), journaled with the given reason (`"erased"`), through the runtime's
222
+ * normal fenced cancel. REQUIRED for `eraseSubject(..., { parked: "shred" })`: that mode leaves
223
+ * no owner stuck, so every parked owner is moved to a terminal state BEFORE its subject is
224
+ * shredded. Resolves `true` when the cancel was performed; a rejection, or an owner that is not
225
+ * terminal afterwards (it woke up and is running), defers the subject exactly as `"defer"`
226
+ * does. `defineConfig` wires it to `runtime.cancel(id, reason, runtime.systemOptions())`.
227
+ */
228
+ cancel?: (executionId: string, reason: string) => Promise<boolean>;
229
+ /**
230
+ * A SECRET (HMAC-SHA256 key) that keeps subject ids off the reserved control stream. Every row the
231
+ * coordinator writes that names a subject (`ERASURE_PENDING`, and the `ERASURE_*_INTENDED` /
232
+ * `ERASURE_*_DELETED` / `ERASURE_SENDER_REDACTED` crash-recovery intents) then carries
233
+ * `subjectRef` (a keyed digest of the tenant and the subject) INSTEAD of `subjectId` and `tenant`,
234
+ * and recovery matches a row by recomputing the digest. Without it those rows name the subject in
235
+ * plaintext, permanently (the stream is append-only), so two subjects of one principal erased
236
+ * together sit side by side.
237
+ *
238
+ * Keep it SECRET: whoever holds it can test a candidate id against a row (it is keyed because
239
+ * subject ids are guessable, so a bare hash would not hide them). At least 16 bytes, else the
240
+ * constructor throws. Each keyed row also carries `keyId`, a fingerprint of the key that wrote it.
241
+ * To ROTATE, list the old key in {@link previousControlStreamKeys} until no request it wrote is
242
+ * open. An open intent written under a key the coordinator does not hold (rotated away, or the
243
+ * key removed) FAILS the erasure with {@link ControlStreamKeyError} rather than re-planning without
244
+ * it, because after the shred the subject's logs are unreadable and the intent is the only record
245
+ * of what is still to delete; a parked row under such a key is reported through `onWarn` at
246
+ * `loadParked`. Rows written in plaintext before the key was set are still read.
247
+ */
248
+ controlStreamKey?: string | Uint8Array;
249
+ /**
250
+ * Former {@link controlStreamKey}s, still used to RECOGNISE rows they wrote (never to write).
251
+ * Keep one here until no erasure it recorded is parked or interrupted.
252
+ */
253
+ previousControlStreamKeys?: ReadonlyArray<string | Uint8Array>;
254
+ /**
255
+ * ADR 0106 — where compaction checkpoints live. A compacted execution's log no longer holds
256
+ * its prefix (`[0]` after compacting through the tip), so folding the log alone reads a
257
+ * TERMINAL execution as still running and a parked erasure would wait for it forever. With
258
+ * this, liveness folds on the latest snapshot. Pass it whenever the log can be compacted.
259
+ */
260
+ snapshotStore?: {
261
+ loadLatest(executionId: string): Promise<{
262
+ executionId?: string;
263
+ sequence: number;
264
+ state: ExecutionState;
265
+ } | null>;
266
+ /**
267
+ * Optional (SnapshotStore SPI 1.1.0): the latest snapshot as stored at rest, readable after the
268
+ * subject's key is destroyed. Used ONLY in the destroy window (see `foldExecution`), so a
269
+ * sealing store (`EncryptingSnapshotStore`) does not make the erasure's retry throw
270
+ * `KeyUnavailableError`. Without it that retry cannot read a sealed checkpoint and rethrows.
271
+ */
272
+ loadLatestSealed?(executionId: string): Promise<{
273
+ executionId?: string;
274
+ sequence: number;
275
+ state: ExecutionState;
276
+ } | null>;
277
+ };
278
+ }
279
+ export interface EraseSubjectResult {
280
+ subjectId: string;
281
+ /** `erased` — key destroyed now; `parked` — deferred (live execution); `unknown` — no records. */
282
+ outcome: "erased" | "parked" | "unknown";
283
+ /** Executions whose data is now shredded (all of the subject's, when erased). */
284
+ erased: string[];
285
+ /**
286
+ * What the erasure deleted from plaintext archives (ADR 0138 / 0139); present when it deleted anything.
287
+ * The same fact is journaled on the control stream as `ERASURE_ARCHIVE_DELETED`.
288
+ */
289
+ archivedCopiesDeleted?: ArchivedCopiesDeleted;
290
+ /** Claim-checked blobs (ADR 0140) the erasure deleted from a plaintext content store; present when it deleted or kept any it found. */
291
+ contentDeleted?: ContentCopiesDeleted;
292
+ /** Archived reserved segments of OTHER chains whose sender fields naming the subject were redacted (plaintext archives). */
293
+ senderRedactions?: SenderRedaction[];
294
+ /** Live executions blocking completion (a parked request completes when these terminate). */
295
+ pending: string[];
296
+ /**
297
+ * `parked: "shred"` only — the executions that were PARKED (suspended, waiting for an approval, a
298
+ * message or a timer) when their subject was shredded. They are not finished: see
299
+ * {@link EraseSubjectOptions.parked} for what one experiences when it resumes.
300
+ */
301
+ parkedShredded?: string[];
302
+ }
303
+ export interface EraseSubjectOptions {
304
+ /**
305
+ * What to do with a subject whose executions are not all terminal.
306
+ *
307
+ * - `"defer"` (**default**): any execution that is not terminal (a `suspended` one included) is
308
+ * LIVE: the request is recorded durably (`ERASURE_PENDING`) and completes when the last one
309
+ * terminates. A flow parked forever (a chat agent on `receive`) therefore blocks erasure
310
+ * forever; the result is an honest `parked` naming it.
311
+ * - `"shred"` (opt-in): shred NOW when every non-terminal execution is PARKED (`suspended`).
312
+ * An execution that is `running` (or `pending`) still defers: its fold is in flight and would
313
+ * read the key as it is destroyed, which is the one hard rule (R4.5). The window between the
314
+ * check and the key's destruction is the shredder's to close: a parked execution that a timer,
315
+ * a send or a resume wakes in it becomes a running one whose reads fail (below).
316
+ *
317
+ * Leaves NO stuck owner. Each parked (`suspended`) owner is first CANCELLED under the
318
+ * coordinator's own authority ({@link EraseCoordinatorOptions.cancel}, reason `"erased"`, the
319
+ * same fenced path a normal cancel takes), and only when every owner is terminal is the subject
320
+ * shredded. The owners are therefore terminal (`cancelled`) afterwards, so the terminal-head
321
+ * purge of the reserved inbox streams also runs for them, which removes plaintext inbox payloads
322
+ * written before crypto was enabled (these survive the key's destruction) too. If a cancel
323
+ * fails, or an owner woke up and is running by then, nothing is shredded and the subject
324
+ * defers as `"defer"` does (owners already cancelled stay cancelled: the request to erase
325
+ * them was made). Requires {@link EraseCoordinatorOptions.cancel}; without it the call throws.
326
+ *
327
+ * An owner shredded some other way (a direct `shredSubject` call) can still be cancelled:
328
+ * `cancel` folds the redacted view of its log. `resume` of it rejects with the typed
329
+ * `PayloadShreddedError` and appends nothing; so does `loadState` (the strict, authoritative
330
+ * read; its error carries the `redacted` events for operator tooling).
331
+ */
332
+ parked?: "defer" | "shred";
333
+ /**
334
+ * Whether a `parked` outcome is RECORDED (default `true`). When recorded, the request is journaled
335
+ * on the reserved control stream (`ERASURE_PENDING`, never cleared, because the stream is
336
+ * append-only; it names the subject in plaintext unless `controlStreamKey` is set) and the coordinator completes it automatically when the
337
+ * subject's last live execution terminates, across a restart.
338
+ *
339
+ * `record: false` reports `parked` (with `pending`) WITHOUT appending `ERASURE_PENDING` and
340
+ * WITHOUT registering the auto-completion: the subject id is not written to the control stream by
341
+ * a park. The caller owns the retry, and must re-call `eraseSubject` until it returns `erased`
342
+ * (nothing completes it otherwise; `pendingSubjects()` does not list it). Use it when the control
343
+ * stream must not link subject ids, e.g. a principal keyed under a pseudonymous subject and under
344
+ * its own id, both erased.
345
+ *
346
+ * With `parked: "shred"`, the suspended owners are cancelled BEFORE a running one defers the
347
+ * subject; with `record: false` nothing durable then says an erasure is owed, so a caller that
348
+ * stops retrying leaves them cancelled and the subject unerased.
349
+ *
350
+ * It governs the PARK only. A request already recorded by an earlier call stays recorded (and
351
+ * still auto-completes). The `erased` path still journals the subject on the control stream when
352
+ * it deletes plaintext archive copies, claim-checked content or sender copies (the crash-recovery
353
+ * intents); `controlStreamKey` keeps the id out of those rows.
354
+ */
355
+ record?: boolean;
356
+ /**
357
+ * ADR 0130 D7 (round 6) - OPERATOR OVERRIDE, per call (mirrors `ShredOptions.force`). Asserts
358
+ * that the subject's key was destroyed in the authoritative key store, for a store that cannot
359
+ * prove it (no `wasDestroyed`). It lets a retry in the ShredIncomplete window complete, and is
360
+ * passed through to the shredder. A wrong assertion writes a FALSE erasure record while the key
361
+ * lives on.
362
+ */
363
+ force?: "key-destroyed-elsewhere";
364
+ }
365
+ /**
366
+ * An erasure met an OPEN control-stream intent written under a `controlStreamKey` this coordinator
367
+ * does not hold, so it cannot tell whether the intent is this subject's. Fail closed: pass the key
368
+ * that wrote it in `previousControlStreamKeys` (`keyId` names it) and retry.
369
+ */
370
+ export declare class ControlStreamKeyError extends Error {
371
+ readonly keyId: string;
372
+ readonly intentId: string;
373
+ constructor(keyId: string, intentId: string);
374
+ }
375
+ /**
376
+ * Coordinates liveness-gated subject erasure and the auto-completion of parked requests.
377
+ * Feed `handleEvent` the event stream (e.g. from `defineConfig`'s `onEvent`) so a parked
378
+ * erasure completes the moment the subject's last live execution terminates.
379
+ */
380
+ export declare class EraseCoordinator {
381
+ private readonly options;
382
+ private readonly parked;
383
+ private readonly parkedRefs;
384
+ private readonly controlKeys;
385
+ constructor(options: EraseCoordinatorOptions);
386
+ /** Best-effort: repopulate the parked set from the durable control stream (restart recovery). */
387
+ loadParked(): Promise<void>;
388
+ /**
389
+ * ADR 0139 D22 (owner ruling 2026-10-31) - REINSTATE an erased message sender: the explicit, RECORDED
390
+ * operator verb that lets a sender erased by {@link eraseSubject} send again, under a NEW handle (their
391
+ * old messages stay erased and unlinkable to it). Without it a send from an erased sender keeps throwing
392
+ * `SenderErasedError`. Journaled on the control stream: `SENDER_REINSTATE_INTENDED` first, then
393
+ * `SENDER_REINSTATED` with the keyed non-identifying `ref`, the epoch, who (`options.by`) and when;
394
+ * NEVER the plaintext identity. Resolves with whether anything was reinstated (`false`: the sender is
395
+ * not erased, or never sent). Throws unless the coordinator was given `reinstateSender`.
396
+ */
397
+ reinstateSender(actor: string, tenant?: string, options?: {
398
+ by?: string;
399
+ }): Promise<{
400
+ reinstated: boolean;
401
+ ref?: string;
402
+ epoch?: number;
403
+ }>;
404
+ /** Request erasure of a subject within a tenant. Shreds now if all executions are terminal, else parks. */
405
+ eraseSubject(subjectId: string, tenant?: string, options?: EraseSubjectOptions): Promise<EraseSubjectResult>;
406
+ /** Complete a parked erasure when the subject's last live execution terminates. */
407
+ handleEvent(event: EventEnvelope): Promise<void>;
408
+ /**
409
+ * Close the subject's open `ERASURE_PENDING` rows (either form) now that it is erased, so a restart
410
+ * does not park it again. Written LAST: a crash before it leaves the request parked, and completing
411
+ * it again is idempotent. Names the rows, never the subject.
412
+ */
413
+ private clearPending;
414
+ /** Whether a request to erase the subject is parked (registered in memory or restored from either row form). */
415
+ private isParked;
416
+ private unpark;
417
+ /**
418
+ * The keyed digests a control row may name the subject by: one per held key, the current key's first
419
+ * (`[]` without `controlStreamKey`). Each part is length-prefixed, so no (tenant, subject) pair can
420
+ * be spelled as another.
421
+ */
422
+ private subjectRefs;
423
+ /** How a control row names the subject: the current key's digest and its id, or (no key) the plaintext id and tenant. */
424
+ private subjectFields;
425
+ /** Whether a control row names the subject, in either form (a plaintext row predates the key). */
426
+ private rowNames;
427
+ /**
428
+ * ADR 0139 D12 - a PLAINTEXT archive holds other chains' reserved segments naming the erased subject as
429
+ * the sender. Each is rewritten with the identity replaced by an erased marker (the segment digest masks
430
+ * the sender, so it still verifies; the segment's sender digest is recomputed in the same swap) through
431
+ * ONE atomic `ArchiveStore.replaceSegments` per key (ADR 0139 N1: a trim on another node that appends a
432
+ * segment after this read makes the swap fail, and the key is re-read and retried - never lost, never
433
+ * restored over). The INTENT (the keys) is journaled first (`ERASURE_SENDER_REDACTION_INTENDED`) and the
434
+ * completion after (`ERASURE_SENDER_REDACTED`), so a crash between the two is finished by the retry from
435
+ * the intent and a redaction is never unrecorded. Cost: ONE pass over the archive's reserved keys per
436
+ * erasure (a `listExecutions` prefix walk and a `read` of each key; not indexed - an erasure is rare).
437
+ * A live, not yet archived envelope keeps its sender until it is trimmed (a plaintext store has no key to
438
+ * shred; the sender is sealed by default in a sealing deployment, ADR 0139 N6).
439
+ */
440
+ private redactSenderCopies;
441
+ /** `segments` with the matching senders replaced by the erased marker, and the sender digest recomputed. */
442
+ private redactedSegments;
443
+ /** Every reserved key (inbox, quarantine, late run) the archive holds, by prefix walk. */
444
+ private reservedArchiveKeys;
445
+ /**
446
+ * Restore what an EARLIER build's staged rewrite left behind (a `__loom_redact__:<key>` whose original
447
+ * is gone is copied back; a stage whose original is whole is dropped). The current rewrite is one atomic
448
+ * `replaceSegments` and leaves no stage, so this is a no-op on a clean archive. Run at boot (`loadParked`)
449
+ * and exposed as an operator verb (`app.recoverArchiveRedactions`). Returns the keys restored.
450
+ */
451
+ recoverRedactions(): Promise<string[]>;
452
+ /**
453
+ * The open intent of `intentType` for the subject: journaled and not yet completed by a `completionType`
454
+ * event naming it (`intentId`). The latest one, or `undefined`.
455
+ */
456
+ private openIntent;
457
+ private appendIntent;
458
+ private appendCompletion;
459
+ private appendControl;
460
+ /** The plaintext archives (`sealsSegments` is not `true`); a sealing one is erased by the shred. */
461
+ private plaintextArchives;
462
+ /** Every archive key holding the subject's copies: its executions' compaction segments and its chains' reserved segments. */
463
+ private archivedKeys;
464
+ private assertArchivesErasable;
465
+ /** Does the content store seal per subject (so the shred already erased its blobs)? */
466
+ private sealsContent;
467
+ /** Every claim-check ref one execution cites: its live log, its archive segments and its snapshot. */
468
+ private refsOfExecution;
469
+ /**
470
+ * BEFORE the shred: collect the claim-check refs the subject's executions cite, drop every one another
471
+ * (non-erased) execution still cites, refuse when the remainder cannot be deleted, and journal the INTENT
472
+ * (`ERASURE_CONTENT_DELETE_INTENDED`: the addresses and the touch cutoff). A retry after a crash finds the
473
+ * open intent and finishes it. `undefined`: nothing to do (no content store sealing, no refs, no intent).
474
+ */
475
+ private planContentDeletion;
476
+ /** AFTER the shred: delete the planned blobs, then journal the completion naming the intent. */
477
+ private finishContentDeletion;
478
+ /**
479
+ * ADR 0138 / 0139 - the physical half of erasure for the ARCHIVES: delete the subject's compaction
480
+ * segments and its chains' reserved-stream segments from every plaintext archive. ADR 0139 N4: the
481
+ * INTENT (the keys) is journaled BEFORE anything is deleted (`ERASURE_ARCHIVE_DELETE_INTENDED`) and the
482
+ * completion after (`ERASURE_ARCHIVE_DELETED`, naming the intent), so a crash between the delete and the
483
+ * record is finished by the retry from the intent: a deletion is never unrecorded. Idempotent: a retry
484
+ * with nothing left and no open intent records nothing. Over a sealing archive the shred has already made
485
+ * the segments unreadable, so nothing is deleted.
486
+ */
487
+ private deleteArchivedCopies;
488
+ /**
489
+ * ADR 0110 follow-up — erasure reaches payloads still in flight in a RESERVED inbox stream.
490
+ *
491
+ * A chain's inbox (`__loom_inbox__:<chainId>`, and with per-topic streams `…#<hash>` and `…#reg`) holds undelivered messages for its owner. The
492
+ * runtime deletes it when the chain's head turns terminal, but a crash between a sender's append
493
+ * and its re-read can leave an orphan, and until it is reclaimed it holds the subject's data.
494
+ * Once the subject is erased (every execution of it terminal, so nothing can consume) the stream
495
+ * of each execution that is the HEAD of its chain (terminal and not `continued`: a continued
496
+ * execution's successor owns the stream) is deleted. Idempotent, and a no-op for a store that
497
+ * cannot delete or an execution whose stream is already gone.
498
+ *
499
+ * This is the physical half. The cryptographic half is the key: the crypto plugin seals inbox
500
+ * payloads under the OWNER's subject, so `shred` makes a payload unreadable even where a stream
501
+ * cannot be deleted (a consumer then receives the `{ $shredded: true }` marker, never the raw
502
+ * value).
503
+ */
504
+ private purgeInboxStreams;
505
+ /**
506
+ * The inbox streams of the chains whose HEAD is among `executions`: terminal and not `continued`
507
+ * (a continued execution's successor owns the chain's inbox and is judged on its own events).
508
+ * Folded, and each chain's registry read, while the logs are still readable, so a decrypting store
509
+ * has not yet lost its key.
510
+ */
511
+ private finishedChains;
512
+ /** The window proof for one call: the operator's per-call `force` asserts it, else the configured proof. */
513
+ private proofFor;
514
+ /** Subjects with a deferred erasure still awaiting completion (sorted, deduped across tenants). */
515
+ pendingSubjects(): string[];
516
+ /** The subset of `executions` that are not yet terminal (a live fold reading the key), with status. */
517
+ private liveExecutions;
518
+ /**
519
+ * The log, or — when its subject is ALREADY shredded (a direct `shredSubject`, a retry, or the
520
+ * window between the key's destruction and its tombstone) — the redacted view the typed error
521
+ * carries: status and tip are plaintext, so liveness and the
522
+ * chain's head can still be judged, and a re-run of the erasure stays idempotent.
523
+ */
524
+ private readErasedOk;
525
+ /**
526
+ * The latest checkpoint, or — in the destroy window — its at-rest form. A sealing snapshot store
527
+ * throws `KeyUnavailableError` for a sealed checkpoint whose key is destroyed while no
528
+ * `PAYLOAD_SHREDDED` exists yet (the crash between `keys.destroy` and the tombstones), and
529
+ * `PayloadShreddedError` once a shred is recorded. Both are handled exactly like the event read
530
+ * (`readErasedOk`) and `shredSubject` (`loadLatestSealed`): the proven shred reads sealed, the
531
+ * unavailable key only when the log holds this subject's erase intent AND `proof` shows the key
532
+ * store destroyed it. Anything else (a key merely missing, no intent) is rethrown: real key loss
533
+ * is never masked. `opaque` is set when the sealed state carries no plaintext status.
534
+ */
535
+ private readSnapshotErasedOk;
536
+ /** One execution's folded status and its first event, or `undefined` when its log is gone. */
537
+ private foldExecution;
538
+ private appendPending;
539
+ }
540
+ //# sourceMappingURL=eraseSubject.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eraseSubject.d.ts","sourceRoot":"","sources":["../src/eraseSubject.ts"],"names":[],"mappings":"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"}