@byok-sdk/cloud-dataplane 0.5.0 → 0.6.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/README.md +74 -5
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1041 -289
- package/dist/index.js.map +1 -1
- package/dist/migrate.d.ts +42 -0
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +444 -247
- package/dist/runtime.js.map +1 -1
- package/dist/sql/0009_task_cancellation.sql +6 -0
- package/dist/sql/0010_tenant_readiness.sql +21 -0
- package/dist/sql/0011_tenant_erasure.sql +46 -0
- package/dist/stores/core/mailbox.d.ts +13 -0
- package/dist/stores/devices.d.ts +3 -2
- package/dist/stores/index.d.ts +1 -0
- package/dist/stores/r2-blobs.d.ts +1 -1
- package/dist/stores/task-attempts.d.ts +14 -0
- package/dist/stores/task-cancellations.d.ts +9 -0
- package/dist/tenant-erasure.d.ts +77 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -5,14 +5,15 @@ implementations of **all ten cloud-local store ports and all seven `@byok-sdk/co
|
|
|
5
5
|
ports**, the R2/S3 object adapter that backs the blob port, and the forward-only
|
|
6
6
|
migration runner that creates the tables they read.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Four store/maintenance compositions plus one transaction authority ship from here. `createPostgresCloudStores` supplies the full
|
|
9
9
|
`CloudStores` bundle (`activity`, `devices`, `pairingCodes`, `nonces`, `dedup`, `tasks`,
|
|
10
10
|
`receipts`, `proofReceipts`, `blobs`, `rateLimiter`); `createPostgresCoreStores`
|
|
11
11
|
supplies the full `CoreStores` bundle (`mailbox`, `board`, `truth`, `presence`,
|
|
12
12
|
`objects`, `quota`, `skillPacks`). Both return every port rather than a subset,
|
|
13
13
|
because the conformance suites certify a composition as a whole — there is no
|
|
14
14
|
partial bundle for them to run. `createPostgresCloudMaintenance` is the third,
|
|
15
|
-
host-only operational composition;
|
|
15
|
+
host-only operational composition; `createPostgresTenantErasure` is the fourth,
|
|
16
|
+
Node-only destructive operator composition. Both deliberately sit outside port
|
|
16
17
|
inventories.
|
|
17
18
|
|
|
18
19
|
`PostgresDeviceAssertionReplayAuthority` is a separate online security
|
|
@@ -54,14 +55,15 @@ the SDK never detects where it is running and never falls back between the two.
|
|
|
54
55
|
|
|
55
56
|
- **Node/VPS resident service.** Import the package root
|
|
56
57
|
(`@byok-sdk/cloud-dataplane`) and talk to Postgres and R2/S3 directly. The
|
|
57
|
-
migration runner
|
|
58
|
+
migration runner, cleanup/maintenance, and tenant erasure run in-process:
|
|
58
59
|
this is the entry that carries the Node-only operations.
|
|
59
60
|
- **Cloudflare Workers.** Import `@byok-sdk/cloud-dataplane/runtime` — the
|
|
60
61
|
online request path alone (pool, both store compositions, R2 blob store,
|
|
61
62
|
truth committer). Its graph never reaches a node builtin beyond what
|
|
62
63
|
`nodejs_compat` provides, so `pg` stays external in this package's build and
|
|
63
64
|
is satisfied by the platform: Hyperdrive terminates the database connection,
|
|
64
|
-
and R2 is reached over `fetch` with `aws4fetch`. Migrations
|
|
65
|
+
and R2 is reached over `fetch` with `aws4fetch`. Migrations, cleanup, and
|
|
66
|
+
tenant erasure have
|
|
65
67
|
no place on a Worker — they read files off disk — so run them from a CI job
|
|
66
68
|
or an operator's Node process against the direct DSN:
|
|
67
69
|
|
|
@@ -168,6 +170,58 @@ those ports. It sits here rather than inside `@byok-sdk/cloud` for two reasons:
|
|
|
168
170
|
and `@byok-sdk/cloud` stay loadable on Workers precisely because `pg` never enters
|
|
169
171
|
their dependency graph.
|
|
170
172
|
|
|
173
|
+
## Tenant erasure
|
|
174
|
+
|
|
175
|
+
`createPostgresTenantErasure` is a separate Node-only operator primitive, not a
|
|
176
|
+
zero-retention shortcut and not `PostgresCloudCleanup` with a different cursor.
|
|
177
|
+
It owns the product-table inventory, FK-safe deletion order, one operation id,
|
|
178
|
+
CAS lease, bounded R2/SQL pages, and completed receipt. The
|
|
179
|
+
`tenant_erasure_operation` row is package/operator control evidence rather than
|
|
180
|
+
tenant product data: it deliberately remains after completion. The migration
|
|
181
|
+
ledger, deployment config, and every other tenant are never touched.
|
|
182
|
+
|
|
183
|
+
The host must authorize the request and quiesce product writes before starting;
|
|
184
|
+
the SDK cannot manufacture a host write fence. Use a direct Postgres DSN, never
|
|
185
|
+
a request-path pooler, and configure the exact same immutable `keyPrefix` used
|
|
186
|
+
by the serving blob composition.
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
import {
|
|
190
|
+
createByokPool,
|
|
191
|
+
createPostgresTenantErasure,
|
|
192
|
+
type TenantErasureResult,
|
|
193
|
+
} from '@byok-sdk/cloud-dataplane';
|
|
194
|
+
|
|
195
|
+
const pool = createByokPool({ connectionString: env.BYOK_DIRECT_DATABASE_URL });
|
|
196
|
+
const erasure = createPostgresTenantErasure({
|
|
197
|
+
pool,
|
|
198
|
+
clock: { now: () => new Date() },
|
|
199
|
+
objectStorage: {
|
|
200
|
+
endpoint: env.R2_ENDPOINT,
|
|
201
|
+
bucket: env.R2_BUCKET,
|
|
202
|
+
accessKeyId: env.R2_ACCESS_KEY_ID,
|
|
203
|
+
secretAccessKey: env.R2_SECRET_ACCESS_KEY,
|
|
204
|
+
region: 'auto',
|
|
205
|
+
keyPrefix: 'production', // omit only when the deployment has always been unprefixed
|
|
206
|
+
signingClock: { now: () => new Date() },
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const result: TenantErasureResult = await erasure.eraseTenant(tenantId, operationId);
|
|
211
|
+
// `outstanding` / `partial`: retry the same operationId; `conflict`: another
|
|
212
|
+
// unfinished operation owns this tenant; `completed`: retain the receipt.
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Erasure lists only the canonical `tenants/<tenant>/objects/sha256/...` namespace
|
|
216
|
+
(with the configured deployment prefix), deletes each listed canonical object
|
|
217
|
+
before any product table page, and persists progress only after the delete
|
|
218
|
+
returns. A missing/canonical object is a replay-safe delete; a malformed key
|
|
219
|
+
under that namespace is schema/layout drift and returns typed `partial` without
|
|
220
|
+
starting SQL deletion. A response loss/crash between R2 and SQL replays the
|
|
221
|
+
object deletion before it can advance to rows. See
|
|
222
|
+
`deploy/runbooks/tenant-erasure.md` for the operator sequence and recovery
|
|
223
|
+
rules.
|
|
224
|
+
|
|
171
225
|
Dependency direction is one-way: `cloud-dataplane → core + cloud + protocol +
|
|
172
226
|
pg +` the explicit S3 signer/XML parser. The protocol edge is used only by the
|
|
173
227
|
host-owned dead-letter replay path to rebind frozen envelope bytes to the new
|
|
@@ -187,11 +241,18 @@ vendoring the SQL into your own repository would make that copy a second source
|
|
|
187
241
|
of truth, free to drift from the runner installed beside it.
|
|
188
242
|
|
|
189
243
|
```ts
|
|
190
|
-
import {
|
|
244
|
+
import {
|
|
245
|
+
createByokPool,
|
|
246
|
+
migrate,
|
|
247
|
+
migrationsDir,
|
|
248
|
+
verifyMigrations,
|
|
249
|
+
} from '@byok-sdk/cloud-dataplane';
|
|
191
250
|
|
|
192
251
|
const pool = createByokPool({ connectionString: process.env.DATABASE_URL! });
|
|
193
252
|
const result = await migrate(pool, migrationsDir());
|
|
194
253
|
console.log(result.applied); // e.g. ['0001_cloud_local.sql', ..., '0004_device_proof_truth.sql']
|
|
254
|
+
const applied = await verifyMigrations(pool);
|
|
255
|
+
console.log(applied); // exact ordered [{ version, checksum }, ...] rows
|
|
195
256
|
```
|
|
196
257
|
|
|
197
258
|
`migrate` still takes its directory explicitly, because the same runner also
|
|
@@ -218,6 +279,14 @@ The runner:
|
|
|
218
279
|
A consequence of per-file transactions: a statement that cannot run inside one
|
|
219
280
|
(`CREATE INDEX CONCURRENTLY`) cannot appear in a migration file.
|
|
220
281
|
|
|
282
|
+
`verifyMigrations(pool, directory = migrationsDir())` is a Node-only root export
|
|
283
|
+
for readiness/readback checks. It reads the package migration files and the
|
|
284
|
+
existing `byok_schema_migration` ledger, returning exact rows in migration order
|
|
285
|
+
only when every version and checksum matches. Missing rows, unexpected rows,
|
|
286
|
+
checksum drift, and an absent ledger table throw one aggregate
|
|
287
|
+
`MigrationStateMismatchError` with stable `issues` ordering. Verification never
|
|
288
|
+
creates the ledger or applies migrations; `./runtime` does not export it.
|
|
289
|
+
|
|
221
290
|
## Pool
|
|
222
291
|
|
|
223
292
|
`createByokPool` exists to configure one thing that matters: `int8` columns
|
package/dist/index.d.ts
CHANGED
|
@@ -27,8 +27,10 @@
|
|
|
27
27
|
* tsup pass over `src/runtime.ts`, and pinned by the runtime-entry test.
|
|
28
28
|
*/
|
|
29
29
|
export * from './runtime';
|
|
30
|
-
export { MigrationChecksumMismatchError, MigrationFilenameError, migrate, readMigrationFiles } from './migrate';
|
|
31
|
-
export type { MigrationFile, MigrationResult } from './migrate';
|
|
30
|
+
export { MigrationChecksumMismatchError, MigrationFilenameError, MigrationStateMismatchError, migrate, readMigrationFiles, verifyMigrations, } from './migrate';
|
|
31
|
+
export type { MigrationFile, MigrationResult, MigrationStateIssue, MigrationVerificationRow, } from './migrate';
|
|
32
32
|
export { migrationsDir } from './migrations-dir';
|
|
33
33
|
export { CLOUD_CLEANUP_ERROR_CODES, CloudCleanupError, PostgresCloudCleanup, createPostgresCloudMaintenance, } from './cleanup';
|
|
34
34
|
export type { CleanupJobState, CloudCleanupErrorCode, CloudCleanupResult, DeadLetterPage, DeadLetterQuery, DeadLetterRef, DeadLetterReplayInput, ObjectUsageRebuildResult, PostgresCloudCleanupOptions, PostgresCloudMaintenanceOptions, TenantRetentionPolicy, TenantRetentionPolicyInput, } from './cleanup';
|
|
35
|
+
export { TENANT_ERASURE_ERROR_CODES, TENANT_ERASURE_TABLES, PostgresTenantErasure, TenantErasureError, createPostgresTenantErasure, } from './tenant-erasure';
|
|
36
|
+
export type { PostgresTenantErasureCompositionOptions, PostgresTenantErasureOptions, TenantErasureConflict, TenantErasureErrorCode, TenantErasureReadback, TenantErasureResult, TenantErasureStatus, } from './tenant-erasure';
|