@substrat-run/control-plane-api 0.52.0 → 0.54.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/dist/api.d.ts.map +1 -1
- package/dist/api.js +185 -20
- package/dist/api.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/r2-backups.d.ts +68 -0
- package/dist/r2-backups.d.ts.map +1 -1
- package/dist/r2-backups.js +150 -0
- package/dist/r2-backups.js.map +1 -1
- package/dist/seal.d.ts +62 -0
- package/dist/seal.d.ts.map +1 -0
- package/dist/seal.js +132 -0
- package/dist/seal.js.map +1 -0
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export type { DoNamespaceReader, DoNamespaceRecord, CfDoNamespaceOptions } from
|
|
|
22
22
|
export { createCfObservabilityReader } from './cf-observability.js';
|
|
23
23
|
export type { CfObservabilityOptions } from './cf-observability.js';
|
|
24
24
|
export type { ScopeBackup, ScopeBackupStore, DirectoryBackup, DirectoryBackupStore, } from './backups.js';
|
|
25
|
-
export { createR2BackupStore, createR2DirectoryBackupStore } from './r2-backups.js';
|
|
25
|
+
export { createR2AccessLogSink, createR2BackupStore, createR2DirectoryBackupStore, pruneAccessLogBatches, pruneScopeBackups, } from './r2-backups.js';
|
|
26
26
|
export { backupDirectoryIfDue } from './directory-backup.js';
|
|
27
27
|
export type { DirectoryBackupOptions, DirectoryBackupResult } from './directory-backup.js';
|
|
28
28
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjD,YAAY,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,2BAA2B,EAC3B,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACrG,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,WAAW,EACX,SAAS,GACV,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACpE,YAAY,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,GAC5B,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACrG,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAChG,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,aAAa,GACd,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,+BAA+B,EAC/B,WAAW,EACX,cAAc,EACd,yBAAyB,EACzB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,yBAAyB,EACzB,gCAAgC,EAChC,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACjG,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACpF,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACrG,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AACpE,YAAY,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACpE,YAAY,EACV,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,oBAAoB,GACrB,MAAM,cAAc,CAAC;AACtB,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ export { blobStoreBindings, collectBlobStoreHandles, collectTenantStoreHandles,
|
|
|
10
10
|
export { createCustomHostnameProvisioner, mapCfStatus, extractRecords, reconcilePendingHostnames, isCustomHostname, validateBindableHostname, } from './custom-hostnames.js';
|
|
11
11
|
export { createCfDoNamespaceReader, namespacesForScript } from './do-namespaces.js';
|
|
12
12
|
export { createCfObservabilityReader } from './cf-observability.js';
|
|
13
|
-
export { createR2BackupStore, createR2DirectoryBackupStore } from './r2-backups.js';
|
|
13
|
+
export { createR2AccessLogSink, createR2BackupStore, createR2DirectoryBackupStore, pruneAccessLogBatches, pruneScopeBackups, } from './r2-backups.js';
|
|
14
14
|
// #40 — the scheduled directory copy. Exported as a function the CP worker's cron calls,
|
|
15
15
|
// not as a route with a timer behind it: this package stays library-only, exactly as the
|
|
16
16
|
// platform-request drain does (the recurrence has to come from a deployment, #444).
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAEjD,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,2BAA2B,EAC3B,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAUrG,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEpE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,GAUvB,MAAM,qBAAqB,CAAC;AAO7B,OAAO,EACL,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAQhG,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,+BAA+B,EAC/B,WAAW,EACX,cAAc,EACd,yBAAyB,EACzB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAS/B,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAQpE,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAEjD,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,2BAA2B,EAC3B,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAUrG,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEpE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,GAUvB,MAAM,qBAAqB,CAAC;AAO7B,OAAO,EACL,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAQhG,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,+BAA+B,EAC/B,WAAW,EACX,cAAc,EACd,yBAAyB,EACzB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAS/B,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAQpE,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,4BAA4B,EAC5B,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,yFAAyF;AACzF,yFAAyF;AACzF,oFAAoF;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/r2-backups.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* One bucket, held by the PLATFORM, not by the tenant. A tenant-held bucket would be
|
|
10
10
|
* deleted by the very teardown these copies exist to survive.
|
|
11
11
|
*/
|
|
12
|
+
import type { AccessLogSink } from '@substrat-run/kernel';
|
|
12
13
|
import type { DirectoryBackupStore, ScopeBackupStore } from './backups.js';
|
|
13
14
|
export declare function createR2BackupStore(bucket: unknown): ScopeBackupStore;
|
|
14
15
|
/**
|
|
@@ -21,4 +22,71 @@ export declare function createR2BackupStore(bucket: unknown): ScopeBackupStore;
|
|
|
21
22
|
* from "survives losing the directory" to "survives losing the account".
|
|
22
23
|
*/
|
|
23
24
|
export declare function createR2DirectoryBackupStore(bucket: unknown): DirectoryBackupStore;
|
|
25
|
+
/**
|
|
26
|
+
* The R2 implementation of the `AccessLogSink` seam (K-24, control-plane.md §4.4) — the
|
|
27
|
+
* Tier 2 that makes the access log's retention window closable.
|
|
28
|
+
*
|
|
29
|
+
* **NDJSON, not JSON.** One row per line, appended-shaped: an operator greps it, a SIEM
|
|
30
|
+
* ingests it line-by-line, and a truncated object still parses up to its last newline.
|
|
31
|
+
* A single JSON array would have to be whole to be readable at all, which is the wrong
|
|
32
|
+
* failure mode for evidence.
|
|
33
|
+
*
|
|
34
|
+
* **Vendor-neutral by construction**, which was the point (#36): this writes a documented
|
|
35
|
+
* line format to an object store, so Splunk, Datadog, Drata and a human with `jq` all
|
|
36
|
+
* consume the same file. A vendor-specific push connector would have coupled the platform's
|
|
37
|
+
* retention policy to one company's roadmap.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createR2AccessLogSink(bucket: unknown): AccessLogSink;
|
|
40
|
+
/**
|
|
41
|
+
* The stored-copy lifecycle rule (#557) — the retention leg #36 left unmade.
|
|
42
|
+
*
|
|
43
|
+
* Every prune here is **operator-chosen**: these functions run only when a deployment
|
|
44
|
+
* has named a window (the CP worker's `SCOPE_BACKUP_RETENTION_DAYS` /
|
|
45
|
+
* `ACCESS_LOG_RETENTION_DAYS`), never on a default. That is the #553 posture — the
|
|
46
|
+
* platform never deletes evidence on a schedule a human did not choose — applied to the
|
|
47
|
+
* copies themselves. Enforced in code rather than by an R2 bucket lifecycle rule so the
|
|
48
|
+
* policy is visible in the repo, testable against the fake bucket, and identical on any
|
|
49
|
+
* future non-R2 store.
|
|
50
|
+
*
|
|
51
|
+
* Both prunes are age-based and **conservative by construction**: an object whose age
|
|
52
|
+
* cannot be determined is kept, and a batch is deleted only when its NEWEST row is out
|
|
53
|
+
* of the window — never one that still holds in-window rows. The walk collects stale
|
|
54
|
+
* keys first and deletes after, so deletion never races the pagination cursor.
|
|
55
|
+
*/
|
|
56
|
+
interface PruneOptions {
|
|
57
|
+
/**
|
|
58
|
+
* Copies strictly older than this many days are dropped. `0` is honored (drop
|
|
59
|
+
* everything on the next pass) — an operator can ask for it, as with the reap windows.
|
|
60
|
+
*/
|
|
61
|
+
olderThanDays: number;
|
|
62
|
+
/** Injected clock, for tests. */
|
|
63
|
+
now?: () => Date;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Drop `scopes/…` reap copies (#493) older than the window. A copy's age is its
|
|
67
|
+
* `capturedAt` — the key's last segment, same as the store's own fallback address — so a
|
|
68
|
+
* hand-uploaded object without metadata still ages out rather than living forever by
|
|
69
|
+
* omission. An unparsable timestamp keeps the copy: deleting what cannot be dated is not
|
|
70
|
+
* retention.
|
|
71
|
+
*
|
|
72
|
+
* Note the interaction with #37 (erasure): per-subject payloads inside these dumps are
|
|
73
|
+
* sealed under per-subject DEKs, so an old copy's PII exposure is bounded by key
|
|
74
|
+
* destruction already — this rule is the cost/hygiene bound, not the privacy one.
|
|
75
|
+
*/
|
|
76
|
+
export declare function pruneScopeBackups(bucket: unknown, options: PruneOptions): Promise<number>;
|
|
77
|
+
/**
|
|
78
|
+
* Drop `access-log/…` NDJSON batches (#553) whose NEWEST row is older than the window.
|
|
79
|
+
* Age comes from the mirrored `to` metadata, falling back to the key's own `lastId` (a
|
|
80
|
+
* ULID carries its timestamp), so a batch is datable from either half of what `ship`
|
|
81
|
+
* wrote. Undatable ⇒ kept, as above.
|
|
82
|
+
*
|
|
83
|
+
* The rows themselves already left the directory DO — `pruneAccessLog` deleted them only
|
|
84
|
+
* after this object durably existed — so this window is where the record's Tier 2
|
|
85
|
+
* lifetime finally ends, and choosing it is choosing how far back "which rows left, and
|
|
86
|
+
* when" can be answered from the object store. The admin log's own `drainAccessLog` /
|
|
87
|
+
* `pruneAccessLog` rows are never touched: the egress stays witnessed after its payload
|
|
88
|
+
* expires.
|
|
89
|
+
*/
|
|
90
|
+
export declare function pruneAccessLogBatches(bucket: unknown, options: PruneOptions): Promise<number>;
|
|
91
|
+
export {};
|
|
24
92
|
//# sourceMappingURL=r2-backups.d.ts.map
|
package/dist/r2-backups.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"r2-backups.d.ts","sourceRoot":"","sources":["../src/r2-backups.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAUH,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAsC3E,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,gBAAgB,CAsErE;AAcD;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,OAAO,GAAG,oBAAoB,CAgElF"}
|
|
1
|
+
{"version":3,"file":"r2-backups.d.ts","sourceRoot":"","sources":["../src/r2-backups.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAUH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAsC3E,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,gBAAgB,CAsErE;AAcD;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,OAAO,GAAG,oBAAoB,CAgElF;AAcD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG,aAAa,CAgCpE;AAED;;;;;;;;;;;;;;;GAeG;AACH,UAAU,YAAY;IACpB;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAuBD;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAa/F;AAiBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,CAAC,CAqBjB"}
|
package/dist/r2-backups.js
CHANGED
|
@@ -172,4 +172,154 @@ export function createR2DirectoryBackupStore(bucket) {
|
|
|
172
172
|
},
|
|
173
173
|
};
|
|
174
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Keys are `access-log/<firstId>-<lastId>.ndjson` — the batch's own id range, which is
|
|
177
|
+
* also its time range (a ULID sorts chronologically). That makes the object self-
|
|
178
|
+
* describing: "which file holds the row I am looking for" is answered by the key alone,
|
|
179
|
+
* without a manifest, and re-shipping an identical batch overwrites in place rather than
|
|
180
|
+
* accumulating duplicates. Shares the bucket with `scopes/` and `directory/` — three
|
|
181
|
+
* prefixes that cannot collide, none of them built by a caller.
|
|
182
|
+
*/
|
|
183
|
+
function accessLogKey(firstId, lastId) {
|
|
184
|
+
return `access-log/${firstId}-${lastId}.ndjson`;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* The R2 implementation of the `AccessLogSink` seam (K-24, control-plane.md §4.4) — the
|
|
188
|
+
* Tier 2 that makes the access log's retention window closable.
|
|
189
|
+
*
|
|
190
|
+
* **NDJSON, not JSON.** One row per line, appended-shaped: an operator greps it, a SIEM
|
|
191
|
+
* ingests it line-by-line, and a truncated object still parses up to its last newline.
|
|
192
|
+
* A single JSON array would have to be whole to be readable at all, which is the wrong
|
|
193
|
+
* failure mode for evidence.
|
|
194
|
+
*
|
|
195
|
+
* **Vendor-neutral by construction**, which was the point (#36): this writes a documented
|
|
196
|
+
* line format to an object store, so Splunk, Datadog, Drata and a human with `jq` all
|
|
197
|
+
* consume the same file. A vendor-specific push connector would have coupled the platform's
|
|
198
|
+
* retention policy to one company's roadmap.
|
|
199
|
+
*/
|
|
200
|
+
export function createR2AccessLogSink(bucket) {
|
|
201
|
+
const r2 = bucket;
|
|
202
|
+
return {
|
|
203
|
+
async ship(entries) {
|
|
204
|
+
const first = entries[0];
|
|
205
|
+
const last = entries[entries.length - 1];
|
|
206
|
+
if (!first || !last) {
|
|
207
|
+
// The sweep does not call `ship` with an empty batch; if some other caller does,
|
|
208
|
+
// returning a ref to an object that was never written would be a lie.
|
|
209
|
+
throw new Error('access-log sink: refusing to ship an empty batch');
|
|
210
|
+
}
|
|
211
|
+
const key = accessLogKey(first.id, last.id);
|
|
212
|
+
const body = `${entries.map((e) => JSON.stringify(e)).join('\n')}\n`;
|
|
213
|
+
await r2.put(key, body, {
|
|
214
|
+
// `application/x-ndjson` so a browser or a fetch-based consumer streams it as
|
|
215
|
+
// lines rather than trying to parse the whole object as one document.
|
|
216
|
+
httpMetadata: { contentType: 'application/x-ndjson' },
|
|
217
|
+
customMetadata: {
|
|
218
|
+
firstId: first.id,
|
|
219
|
+
lastId: last.id,
|
|
220
|
+
rows: String(entries.length),
|
|
221
|
+
// The wall-clock span, mirrored so a listing can answer "what covers March"
|
|
222
|
+
// without fetching a single object.
|
|
223
|
+
from: first.at,
|
|
224
|
+
to: last.at,
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
// Resolving here IS the durability claim the sweep relies on before it stamps
|
|
228
|
+
// `drainedAt`: R2's `put` resolves after the write is committed.
|
|
229
|
+
return { ref: key };
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/** Walk one prefix completely, returning every object's key + custom metadata. */
|
|
234
|
+
async function listAll(r2, prefix) {
|
|
235
|
+
const out = [];
|
|
236
|
+
let cursor;
|
|
237
|
+
do {
|
|
238
|
+
const page = await r2.list({
|
|
239
|
+
prefix,
|
|
240
|
+
include: ['customMetadata'],
|
|
241
|
+
...(cursor ? { cursor } : {}),
|
|
242
|
+
});
|
|
243
|
+
out.push(...page.objects);
|
|
244
|
+
cursor = page.truncated ? page.cursor : undefined;
|
|
245
|
+
} while (cursor);
|
|
246
|
+
return out;
|
|
247
|
+
}
|
|
248
|
+
const DAY_MS = 86_400_000;
|
|
249
|
+
/**
|
|
250
|
+
* Drop `scopes/…` reap copies (#493) older than the window. A copy's age is its
|
|
251
|
+
* `capturedAt` — the key's last segment, same as the store's own fallback address — so a
|
|
252
|
+
* hand-uploaded object without metadata still ages out rather than living forever by
|
|
253
|
+
* omission. An unparsable timestamp keeps the copy: deleting what cannot be dated is not
|
|
254
|
+
* retention.
|
|
255
|
+
*
|
|
256
|
+
* Note the interaction with #37 (erasure): per-subject payloads inside these dumps are
|
|
257
|
+
* sealed under per-subject DEKs, so an old copy's PII exposure is bounded by key
|
|
258
|
+
* destruction already — this rule is the cost/hygiene bound, not the privacy one.
|
|
259
|
+
*/
|
|
260
|
+
export async function pruneScopeBackups(bucket, options) {
|
|
261
|
+
const r2 = bucket;
|
|
262
|
+
const cutoff = (options.now?.() ?? new Date()).getTime() - options.olderThanDays * DAY_MS;
|
|
263
|
+
let pruned = 0;
|
|
264
|
+
for (const o of await listAll(r2, 'scopes/')) {
|
|
265
|
+
const capturedAt = o.customMetadata?.capturedAt ?? o.key.slice(o.key.lastIndexOf('/') + 1).replace(/\.json$/, '');
|
|
266
|
+
const at = new Date(capturedAt).getTime();
|
|
267
|
+
if (Number.isNaN(at) || at >= cutoff)
|
|
268
|
+
continue;
|
|
269
|
+
await r2.delete(o.key);
|
|
270
|
+
pruned += 1;
|
|
271
|
+
}
|
|
272
|
+
return pruned;
|
|
273
|
+
}
|
|
274
|
+
/** Crockford base32, the ULID alphabet — no I, L, O, U. */
|
|
275
|
+
const ULID_ALPHABET = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
|
|
276
|
+
/** A ULID's timestamp: its first 10 chars, base32-decoded to epoch ms. Null when malformed. */
|
|
277
|
+
function ulidTimeMs(id) {
|
|
278
|
+
if (id.length !== 26)
|
|
279
|
+
return null;
|
|
280
|
+
let ms = 0;
|
|
281
|
+
for (const ch of id.slice(0, 10)) {
|
|
282
|
+
const v = ULID_ALPHABET.indexOf(ch.toUpperCase());
|
|
283
|
+
if (v < 0)
|
|
284
|
+
return null;
|
|
285
|
+
ms = ms * 32 + v;
|
|
286
|
+
}
|
|
287
|
+
return ms;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Drop `access-log/…` NDJSON batches (#553) whose NEWEST row is older than the window.
|
|
291
|
+
* Age comes from the mirrored `to` metadata, falling back to the key's own `lastId` (a
|
|
292
|
+
* ULID carries its timestamp), so a batch is datable from either half of what `ship`
|
|
293
|
+
* wrote. Undatable ⇒ kept, as above.
|
|
294
|
+
*
|
|
295
|
+
* The rows themselves already left the directory DO — `pruneAccessLog` deleted them only
|
|
296
|
+
* after this object durably existed — so this window is where the record's Tier 2
|
|
297
|
+
* lifetime finally ends, and choosing it is choosing how far back "which rows left, and
|
|
298
|
+
* when" can be answered from the object store. The admin log's own `drainAccessLog` /
|
|
299
|
+
* `pruneAccessLog` rows are never touched: the egress stays witnessed after its payload
|
|
300
|
+
* expires.
|
|
301
|
+
*/
|
|
302
|
+
export async function pruneAccessLogBatches(bucket, options) {
|
|
303
|
+
const r2 = bucket;
|
|
304
|
+
const cutoff = (options.now?.() ?? new Date()).getTime() - options.olderThanDays * DAY_MS;
|
|
305
|
+
let pruned = 0;
|
|
306
|
+
for (const o of await listAll(r2, 'access-log/')) {
|
|
307
|
+
let newest = null;
|
|
308
|
+
if (o.customMetadata?.to) {
|
|
309
|
+
const at = new Date(o.customMetadata.to).getTime();
|
|
310
|
+
newest = Number.isNaN(at) ? null : at;
|
|
311
|
+
}
|
|
312
|
+
if (newest === null) {
|
|
313
|
+
// `access-log/<firstId>-<lastId>.ndjson` — the lastId is chars 27…52 of the name.
|
|
314
|
+
const name = o.key.slice('access-log/'.length).replace(/\.ndjson$/, '');
|
|
315
|
+
const lastId = name.length === 53 && name[26] === '-' ? name.slice(27) : null;
|
|
316
|
+
newest = lastId ? ulidTimeMs(lastId) : null;
|
|
317
|
+
}
|
|
318
|
+
if (newest === null || newest >= cutoff)
|
|
319
|
+
continue;
|
|
320
|
+
await r2.delete(o.key);
|
|
321
|
+
pruned += 1;
|
|
322
|
+
}
|
|
323
|
+
return pruned;
|
|
324
|
+
}
|
|
175
325
|
//# sourceMappingURL=r2-backups.js.map
|
package/dist/r2-backups.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"r2-backups.js","sourceRoot":"","sources":["../src/r2-backups.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACL,aAAa,EACb,SAAS,GAKV,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"r2-backups.js","sourceRoot":"","sources":["../src/r2-backups.ts"],"names":[],"mappings":"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"}
|
package/dist/seal.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-subject sealing of a scope dump on its way into a platform-retained copy (#37).
|
|
3
|
+
*
|
|
4
|
+
* The sibling of [`mask.ts`](./mask.ts), and the contrast is the point. Masking is for a
|
|
5
|
+
* copy a HUMAN will read: lossy, heuristic, irreversible, and applied when data leaves the
|
|
6
|
+
* governed environment. Sealing is for a copy the PLATFORM keeps: lossless, keyed, and
|
|
7
|
+
* reversible — right up until the subject's key is destroyed, after which it is more
|
|
8
|
+
* irreversible than masking ever was.
|
|
9
|
+
*
|
|
10
|
+
* **Why a backup needs this at all.** Reap backups and stored dumps are full-fidelity on
|
|
11
|
+
* purpose (`backups.ts`: *"a backup that cannot restore is a false promise"*), which is
|
|
12
|
+
* exactly why an erasure cannot reach one. `UPDATE … SET payload = NULL` erases the live
|
|
13
|
+
* scope; it does nothing to the copy sitting in R2 from last Tuesday. Sealing each
|
|
14
|
+
* subject's payloads under their own key on the way out means destroying that one key
|
|
15
|
+
* reaches backwards into every copy already taken — which is the only mechanism that makes
|
|
16
|
+
* "erased" true of an immutable store.
|
|
17
|
+
*
|
|
18
|
+
* **Scope is narrow and deliberate.** Only `_substrat_outbox`, only rows the kernel has
|
|
19
|
+
* already classified (`pii_class != 'none'` with a `subject_id`), only the `payload` cell.
|
|
20
|
+
* The envelope stays readable so a restored copy is still navigable, and vertical-owned
|
|
21
|
+
* tables are untouched — a vertical's own PII is a documented limit of this mechanism, not
|
|
22
|
+
* something quietly half-covered here (see kernel-design.md, open question 17).
|
|
23
|
+
*/
|
|
24
|
+
import type { ScopeDumpTable } from '@substrat-run/contracts';
|
|
25
|
+
import type { SealedSecret } from '@substrat-run/kernel';
|
|
26
|
+
/**
|
|
27
|
+
* The key operations both directions need, as `HostAdmin` exposes them. Narrowed to an
|
|
28
|
+
* interface so this module can be tested without a host, and so the seal path cannot reach
|
|
29
|
+
* for any other admin capability.
|
|
30
|
+
*/
|
|
31
|
+
export interface SubjectSealer {
|
|
32
|
+
seal(items: readonly {
|
|
33
|
+
subjectId: string;
|
|
34
|
+
plaintext: string;
|
|
35
|
+
}[]): Promise<(SealedSecret | null)[]>;
|
|
36
|
+
open(items: readonly {
|
|
37
|
+
subjectId: string;
|
|
38
|
+
sealed: SealedSecret;
|
|
39
|
+
}[]): Promise<(string | null)[]>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Seal every classified payload in a dump. Returns new arrays; never mutates the input.
|
|
43
|
+
*
|
|
44
|
+
* A payload whose subject has been shredded seals to `null` — the sealer refuses to mint a
|
|
45
|
+
* key for a tombstoned subject, and the copy records the same absence a live redaction
|
|
46
|
+
* leaves. A cell that is ALREADY sealed passes through untouched, so re-sealing a dump
|
|
47
|
+
* (a backup of a backup, a retried capture) is a no-op rather than double encryption.
|
|
48
|
+
*/
|
|
49
|
+
export declare function sealDump(tables: ScopeDumpTable[], sealer: SubjectSealer): Promise<ScopeDumpTable[]>;
|
|
50
|
+
/**
|
|
51
|
+
* Open every sealed payload in a dump — the restore half.
|
|
52
|
+
*
|
|
53
|
+
* A payload whose key is gone opens to `null`. That is where an erasure actually bites: the
|
|
54
|
+
* ciphertext was in the backup all along, and after the shred nothing turns it back into a
|
|
55
|
+
* person. The restored scope then looks exactly like a live scope that was redacted, which
|
|
56
|
+
* is the property worth having — two copies of the same tenant that disagree about who was
|
|
57
|
+
* erased is the failure this shape avoids.
|
|
58
|
+
*
|
|
59
|
+
* Unsealed cells pass through, so a dump taken before sealing existed still restores.
|
|
60
|
+
*/
|
|
61
|
+
export declare function openDump(tables: ScopeDumpTable[], sealer: SubjectSealer): Promise<ScopeDumpTable[]>;
|
|
62
|
+
//# sourceMappingURL=seal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seal.d.ts","sourceRoot":"","sources":["../src/seal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,CACF,KAAK,EAAE,SAAS;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EAAE,GACzD,OAAO,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;IACpC,IAAI,CAAC,KAAK,EAAE,SAAS;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,YAAY,CAAA;KAAE,EAAE,GAAG,OAAO,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;CACjG;AAkED;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,cAAc,EAAE,EACxB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,cAAc,EAAE,CAAC,CAwB3B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,cAAc,EAAE,EACxB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,cAAc,EAAE,CAAC,CAoB3B"}
|
package/dist/seal.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-subject sealing of a scope dump on its way into a platform-retained copy (#37).
|
|
3
|
+
*
|
|
4
|
+
* The sibling of [`mask.ts`](./mask.ts), and the contrast is the point. Masking is for a
|
|
5
|
+
* copy a HUMAN will read: lossy, heuristic, irreversible, and applied when data leaves the
|
|
6
|
+
* governed environment. Sealing is for a copy the PLATFORM keeps: lossless, keyed, and
|
|
7
|
+
* reversible — right up until the subject's key is destroyed, after which it is more
|
|
8
|
+
* irreversible than masking ever was.
|
|
9
|
+
*
|
|
10
|
+
* **Why a backup needs this at all.** Reap backups and stored dumps are full-fidelity on
|
|
11
|
+
* purpose (`backups.ts`: *"a backup that cannot restore is a false promise"*), which is
|
|
12
|
+
* exactly why an erasure cannot reach one. `UPDATE … SET payload = NULL` erases the live
|
|
13
|
+
* scope; it does nothing to the copy sitting in R2 from last Tuesday. Sealing each
|
|
14
|
+
* subject's payloads under their own key on the way out means destroying that one key
|
|
15
|
+
* reaches backwards into every copy already taken — which is the only mechanism that makes
|
|
16
|
+
* "erased" true of an immutable store.
|
|
17
|
+
*
|
|
18
|
+
* **Scope is narrow and deliberate.** Only `_substrat_outbox`, only rows the kernel has
|
|
19
|
+
* already classified (`pii_class != 'none'` with a `subject_id`), only the `payload` cell.
|
|
20
|
+
* The envelope stays readable so a restored copy is still navigable, and vertical-owned
|
|
21
|
+
* tables are untouched — a vertical's own PII is a documented limit of this mechanism, not
|
|
22
|
+
* something quietly half-covered here (see kernel-design.md, open question 17).
|
|
23
|
+
*/
|
|
24
|
+
const SPINE_TABLE = '_substrat_outbox';
|
|
25
|
+
const isSealedCell = (value) => typeof value === 'object' &&
|
|
26
|
+
value !== null &&
|
|
27
|
+
'__sealed' in value &&
|
|
28
|
+
typeof value.__sealed?.ciphertext === 'string';
|
|
29
|
+
/** Parse a cell that may already be a sealed envelope; anything else reads as "not sealed". */
|
|
30
|
+
function readSealed(cell) {
|
|
31
|
+
if (typeof cell !== 'string')
|
|
32
|
+
return undefined;
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(cell);
|
|
35
|
+
return isSealedCell(parsed) ? parsed.__sealed : undefined;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Locate the spine table and the three columns that drive both directions. */
|
|
42
|
+
function spineColumns(tables) {
|
|
43
|
+
const index = tables.findIndex((t) => t.name === SPINE_TABLE);
|
|
44
|
+
if (index < 0)
|
|
45
|
+
return undefined;
|
|
46
|
+
const table = tables[index];
|
|
47
|
+
const subject = table.columns.indexOf('subject_id');
|
|
48
|
+
const pii = table.columns.indexOf('pii_class');
|
|
49
|
+
const payload = table.columns.indexOf('payload');
|
|
50
|
+
// A dump from before any of these columns existed is left entirely alone rather than
|
|
51
|
+
// half-processed — the honest answer for a copy the classification never reached.
|
|
52
|
+
if (subject < 0 || pii < 0 || payload < 0)
|
|
53
|
+
return undefined;
|
|
54
|
+
return { index, subject, pii, payload };
|
|
55
|
+
}
|
|
56
|
+
/** Rows this pass should act on, as (row index, subject id) pairs. */
|
|
57
|
+
function classifiedRows(table, cols) {
|
|
58
|
+
const out = [];
|
|
59
|
+
table.rows.forEach((row, i) => {
|
|
60
|
+
const subjectId = row[cols.subject];
|
|
61
|
+
const piiClass = row[cols.pii];
|
|
62
|
+
if (typeof subjectId !== 'string' || subjectId === '')
|
|
63
|
+
return;
|
|
64
|
+
if (piiClass === 'none' || typeof piiClass !== 'string')
|
|
65
|
+
return;
|
|
66
|
+
if (row[cols.payload] === null || row[cols.payload] === undefined)
|
|
67
|
+
return;
|
|
68
|
+
out.push({ row: i, subjectId });
|
|
69
|
+
});
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Seal every classified payload in a dump. Returns new arrays; never mutates the input.
|
|
74
|
+
*
|
|
75
|
+
* A payload whose subject has been shredded seals to `null` — the sealer refuses to mint a
|
|
76
|
+
* key for a tombstoned subject, and the copy records the same absence a live redaction
|
|
77
|
+
* leaves. A cell that is ALREADY sealed passes through untouched, so re-sealing a dump
|
|
78
|
+
* (a backup of a backup, a retried capture) is a no-op rather than double encryption.
|
|
79
|
+
*/
|
|
80
|
+
export async function sealDump(tables, sealer) {
|
|
81
|
+
const cols = spineColumns(tables);
|
|
82
|
+
if (!cols)
|
|
83
|
+
return tables;
|
|
84
|
+
const table = tables[cols.index];
|
|
85
|
+
const targets = classifiedRows(table, cols).filter((t) => readSealed(table.rows[t.row][cols.payload]) === undefined);
|
|
86
|
+
if (targets.length === 0)
|
|
87
|
+
return tables;
|
|
88
|
+
const sealed = await sealer.seal(targets.map((t) => ({
|
|
89
|
+
subjectId: t.subjectId,
|
|
90
|
+
// Cells are SQLite text; a non-string payload would be a corrupt spine row, and
|
|
91
|
+
// String() keeps that from throwing mid-backup.
|
|
92
|
+
plaintext: String(table.rows[t.row][cols.payload]),
|
|
93
|
+
})));
|
|
94
|
+
const rows = table.rows.map((row) => [...row]);
|
|
95
|
+
targets.forEach((t, i) => {
|
|
96
|
+
const result = sealed[i];
|
|
97
|
+
rows[t.row][cols.payload] = result ? JSON.stringify({ __sealed: result }) : null;
|
|
98
|
+
});
|
|
99
|
+
return tables.map((t, i) => (i === cols.index ? { ...t, rows } : t));
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Open every sealed payload in a dump — the restore half.
|
|
103
|
+
*
|
|
104
|
+
* A payload whose key is gone opens to `null`. That is where an erasure actually bites: the
|
|
105
|
+
* ciphertext was in the backup all along, and after the shred nothing turns it back into a
|
|
106
|
+
* person. The restored scope then looks exactly like a live scope that was redacted, which
|
|
107
|
+
* is the property worth having — two copies of the same tenant that disagree about who was
|
|
108
|
+
* erased is the failure this shape avoids.
|
|
109
|
+
*
|
|
110
|
+
* Unsealed cells pass through, so a dump taken before sealing existed still restores.
|
|
111
|
+
*/
|
|
112
|
+
export async function openDump(tables, sealer) {
|
|
113
|
+
const cols = spineColumns(tables);
|
|
114
|
+
if (!cols)
|
|
115
|
+
return tables;
|
|
116
|
+
const table = tables[cols.index];
|
|
117
|
+
const targets = [];
|
|
118
|
+
for (const t of classifiedRows(table, cols)) {
|
|
119
|
+
const sealed = readSealed(table.rows[t.row][cols.payload]);
|
|
120
|
+
if (sealed)
|
|
121
|
+
targets.push({ ...t, sealed });
|
|
122
|
+
}
|
|
123
|
+
if (targets.length === 0)
|
|
124
|
+
return tables;
|
|
125
|
+
const opened = await sealer.open(targets.map((t) => ({ subjectId: t.subjectId, sealed: t.sealed })));
|
|
126
|
+
const rows = table.rows.map((row) => [...row]);
|
|
127
|
+
targets.forEach((t, i) => {
|
|
128
|
+
rows[t.row][cols.payload] = opened[i] ?? null;
|
|
129
|
+
});
|
|
130
|
+
return tables.map((t, i) => (i === cols.index ? { ...t, rows } : t));
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=seal.js.map
|
package/dist/seal.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seal.js","sourceRoot":"","sources":["../src/seal.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@substrat-run/control-plane-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.54.0",
|
|
4
4
|
"description": "HTTP surface over HostAdmin — the audited control-plane transport (control-plane.md §4.5)",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"hono": "^4.6.0",
|
|
26
|
-
"@substrat-run/
|
|
26
|
+
"@substrat-run/kernel": "^0.54.0",
|
|
27
27
|
"@substrat-run/psl": "^0.2.0",
|
|
28
|
-
"@substrat-run/
|
|
28
|
+
"@substrat-run/contracts": "^0.54.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@hono/node-server": "^1.13.0",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"tsx": "^4.19.0",
|
|
37
37
|
"typescript": "^7.0.0",
|
|
38
38
|
"vitest": "^3.0.0",
|
|
39
|
-
"@substrat-run/adapter-sqlite": "^0.
|
|
39
|
+
"@substrat-run/adapter-sqlite": "^0.54.0"
|
|
40
40
|
},
|
|
41
41
|
"publishConfig": {
|
|
42
42
|
"access": "public"
|