dpdp-erasure-cli 1.0.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/.env.example +55 -0
- package/Dockerfile +33 -0
- package/compliance.worker.yaml +64 -0
- package/package.json +41 -0
- package/src/constants/index.ts +1 -0
- package/src/errors/fail.ts +110 -0
- package/src/errors/index.ts +4 -0
- package/src/errors/inferer.ts +166 -0
- package/src/errors/registry.ts +122 -0
- package/src/errors/types.ts +65 -0
- package/src/errors/worker.ts +161 -0
- package/src/index.ts +328 -0
- package/src/lib/crypto/digest.ts +22 -0
- package/src/lib/crypto/encoding.ts +78 -0
- package/src/lib/crypto/index.ts +2 -0
- package/src/lib/index.ts +1 -0
- package/src/modules/bootstrap/index.ts +2 -0
- package/src/modules/bootstrap/integrity.ts +38 -0
- package/src/modules/bootstrap/preflight.ts +296 -0
- package/src/modules/cli/check-integrity.ts +48 -0
- package/src/modules/cli/dry-run.ts +90 -0
- package/src/modules/cli/graph.ts +87 -0
- package/src/modules/cli/index.ts +184 -0
- package/src/modules/cli/init.ts +115 -0
- package/src/modules/cli/inspect.ts +86 -0
- package/src/modules/cli/introspector.ts +117 -0
- package/src/modules/cli/keygen.ts +38 -0
- package/src/modules/cli/scan.ts +126 -0
- package/src/modules/cli/sign.ts +50 -0
- package/src/modules/cli/ui.ts +61 -0
- package/src/modules/cli/verify-schema.ts +31 -0
- package/src/modules/cli/verify.ts +85 -0
- package/src/modules/config/compatibility.ts +271 -0
- package/src/modules/config/index.ts +4 -0
- package/src/modules/config/reader.ts +149 -0
- package/src/modules/config/signature.ts +69 -0
- package/src/modules/config/validation.ts +658 -0
- package/src/modules/crypto/aes.ts +158 -0
- package/src/modules/crypto/envelope.ts +48 -0
- package/src/modules/crypto/hmac.ts +60 -0
- package/src/modules/crypto/index.ts +3 -0
- package/src/modules/db/drift.ts +36 -0
- package/src/modules/db/graph.ts +203 -0
- package/src/modules/db/index.ts +4 -0
- package/src/modules/db/migrations.ts +254 -0
- package/src/modules/db/sql-debug.ts +61 -0
- package/src/modules/engine/blob/index.ts +3 -0
- package/src/modules/engine/blob/s3.ts +455 -0
- package/src/modules/engine/blob/store.ts +236 -0
- package/src/modules/engine/blob/types.ts +44 -0
- package/src/modules/engine/helpers/identity.ts +47 -0
- package/src/modules/engine/helpers/index.ts +4 -0
- package/src/modules/engine/helpers/outbox.ts +118 -0
- package/src/modules/engine/helpers/runtime.ts +115 -0
- package/src/modules/engine/helpers/types.ts +61 -0
- package/src/modules/engine/index.ts +6 -0
- package/src/modules/engine/notifier/config.ts +147 -0
- package/src/modules/engine/notifier/dispatcher.ts +300 -0
- package/src/modules/engine/notifier/index.ts +3 -0
- package/src/modules/engine/notifier/payload.ts +51 -0
- package/src/modules/engine/notifier/reservation.ts +153 -0
- package/src/modules/engine/notifier/types.ts +38 -0
- package/src/modules/engine/shredder.ts +254 -0
- package/src/modules/engine/types.ts +146 -0
- package/src/modules/engine/vault/compiled-targets.ts +562 -0
- package/src/modules/engine/vault/context.ts +254 -0
- package/src/modules/engine/vault/dry-run.ts +94 -0
- package/src/modules/engine/vault/execution.ts +485 -0
- package/src/modules/engine/vault/index.ts +3 -0
- package/src/modules/engine/vault/purge.ts +82 -0
- package/src/modules/engine/vault/retention.ts +124 -0
- package/src/modules/engine/vault/satellite-mutation.ts +193 -0
- package/src/modules/engine/vault/satellite.ts +103 -0
- package/src/modules/engine/vault/shadow.ts +36 -0
- package/src/modules/engine/vault/static-plan.ts +116 -0
- package/src/modules/engine/vault/store.ts +34 -0
- package/src/modules/engine/vault/vault.ts +84 -0
- package/src/modules/introspector/classifier.ts +502 -0
- package/src/modules/introspector/dag.ts +276 -0
- package/src/modules/introspector/index.ts +7 -0
- package/src/modules/introspector/naming.ts +75 -0
- package/src/modules/introspector/report.ts +153 -0
- package/src/modules/introspector/run.ts +123 -0
- package/src/modules/introspector/s3-sampler.ts +227 -0
- package/src/modules/introspector/types.ts +131 -0
- package/src/modules/introspector/yaml.ts +101 -0
- package/src/modules/network/api/control-plane.ts +275 -0
- package/src/modules/network/api/index.ts +1 -0
- package/src/modules/network/api/validation.ts +71 -0
- package/src/modules/network/index.ts +4 -0
- package/src/modules/network/object-store/aws/client.ts +444 -0
- package/src/modules/network/object-store/aws/credentials.ts +271 -0
- package/src/modules/network/object-store/aws/index.ts +2 -0
- package/src/modules/network/object-store/aws/sigv4.ts +190 -0
- package/src/modules/network/object-store/aws/type.ts +6 -0
- package/src/modules/network/object-store/index.ts +1 -0
- package/src/modules/network/outbox/dispatcher.ts +183 -0
- package/src/modules/network/outbox/index.ts +3 -0
- package/src/modules/network/outbox/process.ts +133 -0
- package/src/modules/network/outbox/shared.ts +56 -0
- package/src/modules/network/outbox/store.ts +346 -0
- package/src/modules/network/outbox/types.ts +54 -0
- package/src/modules/network/request-signing.ts +61 -0
- package/src/modules/worker/index.ts +2 -0
- package/src/modules/worker/tasks.ts +58 -0
- package/src/modules/worker/types.ts +89 -0
- package/src/modules/worker/worker.ts +243 -0
- package/src/secrets/index.ts +4 -0
- package/src/secrets/kms/index.ts +2 -0
- package/src/secrets/kms/signature.ts +82 -0
- package/src/secrets/kms/validation.ts +64 -0
- package/src/secrets/reader.ts +42 -0
- package/src/secrets/repository/crypto.ts +89 -0
- package/src/secrets/repository/index.ts +2 -0
- package/src/secrets/repository/methods.ts +37 -0
- package/src/secrets/resolvers.ts +247 -0
- package/src/secrets/signature.ts +78 -0
- package/src/types/index.ts +1 -0
- package/src/types/types.ts +23 -0
- package/src/utils/identifiers.ts +48 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/json.ts +35 -0
- package/src/utils/logger.ts +161 -0
- package/src/validation/zod.ts +70 -0
- package/tests/adversarial.test.ts +464 -0
- package/tests/blob-s3.test.ts +216 -0
- package/tests/config.test.ts +395 -0
- package/tests/control-plane-client.test.ts +108 -0
- package/tests/crypto.test.ts +106 -0
- package/tests/errors.test.ts +69 -0
- package/tests/fetch-dispatcher.test.ts +213 -0
- package/tests/graph.test.ts +84 -0
- package/tests/helpers/index.ts +101 -0
- package/tests/index-preflight.test.ts +168 -0
- package/tests/introspector-classifier.test.ts +62 -0
- package/tests/introspector-report.test.ts +85 -0
- package/tests/introspector.test.ts +394 -0
- package/tests/kms.test.ts +124 -0
- package/tests/logger.test.ts +61 -0
- package/tests/notifier.test.ts +303 -0
- package/tests/outbox.test.ts +478 -0
- package/tests/purge-policy.test.ts +124 -0
- package/tests/retention.test.ts +103 -0
- package/tests/s3-client.test.ts +110 -0
- package/tests/satellite.test.ts +119 -0
- package/tests/schema-compatibility.test.ts +237 -0
- package/tests/schema-integrity.test.ts +64 -0
- package/tests/shredder.test.ts +163 -0
- package/tests/vault.compiled-targets.test.ts +243 -0
- package/tests/vault.replica.test.ts +59 -0
- package/tests/vault.test.ts +279 -0
- package/tests/worker.retry.test.ts +291 -0
- package/tests/worker.test.ts +200 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { CODE, fail } from "@/errors";
|
|
2
|
+
import { getLogger } from "@/utils";
|
|
3
|
+
import type { Sql } from "@/types";
|
|
4
|
+
import { countPendingBlobObjectsForUser, shredBlobObjects } from "./blob";
|
|
5
|
+
import type { ShredUserOptions, ShredUserResult } from "./types";
|
|
6
|
+
import { DESTROYED_PII_SENTINEL, enqueueOutboxEvent, resolveSchemas } from "./helpers";
|
|
7
|
+
import { getVaultRecordByUserId } from "./vault/store";
|
|
8
|
+
|
|
9
|
+
const logger = getLogger({ component: "shredder" });
|
|
10
|
+
|
|
11
|
+
function buildShredDryRunPlan(
|
|
12
|
+
appSchema: string,
|
|
13
|
+
engineSchema: string,
|
|
14
|
+
rootTable: string,
|
|
15
|
+
subjectId: string | number,
|
|
16
|
+
userHash: string,
|
|
17
|
+
retentionExpiry: Date
|
|
18
|
+
) {
|
|
19
|
+
return {
|
|
20
|
+
mode: "dry-run" as const,
|
|
21
|
+
summary: `Would crypto-shred root row ${subjectId} (${userHash}) in ${appSchema}.${rootTable} after ${retentionExpiry.toISOString()}.`,
|
|
22
|
+
checks: [
|
|
23
|
+
`Read ${engineSchema}.pii_vault using (${appSchema}, ${rootTable}, ${subjectId}) as the lookup key.`,
|
|
24
|
+
"Confirm that retention_expiry has passed.",
|
|
25
|
+
"Require a completed notification unless explicitly disabled.",
|
|
26
|
+
"Delete the DEK and replace the vault payload in one transaction.",
|
|
27
|
+
],
|
|
28
|
+
cryptoSteps: [
|
|
29
|
+
"Delete the encrypted DEK from the key ring.",
|
|
30
|
+
"Leave only non-PII metadata and a destroyed sentinel in the vault row.",
|
|
31
|
+
],
|
|
32
|
+
sqlSteps: [
|
|
33
|
+
"BEGIN ISOLATION LEVEL REPEATABLE READ;",
|
|
34
|
+
`SELECT * FROM ${engineSchema}.pii_vault WHERE root_schema = '${appSchema}' AND root_table = '${rootTable}' AND root_id = '${subjectId}' FOR UPDATE;`,
|
|
35
|
+
`DELETE FROM ${engineSchema}.user_keys WHERE user_uuid_hash = '<user-hash>';`,
|
|
36
|
+
`UPDATE ${engineSchema}.pii_vault SET encrypted_pii = '{"destroyed":true}', shredded_at = '<timestamp>';`,
|
|
37
|
+
`INSERT INTO ${engineSchema}.outbox (...) VALUES (... 'SHRED_SUCCESS' ...);`,
|
|
38
|
+
"COMMIT;",
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Destroys the DEK and replaces vaulted ciphertext with a non-PII sentinel.
|
|
45
|
+
*
|
|
46
|
+
* The function enforces fail-closed shredding semantics:
|
|
47
|
+
* 1. Retention must be fully elapsed.
|
|
48
|
+
* 2. Pre-erasure notice must be sent unless explicitly bypassed.
|
|
49
|
+
* 3. Key deletion and vault mutation happen atomically inside one repeatable-read transaction.
|
|
50
|
+
*
|
|
51
|
+
* @param sql - Postgres connection pool used for transactional shredding.
|
|
52
|
+
* @param subjectId - Subject identifier from the root table.
|
|
53
|
+
* @param options - Shredding overrides such as schema/table, dry-run mode, and clock injection.
|
|
54
|
+
* @returns Structured shred result describing whether shredding executed, was skipped, or was simulated.
|
|
55
|
+
* @throws {WorkerError} When retention/notice preconditions fail or key/vault invariants are broken.
|
|
56
|
+
*/
|
|
57
|
+
export async function shredUser(
|
|
58
|
+
sql: Sql,
|
|
59
|
+
subjectId: string | number,
|
|
60
|
+
options: ShredUserOptions = {}
|
|
61
|
+
): Promise<ShredUserResult> {
|
|
62
|
+
if ((typeof subjectId !== "string" && typeof subjectId !== "number") || String(subjectId).trim().length === 0) {
|
|
63
|
+
fail({
|
|
64
|
+
code: `SHREDDER_${CODE.USER_ID_INVALID}`
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const normalizedSubjectId = String(subjectId);
|
|
69
|
+
const { appSchema, engineSchema } = resolveSchemas(options);
|
|
70
|
+
const rootTable = options.rootTable ?? "users";
|
|
71
|
+
const now = options.now ? new Date(options.now) : new Date();
|
|
72
|
+
const requireNotification = options.requireNotification ?? true;
|
|
73
|
+
|
|
74
|
+
const vault = await getVaultRecordByUserId(sql, engineSchema, appSchema, normalizedSubjectId, rootTable);
|
|
75
|
+
if (!vault) {
|
|
76
|
+
fail({
|
|
77
|
+
code: `SHREDDER_${CODE.VAULT_NOT_FOUND}`,
|
|
78
|
+
data: { appSchema, rootTable, normalizedSubjectId }
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (options.dryRun) {
|
|
83
|
+
return {
|
|
84
|
+
action: "dry_run",
|
|
85
|
+
userHash: vault.user_uuid_hash,
|
|
86
|
+
dryRun: true,
|
|
87
|
+
shreddedAt: vault.shredded_at ? vault.shredded_at.toISOString() : null,
|
|
88
|
+
outboxEventType: "SHRED_SUCCESS",
|
|
89
|
+
plan: buildShredDryRunPlan(
|
|
90
|
+
appSchema,
|
|
91
|
+
engineSchema,
|
|
92
|
+
rootTable,
|
|
93
|
+
normalizedSubjectId,
|
|
94
|
+
vault.user_uuid_hash,
|
|
95
|
+
new Date(vault.retention_expiry)
|
|
96
|
+
),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return sql.begin("isolation level repeatable read", async (tx) => {
|
|
101
|
+
await tx.unsafe("SET LOCAL lock_timeout = '5s'");
|
|
102
|
+
const [transactionRow] = await tx<{ txid: string }[]>`
|
|
103
|
+
SELECT txid_current()::text AS txid
|
|
104
|
+
`;
|
|
105
|
+
const postgresTransactionId = transactionRow?.txid ?? null;
|
|
106
|
+
|
|
107
|
+
const [lockedVault] = await tx`
|
|
108
|
+
SELECT *
|
|
109
|
+
FROM ${tx(engineSchema)}.pii_vault
|
|
110
|
+
WHERE root_schema = ${appSchema}
|
|
111
|
+
AND root_table = ${rootTable}
|
|
112
|
+
AND root_id = ${normalizedSubjectId}
|
|
113
|
+
FOR UPDATE
|
|
114
|
+
`;
|
|
115
|
+
|
|
116
|
+
if (!lockedVault) {
|
|
117
|
+
fail({
|
|
118
|
+
code: "SHREDDER_VAULT_LOST",
|
|
119
|
+
title: "Vault record vanished during shredding",
|
|
120
|
+
detail: `Vault record for ${appSchema}.${rootTable}#${normalizedSubjectId} disappeared during shredding.`,
|
|
121
|
+
category: "concurrency",
|
|
122
|
+
retryable: true,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (lockedVault.shredded_at) {
|
|
127
|
+
return {
|
|
128
|
+
action: "already_shredded",
|
|
129
|
+
userHash: lockedVault.user_uuid_hash,
|
|
130
|
+
dryRun: false,
|
|
131
|
+
shreddedAt: new Date(lockedVault.shredded_at).toISOString(),
|
|
132
|
+
outboxEventType: null,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (new Date(lockedVault.retention_expiry) > now) {
|
|
137
|
+
fail({
|
|
138
|
+
code: "SHREDDER_RETENTION_NOT_REACHED",
|
|
139
|
+
title: "Retention window still active",
|
|
140
|
+
detail: `Cannot shred root row ${normalizedSubjectId} before retention expiry (${new Date(lockedVault.retention_expiry).toISOString()}).`,
|
|
141
|
+
category: "validation",
|
|
142
|
+
retryable: false,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (requireNotification && !lockedVault.notification_sent_at) {
|
|
147
|
+
fail({
|
|
148
|
+
code: "SHREDDER_NOTICE_MISSING",
|
|
149
|
+
title: "Pre-erasure notice missing",
|
|
150
|
+
detail: `Cannot shred root row ${normalizedSubjectId} before the pre-erasure notice has been sent.`,
|
|
151
|
+
category: "validation",
|
|
152
|
+
retryable: false,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const pendingBlobCount = await countPendingBlobObjectsForUser(
|
|
157
|
+
tx,
|
|
158
|
+
engineSchema,
|
|
159
|
+
lockedVault.user_uuid_hash
|
|
160
|
+
);
|
|
161
|
+
if (pendingBlobCount > 0 && !options.hmacKey) {
|
|
162
|
+
fail({
|
|
163
|
+
code: "SHREDDER_BLOB_HMAC_KEY_MISSING",
|
|
164
|
+
title: "Blob shred HMAC key missing",
|
|
165
|
+
detail: "Blob deletion receipts require the worker HMAC key so raw S3 object paths never leave the VPC.",
|
|
166
|
+
category: "configuration",
|
|
167
|
+
retryable: false,
|
|
168
|
+
fatal: true,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const deletedKeys = await tx`
|
|
173
|
+
DELETE FROM ${tx(engineSchema)}.user_keys
|
|
174
|
+
WHERE user_uuid_hash = ${lockedVault.user_uuid_hash}
|
|
175
|
+
RETURNING user_uuid_hash
|
|
176
|
+
`;
|
|
177
|
+
|
|
178
|
+
if (deletedKeys.length === 0) {
|
|
179
|
+
fail({
|
|
180
|
+
code: "SHREDDER_KEY_MISSING",
|
|
181
|
+
title: "Key ring record missing",
|
|
182
|
+
detail: `Cannot shred root row ${normalizedSubjectId}: no active key exists for hash ${lockedVault.user_uuid_hash}.`,
|
|
183
|
+
category: "integrity",
|
|
184
|
+
retryable: false,
|
|
185
|
+
fatal: true,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
await tx`
|
|
190
|
+
UPDATE ${tx(engineSchema)}.pii_vault
|
|
191
|
+
SET encrypted_pii = ${tx.json(DESTROYED_PII_SENTINEL)},
|
|
192
|
+
shredded_at = ${now},
|
|
193
|
+
updated_at = ${now}
|
|
194
|
+
WHERE user_uuid_hash = ${lockedVault.user_uuid_hash}
|
|
195
|
+
`;
|
|
196
|
+
|
|
197
|
+
const blobReceipts = options.hmacKey
|
|
198
|
+
? await shredBlobObjects(
|
|
199
|
+
tx,
|
|
200
|
+
engineSchema,
|
|
201
|
+
lockedVault.user_uuid_hash,
|
|
202
|
+
options.hmacKey,
|
|
203
|
+
now,
|
|
204
|
+
options.s3Client
|
|
205
|
+
)
|
|
206
|
+
: [];
|
|
207
|
+
|
|
208
|
+
await enqueueOutboxEvent(
|
|
209
|
+
tx,
|
|
210
|
+
engineSchema,
|
|
211
|
+
lockedVault.user_uuid_hash,
|
|
212
|
+
"SHRED_SUCCESS",
|
|
213
|
+
{
|
|
214
|
+
request_id: lockedVault.request_id,
|
|
215
|
+
subject_opaque_id: lockedVault.root_id,
|
|
216
|
+
tenant_id: lockedVault.tenant_id || null,
|
|
217
|
+
trigger_source: lockedVault.trigger_source,
|
|
218
|
+
legal_framework: lockedVault.legal_framework,
|
|
219
|
+
actor_opaque_id: lockedVault.actor_opaque_id,
|
|
220
|
+
applied_rule_name: lockedVault.applied_rule_name,
|
|
221
|
+
applied_rule_citation: lockedVault.applied_rule_citation,
|
|
222
|
+
event_timestamp: now.toISOString(),
|
|
223
|
+
root_schema: appSchema,
|
|
224
|
+
root_table: rootTable,
|
|
225
|
+
root_id: normalizedSubjectId,
|
|
226
|
+
shredded_at: now.toISOString(),
|
|
227
|
+
postgres_transaction_ids: postgresTransactionId ? [postgresTransactionId] : [],
|
|
228
|
+
blob_receipts: blobReceipts,
|
|
229
|
+
},
|
|
230
|
+
lockedVault.request_id
|
|
231
|
+
? `shred:${lockedVault.request_id}`
|
|
232
|
+
: `shred:${appSchema}:${rootTable}:${normalizedSubjectId}`,
|
|
233
|
+
now
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
logger.info(
|
|
237
|
+
{
|
|
238
|
+
userHash: lockedVault.user_uuid_hash,
|
|
239
|
+
rootTable,
|
|
240
|
+
rootId: normalizedSubjectId,
|
|
241
|
+
},
|
|
242
|
+
"Root row crypto-shredded"
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
action: "shredded",
|
|
247
|
+
userHash: lockedVault.user_uuid_hash,
|
|
248
|
+
dryRun: false,
|
|
249
|
+
shreddedAt: now.toISOString(),
|
|
250
|
+
outboxEventType: "SHRED_SUCCESS",
|
|
251
|
+
blobReceiptCount: blobReceipts.length,
|
|
252
|
+
};
|
|
253
|
+
});
|
|
254
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { S3Client } from "@modules/network";
|
|
2
|
+
import type { Sql } from "@/types";
|
|
3
|
+
import type {
|
|
4
|
+
BlobTarget,
|
|
5
|
+
CompiledExecutionTargetInput,
|
|
6
|
+
RetentionRule,
|
|
7
|
+
RootPiiColumns,
|
|
8
|
+
SatelliteTarget,
|
|
9
|
+
} from "../config";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Cryptographic material required by worker mutation pipelines.
|
|
13
|
+
*/
|
|
14
|
+
export interface WorkerSecrets {
|
|
15
|
+
kek: Uint8Array;
|
|
16
|
+
hmacKey?: Uint8Array;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Cryptographic material required by worker mutation pipelines.
|
|
20
|
+
export interface WorkerSecrets {
|
|
21
|
+
kek: Uint8Array;
|
|
22
|
+
hmacKey?: Uint8Array;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Schema overrides for multi-tenant or non-default deployments.
|
|
26
|
+
export interface WorkerSchemas {
|
|
27
|
+
appSchema?: string;
|
|
28
|
+
engineSchema?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Common runtime controls for worker operations.
|
|
32
|
+
export interface WorkerTimingOptions {
|
|
33
|
+
now?: Date,
|
|
34
|
+
defaultRetentionYears?: number;
|
|
35
|
+
noticeWindowHours?: number;
|
|
36
|
+
graphMaxDepth?: number;
|
|
37
|
+
dryRun?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Human-readable execution plan returned during dry-run mode.
|
|
42
|
+
*/
|
|
43
|
+
export interface DryRunPlan {
|
|
44
|
+
mode: "dry-run";
|
|
45
|
+
summary: string;
|
|
46
|
+
checks: string[];
|
|
47
|
+
cryptoSteps: string[];
|
|
48
|
+
sqlSteps: string[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Vault operation options controlling root mutation, retention logic, and transport metadata.
|
|
53
|
+
*/
|
|
54
|
+
export interface VaultUserOptions extends WorkerSchemas, WorkerTimingOptions {
|
|
55
|
+
rootTable?: string;
|
|
56
|
+
rootIdColumn?: string;
|
|
57
|
+
rootPiiColumns?: RootPiiColumns;
|
|
58
|
+
satelliteTargets?: SatelliteTarget[];
|
|
59
|
+
blobTargets?: BlobTarget[];
|
|
60
|
+
compiledTargets?: CompiledExecutionTargetInput[];
|
|
61
|
+
retentionRules?: readonly RetentionRule[];
|
|
62
|
+
tenantId?: string;
|
|
63
|
+
requestId?: string;
|
|
64
|
+
subjectOpaqueId?: string;
|
|
65
|
+
triggerSource?: string;
|
|
66
|
+
actorOpaqueId?: string;
|
|
67
|
+
legalFramework?: string;
|
|
68
|
+
requestTimestamp?: string;
|
|
69
|
+
shadowMode?: boolean;
|
|
70
|
+
sqlReplica?: Sql;
|
|
71
|
+
s3Client?: S3Client;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Notice dispatch options.
|
|
76
|
+
*/
|
|
77
|
+
export interface DispatchNoticeOptions extends WorkerSchemas, WorkerTimingOptions {
|
|
78
|
+
rootTable?: string;
|
|
79
|
+
notificationLeaseSeconds?: number;
|
|
80
|
+
noticeEmailColumn?: string;
|
|
81
|
+
noticeNameColumn?: string;
|
|
82
|
+
rootPiiColumns?: RootPiiColumns;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Crypto-shred operation options.
|
|
87
|
+
*/
|
|
88
|
+
export interface ShredUserOptions extends WorkerSchemas, WorkerTimingOptions {
|
|
89
|
+
rootTable?: string;
|
|
90
|
+
requireNotification?: boolean;
|
|
91
|
+
hmacKey?: Uint8Array;
|
|
92
|
+
s3Client?: S3Client;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Base result fields shared by all worker operations.
|
|
97
|
+
*/
|
|
98
|
+
export interface WorkerOperationResult {
|
|
99
|
+
userHash: string | null;
|
|
100
|
+
dryRun: boolean;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Result envelope for vault/hard-delete operations.
|
|
105
|
+
*/
|
|
106
|
+
export interface VaultUserResult extends WorkerOperationResult {
|
|
107
|
+
action:
|
|
108
|
+
| "vaulted"
|
|
109
|
+
| "already_vaulted"
|
|
110
|
+
| "hard_deleted"
|
|
111
|
+
| "already_hard_deleted"
|
|
112
|
+
| "dry_run";
|
|
113
|
+
dependencyCount: number;
|
|
114
|
+
retentionYears: number | null;
|
|
115
|
+
appliedRuleName: string | null;
|
|
116
|
+
appliedRuleCitation: string | null;
|
|
117
|
+
retentionExpiry: string | null;
|
|
118
|
+
notificationDueAt: string | null;
|
|
119
|
+
pseudonym: string | null;
|
|
120
|
+
outboxEventType: string | null;
|
|
121
|
+
blobProtectionCount?: number;
|
|
122
|
+
plan?: DryRunPlan;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Result envelope for pre-erasure notice dispatch.
|
|
127
|
+
*/
|
|
128
|
+
export interface DispatchNoticeResult extends WorkerOperationResult {
|
|
129
|
+
action: "sent" | "already_sent" | "not_due" | "dry_run";
|
|
130
|
+
retentionExpiry: string | null;
|
|
131
|
+
notificationDueAt: string | null;
|
|
132
|
+
notificationSentAt: string | null;
|
|
133
|
+
outboxEventType: string | null;
|
|
134
|
+
plan?: DryRunPlan;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Result envelope for crypto-shredding.
|
|
139
|
+
*/
|
|
140
|
+
export interface ShredUserResult extends WorkerOperationResult {
|
|
141
|
+
action: "shredded" | "already_shredded" | "dry_run";
|
|
142
|
+
shreddedAt: string | null;
|
|
143
|
+
outboxEventType: string | null;
|
|
144
|
+
blobReceiptCount?: number;
|
|
145
|
+
plan?: DryRunPlan;
|
|
146
|
+
}
|