@substrat-run/control-plane-api 0.160.0 → 0.162.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 +8 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +142 -9
- package/dist/api.js.map +1 -1
- package/dist/cf-observability.d.ts.map +1 -1
- package/dist/cf-observability.js +31 -8
- package/dist/cf-observability.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/observability.d.ts +6 -2
- package/dist/observability.d.ts.map +1 -1
- package/dist/observability.js +1 -1
- package/dist/observability.js.map +1 -1
- package/dist/platform-drain.d.ts +26 -2
- package/dist/platform-drain.d.ts.map +1 -1
- package/dist/platform-drain.js +203 -88
- package/dist/platform-drain.js.map +1 -1
- package/dist/platform-wire.d.ts +2 -1
- package/dist/platform-wire.d.ts.map +1 -1
- package/dist/platform-wire.js +23 -3
- package/dist/platform-wire.js.map +1 -1
- package/dist/scope-copy-cleanup.js +1 -1
- package/dist/scope-copy-cleanup.js.map +1 -1
- package/dist/vertical-client.d.ts +58 -2
- package/dist/vertical-client.d.ts.map +1 -1
- package/dist/vertical-client.js +224 -21
- package/dist/vertical-client.js.map +1 -1
- package/dist/wire-readiness.d.ts +149 -0
- package/dist/wire-readiness.d.ts.map +1 -0
- package/dist/wire-readiness.js +250 -0
- package/dist/wire-readiness.js.map +1 -0
- package/dist/wire-settings.d.ts +77 -0
- package/dist/wire-settings.d.ts.map +1 -0
- package/dist/wire-settings.js +155 -0
- package/dist/wire-settings.js.map +1 -0
- package/dist/wire-upload.d.ts +26 -0
- package/dist/wire-upload.d.ts.map +1 -1
- package/dist/wire-upload.js +29 -2
- package/dist/wire-upload.js.map +1 -1
- package/dist/wire-walk.d.ts +161 -0
- package/dist/wire-walk.d.ts.map +1 -0
- package/dist/wire-walk.js +345 -0
- package/dist/wire-walk.js.map +1 -0
- package/package.json +8 -8
package/dist/api.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ import type { PlatformRuntime } from './platform-runtime.js';
|
|
|
17
17
|
import { type DoNamespaceReader } from './do-namespaces.js';
|
|
18
18
|
import type { DirectoryBackupStore, ScopeBackupStore } from './backups.js';
|
|
19
19
|
import { type WireUploadSettings } from './wire-upload.js';
|
|
20
|
+
import type { WireSettingsApi } from './wire-settings.js';
|
|
20
21
|
import { type LakeQueryEngine } from './lake-gateway.js';
|
|
21
22
|
import { type CustomHostnameProvisioner } from './custom-hostnames.js';
|
|
22
23
|
import { type SocialRelayOptions } from './social-relay.js';
|
|
@@ -163,6 +164,13 @@ export interface ControlPlaneApiOptions {
|
|
|
163
164
|
* unset, no upload gives anything, and each still records that it gave nothing.
|
|
164
165
|
*/
|
|
165
166
|
wireUpload?: WireUploadSettings;
|
|
167
|
+
/**
|
|
168
|
+
* RFC wire-signing follow-on 4: the namespace's script-settings endpoint, through which the fleet
|
|
169
|
+
* walk (`POST /wire-auth/walk`) brings a deployed script's four `SUBSTRAT_WIRE_*` settings to what
|
|
170
|
+
* `wireUpload` gives now, without uploading it again (`createWfpWireSettingsApi`). Absent, the walk
|
|
171
|
+
* answers 501.
|
|
172
|
+
*/
|
|
173
|
+
wireSettings?: WireSettingsApi;
|
|
166
174
|
/** #1722: how long a copy move owns its ledger entries, and so how long its restore may land
|
|
167
175
|
* (`CopyRestoreFence`). `SCOPE_COPY_LEASE_MS` unless a test needs a lease it can outlive. */
|
|
168
176
|
copyLeaseMs?: number;
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AAE9G,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAsBL,KAAK,qBAAqB,EAqF1B,CAAC,EAeD,KAAK,cAAc,EAEnB,KAAK,uBAAuB,EAE7B,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAKV,UAAU,EACV,kBAAkB,EAGlB,wBAAwB,EACxB,oBAAoB,EAEpB,eAAe,EAKf,eAAe,
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/api.js
CHANGED
|
@@ -67,7 +67,9 @@ import { parseTenantRequestQuery, parseTenantWindowQuery, refineTenantWindow, sc
|
|
|
67
67
|
import { namespacesForScript } from './do-namespaces.js';
|
|
68
68
|
import { backupDirectoryIfDue } from './directory-backup.js';
|
|
69
69
|
import { deployedDoClasses, platformSweeperDecision } from './platform-entry.js';
|
|
70
|
-
import { wireUploadFor } from './wire-upload.js';
|
|
70
|
+
import { wireSettingsRefusal, wireUploadFor } from './wire-upload.js';
|
|
71
|
+
import { parseWireWalkCursor, runWireWalk, WIRE_WALK_LIMIT_DEFAULT, WIRE_WALK_LIMIT_MAX, WIRE_WALK_RELEASE_MAX, } from './wire-walk.js';
|
|
72
|
+
import { wireKeyDropSince, wireReadinessReport, WIRE_READINESS_LIMIT_DEFAULT, WIRE_READINESS_LIMIT_MAX } from './wire-readiness.js';
|
|
71
73
|
import { STORAGE_PAGE_DEFAULT, STORAGE_PAGE_MAX, readStoragePage } from './storage-meter.js';
|
|
72
74
|
import { queryLakeEvents } from './lake-gateway.js';
|
|
73
75
|
import { LakeQueryError } from './r2-sql-engine.js';
|
|
@@ -3468,7 +3470,7 @@ export function createControlPlaneApi(options) {
|
|
|
3468
3470
|
};
|
|
3469
3471
|
/** Whether the scope's store in `holder` is a wiped copy: it holds the `carried_away` tombstone. */
|
|
3470
3472
|
const isCarriedAway = async (holder, scope) => {
|
|
3471
|
-
const page = await holder.
|
|
3473
|
+
const page = await holder.readTombstoneTable(scope.id, { table: '_substrat_meta', limit: 100, offset: 0 });
|
|
3472
3474
|
return metaValueIn(page.rows, page.columns, CARRIED_AWAY_KEY) !== null;
|
|
3473
3475
|
};
|
|
3474
3476
|
const recordCarryCleanup = (c, scope, versionId, stage, e) => {
|
|
@@ -3837,12 +3839,24 @@ export function createControlPlaneApi(options) {
|
|
|
3837
3839
|
return refuse(c, 'not_found', `unknown scope for tenant: (${tenantId}, ${scopeId})`);
|
|
3838
3840
|
// Health reads the scope the same way the Data view does, so it leaves the same
|
|
3839
3841
|
// row: an auditor should not be able to tell which route reached the tables.
|
|
3842
|
+
// #2544: a deployment that does not hold the scope refuses the table read. Health is the one
|
|
3843
|
+
// reader that wants the answer "nothing stored" for it: the scope has no role rows, which is
|
|
3844
|
+
// what `roleProjectionEmpty` reports for an active one, rather than a refusal of the whole route.
|
|
3845
|
+
let notHeld = false;
|
|
3840
3846
|
const tables = await delegatedRead(c, tenantId, scopeId, scope, 'listScopeTables', null, {
|
|
3841
3847
|
viaVertical: (v) => v.listScopeTables(scopeId),
|
|
3842
3848
|
colocated: () => c.var.admin.listScopeTables(c.get('actor'), tenantId, scopeId),
|
|
3843
|
-
}, (r) => r.length)
|
|
3849
|
+
}, (r) => r.length).catch((err) => {
|
|
3850
|
+
if (!isScopeNotHeld(err, scopeId))
|
|
3851
|
+
throw err;
|
|
3852
|
+
notHeld = true;
|
|
3853
|
+
return [];
|
|
3854
|
+
});
|
|
3844
3855
|
const roles = tables.find((t) => t.name === '_substrat_roles');
|
|
3845
|
-
|
|
3856
|
+
// #2531: the roles in force. A dropped role is kept as a tombstone, so the table's own row count
|
|
3857
|
+
// would read a projection that defines nothing as populated. A host from before tombstones
|
|
3858
|
+
// answers no `liveRowCount`, and kept none, so its `rowCount` is that count.
|
|
3859
|
+
const roleCount = notHeld ? 0 : roles ? (roles.liveRowCount ?? roles.rowCount) : null;
|
|
3846
3860
|
const roleProjectionEmpty = scope.status === 'active' && roleCount === 0;
|
|
3847
3861
|
const missingStores = scope.vertical
|
|
3848
3862
|
? await missingStoresForTenant({
|
|
@@ -8472,7 +8486,12 @@ export function createControlPlaneApi(options) {
|
|
|
8472
8486
|
});
|
|
8473
8487
|
// #1877: scoped to the app's own scripts — the scan and trust boundary.
|
|
8474
8488
|
const services = await appServices(c.get('actor'), input.tenantId, input.scopeId, input.vertical);
|
|
8475
|
-
|
|
8489
|
+
const { events, incomplete } = await options.observability.tenantLogs({ ...input, services });
|
|
8490
|
+
// The body stays the list every caller reads; a page that may be missing lines behind a cut
|
|
8491
|
+
// says so beside it, as `/observability/logs` does (#2521).
|
|
8492
|
+
if (incomplete)
|
|
8493
|
+
c.header(LOGS_INCOMPLETE_HEADER, '1');
|
|
8494
|
+
return c.json(events);
|
|
8476
8495
|
});
|
|
8477
8496
|
// #1331: one app's sampled field reports, joined to the router's own request lines.
|
|
8478
8497
|
app.get('/observability/tenant-field-coverage', async (c) => {
|
|
@@ -10461,19 +10480,19 @@ export function createControlPlaneApi(options) {
|
|
|
10461
10480
|
* `text/plain` beacon cannot set it without a CORS preflight, 415) and refuse a request a
|
|
10462
10481
|
* browser marks as from another origin (403). A request with neither header is the CLI's.
|
|
10463
10482
|
*/
|
|
10464
|
-
const byAPerson = async (c) => {
|
|
10483
|
+
const byAPerson = async (c, words = { write: 'a store driver release write', person: 'a store driver release is published by a person' }) => {
|
|
10465
10484
|
if (!/^application\/json\s*(;|$)/i.test(c.req.header('content-type') ?? '')) {
|
|
10466
|
-
throw new ControlPlaneError(415,
|
|
10485
|
+
throw new ControlPlaneError(415, `${words.write} takes application/json`);
|
|
10467
10486
|
}
|
|
10468
10487
|
const origin = c.req.header('origin');
|
|
10469
10488
|
const site = c.req.header('sec-fetch-site');
|
|
10470
10489
|
if ((origin && origin !== new URL(c.req.url).origin) || (site && site !== 'same-origin' && site !== 'none')) {
|
|
10471
|
-
throw new ControlPlaneError(403,
|
|
10490
|
+
throw new ControlPlaneError(403, `${words.write} is not taken from another origin`);
|
|
10472
10491
|
}
|
|
10473
10492
|
const actor = c.get('actor');
|
|
10474
10493
|
const person = options.humanStaffActor ? await options.humanStaffActor(c.req.raw) : null;
|
|
10475
10494
|
if (!person || person !== actor) {
|
|
10476
|
-
throw new ControlPlaneError(403,
|
|
10495
|
+
throw new ControlPlaneError(403, `${words.person}: this takes a staff sign-in, not a service token`);
|
|
10477
10496
|
}
|
|
10478
10497
|
return actor;
|
|
10479
10498
|
};
|
|
@@ -10637,8 +10656,122 @@ export function createControlPlaneApi(options) {
|
|
|
10637
10656
|
const again = ran.outcome === 'paused' || ran.outcome === 'held' || ran.outcome === 'busy';
|
|
10638
10657
|
return c.json({ pass: ran, rebuild }, again ? 202 : 200);
|
|
10639
10658
|
});
|
|
10659
|
+
// RFC wire-signing follow-on 4: one pass of the fleet walk, which brings every script's key lists,
|
|
10660
|
+
// audience and state to what this control plane gives now (`runWireWalk`). A person's act; a dry run
|
|
10661
|
+
// unless the body says `apply: true`; at most `limit` scripts, from `cursor`, answering where the next
|
|
10662
|
+
// pass starts. `release` names held scripts a person has looked at. Audited as `walkWireAuth`
|
|
10663
|
+
// however the pass ends. Not to be run while a push or promote of a walked vertical is in flight.
|
|
10664
|
+
app.post('/wire-auth/walk', async (c) => {
|
|
10665
|
+
const actor = await byAPerson(c, { write: 'a wire walk', person: 'the wire walk is run by a person' });
|
|
10666
|
+
const api = options.wireSettings;
|
|
10667
|
+
if (!api || !c.var.admin.listWireAuth || !c.var.admin.recordWireAuth || !c.var.admin.recordWireWalk) {
|
|
10668
|
+
throw new ControlPlaneError(501, 'this control plane does not walk the fleet’s wire settings');
|
|
10669
|
+
}
|
|
10670
|
+
const body = z
|
|
10671
|
+
.object({
|
|
10672
|
+
apply: z.boolean().optional(),
|
|
10673
|
+
confirm: z.boolean().optional(),
|
|
10674
|
+
cursor: z.string().min(1).max(512).optional(),
|
|
10675
|
+
limit: z.number().int().min(1).max(WIRE_WALK_LIMIT_MAX).optional(),
|
|
10676
|
+
release: z.array(z.string().min(1).max(255)).max(WIRE_WALK_RELEASE_MAX).optional(),
|
|
10677
|
+
})
|
|
10678
|
+
.strict()
|
|
10679
|
+
.parse(await readCappedJson(c.req.raw, WIRE_WALK_REQUEST_MAX_BYTES));
|
|
10680
|
+
const cursor = body.cursor === undefined ? null : parseWireWalkCursor(body.cursor);
|
|
10681
|
+
if (body.cursor !== undefined && cursor === null)
|
|
10682
|
+
throw new ControlPlaneError(422, 'the cursor is not one a pass answered');
|
|
10683
|
+
const apply = body.apply === true;
|
|
10684
|
+
const confirm = body.confirm === true;
|
|
10685
|
+
// Before anything is read: the readiness report trusts a record as confirmed only when it was
|
|
10686
|
+
// written after the confirming walk began.
|
|
10687
|
+
const startedAt = new Date().toISOString();
|
|
10688
|
+
// Refused settings give every script nothing, which an applied pass would write as a fleet-wide
|
|
10689
|
+
// withdrawal: the pass is refused before it walks anything, dry or applied, and audited as such.
|
|
10690
|
+
const refused = await wireSettingsRefusal(options.wireUpload);
|
|
10691
|
+
if (refused) {
|
|
10692
|
+
await c.var.admin.recordWireWalk(actor, {
|
|
10693
|
+
apply, confirm, startedAt, from: body.cursor ?? null, next: null, walked: 0,
|
|
10694
|
+
updated: [], held: [], released: [], skipped: [], failed: [],
|
|
10695
|
+
refused: `${refused.withheld}: ${refused.reason}`,
|
|
10696
|
+
});
|
|
10697
|
+
throw new ControlPlaneError(409, `the wire walk is refused: ${refused.withheld}: ${refused.reason}; fix the control plane's setting, then walk`);
|
|
10698
|
+
}
|
|
10699
|
+
// The pass puts each script's result here as it goes, so a pass that ends in an error is still
|
|
10700
|
+
// audited with what it did.
|
|
10701
|
+
const walked = [];
|
|
10702
|
+
let pass = null;
|
|
10703
|
+
let failedWith;
|
|
10704
|
+
try {
|
|
10705
|
+
pass = await runWireWalk({
|
|
10706
|
+
admin: c.var.admin,
|
|
10707
|
+
actor,
|
|
10708
|
+
settings: options.wireUpload,
|
|
10709
|
+
api,
|
|
10710
|
+
...(options.fetchVerticalModules ? { fetchModules: options.fetchVerticalModules } : {}),
|
|
10711
|
+
fail: ({ vertical, version, stage, message }) => recordFailure({ actor, operation: 'wire-walk', stage, vertical, ...(version ? { version } : {}), message }),
|
|
10712
|
+
}, { apply, confirm, cursor, limit: body.limit ?? WIRE_WALK_LIMIT_DEFAULT, release: body.release ?? [], sink: walked });
|
|
10713
|
+
}
|
|
10714
|
+
catch (error) {
|
|
10715
|
+
failedWith = 'the pass stopped on a directory read it could not make; the scripts it walked before are listed';
|
|
10716
|
+
throw error;
|
|
10717
|
+
}
|
|
10718
|
+
finally {
|
|
10719
|
+
// Written however the pass ended: a person reached the fleet, or tried to.
|
|
10720
|
+
const named = (pick) => walked.filter(pick).map((s) => s.deploymentRef);
|
|
10721
|
+
const why = (action) => walked.filter((s) => s.action === action).map((s) => ({ deploymentRef: s.deploymentRef, reason: s.reason ?? action }));
|
|
10722
|
+
await c.var.admin.recordWireWalk(actor, {
|
|
10723
|
+
apply,
|
|
10724
|
+
confirm,
|
|
10725
|
+
startedAt,
|
|
10726
|
+
from: body.cursor ?? null,
|
|
10727
|
+
next: pass?.next ?? null,
|
|
10728
|
+
walked: walked.length,
|
|
10729
|
+
updated: named((s) => s.action === 'updated'),
|
|
10730
|
+
held: named((s) => s.held === true),
|
|
10731
|
+
released: named((s) => s.released === true),
|
|
10732
|
+
skipped: why('skipped'),
|
|
10733
|
+
failed: why('failed'),
|
|
10734
|
+
...(failedWith ? { error: failedWith } : {}),
|
|
10735
|
+
});
|
|
10736
|
+
}
|
|
10737
|
+
return c.json(pass);
|
|
10738
|
+
});
|
|
10739
|
+
// RFC wire-signing follow-on 4: one page of the readiness report (`wireReadinessReport`): what blocks
|
|
10740
|
+
// `signed-only`, a key drop (when the body names one) and the withdrawal of the shared credential.
|
|
10741
|
+
// Advisory and read-only: it reads the registry, the capability records and the walk's rows, and
|
|
10742
|
+
// nothing else; its record reads leave their access-log rows. A staff sign-in, never a service token.
|
|
10743
|
+
// Bounded and resumable like the walk: at most `limit` scripts, from `cursor`, answering `next`.
|
|
10744
|
+
app.post('/wire-auth/readiness', async (c) => {
|
|
10745
|
+
const actor = await byAPerson(c, { write: 'a wire readiness report', person: 'the wire readiness report is read by a person' });
|
|
10746
|
+
if (!c.var.admin.listWireAuth)
|
|
10747
|
+
throw new ControlPlaneError(501, 'this control plane does not keep capability records');
|
|
10748
|
+
const body = z
|
|
10749
|
+
.object({
|
|
10750
|
+
cursor: z.string().min(1).max(512).optional(),
|
|
10751
|
+
limit: z.number().int().min(1).max(WIRE_READINESS_LIMIT_MAX).optional(),
|
|
10752
|
+
keyDrop: z
|
|
10753
|
+
.object({ issuer: z.enum(['router', 'platform']), kid: z.string().min(1).max(256), since: z.unknown().optional() })
|
|
10754
|
+
.strict()
|
|
10755
|
+
.optional(),
|
|
10756
|
+
})
|
|
10757
|
+
.strict()
|
|
10758
|
+
.parse(await readCappedJson(c.req.raw, WIRE_WALK_REQUEST_MAX_BYTES));
|
|
10759
|
+
const cursor = body.cursor === undefined ? null : parseWireWalkCursor(body.cursor);
|
|
10760
|
+
if (body.cursor !== undefined && cursor === null)
|
|
10761
|
+
throw new ControlPlaneError(422, 'the cursor is not one a page answered');
|
|
10762
|
+
let keyDrop;
|
|
10763
|
+
if (body.keyDrop) {
|
|
10764
|
+
const since = wireKeyDropSince(body.keyDrop.since, new Date());
|
|
10765
|
+
if (typeof since !== 'string')
|
|
10766
|
+
throw new ControlPlaneError(422, since.refused);
|
|
10767
|
+
keyDrop = { issuer: body.keyDrop.issuer, kid: body.keyDrop.kid, since };
|
|
10768
|
+
}
|
|
10769
|
+
return c.json(await wireReadinessReport({ admin: c.var.admin, actor, settings: options.wireUpload }, { cursor, from: body.cursor ?? null, limit: body.limit ?? WIRE_READINESS_LIMIT_DEFAULT, ...(keyDrop ? { keyDrop } : {}) }));
|
|
10770
|
+
});
|
|
10640
10771
|
return app;
|
|
10641
10772
|
}
|
|
10773
|
+
/** The most a wire walk's request body may be: a cursor and up to `WIRE_WALK_RELEASE_MAX` script names. */
|
|
10774
|
+
const WIRE_WALK_REQUEST_MAX_BYTES = 64 * 1024;
|
|
10642
10775
|
/**
|
|
10643
10776
|
* The most a release upload's body may be: the decoded cap, as base64 (4 bytes per 3), plus room
|
|
10644
10777
|
* for the JSON around it. A bigger body is refused (413) before it is read whole.
|