@loomwork/compliance 0.0.0-stage → 0.1.0
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.
- package/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +87 -2
- package/dist/SubjectIndex.d.ts +48 -0
- package/dist/SubjectIndex.d.ts.map +1 -0
- package/dist/SubjectIndex.js +90 -0
- package/dist/SubjectIndex.js.map +1 -0
- package/dist/eraseSubject.d.ts +540 -0
- package/dist/eraseSubject.d.ts.map +1 -0
- package/dist/eraseSubject.js +1294 -0
- package/dist/eraseSubject.js.map +1 -0
- package/dist/exportSubject.d.ts +164 -0
- package/dist/exportSubject.d.ts.map +1 -0
- package/dist/exportSubject.js +143 -0
- package/dist/exportSubject.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -3
- package/src/SubjectIndex.ts +132 -0
- package/src/eraseSubject.ts +1780 -0
- package/src/exportSubject.ts +326 -0
- package/src/index.ts +36 -0
|
@@ -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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"}
|