@substrat-run/control-plane-api 0.166.0 → 0.167.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 +11 -2
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +113 -46
- package/dist/api.js.map +1 -1
- package/dist/connection-binding-options.d.ts +13 -0
- package/dist/connection-binding-options.d.ts.map +1 -1
- package/dist/connection-binding-options.js +17 -0
- package/dist/connection-binding-options.js.map +1 -1
- package/dist/deploy.d.ts +2 -4
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +0 -2
- package/dist/deploy.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/staff-client.d.ts +2 -2
- package/dist/staff-client.d.ts.map +1 -1
- package/dist/staff-client.js.map +1 -1
- package/dist/store-rebuild.d.ts +7 -2
- package/dist/store-rebuild.d.ts.map +1 -1
- package/dist/store-rebuild.js +6 -3
- package/dist/store-rebuild.js.map +1 -1
- package/dist/store-releases.d.ts +16 -6
- package/dist/store-releases.d.ts.map +1 -1
- package/dist/store-releases.js +51 -11
- package/dist/store-releases.js.map +1 -1
- package/dist/store-script.d.ts +31 -6
- package/dist/store-script.d.ts.map +1 -1
- package/dist/store-script.js +36 -16
- package/dist/store-script.js.map +1 -1
- package/package.json +8 -8
package/dist/api.d.ts
CHANGED
|
@@ -3,12 +3,12 @@ import type { ConnectionScopeBinding, ConnectionScopeBindingInput } from '@subst
|
|
|
3
3
|
import { Hono } from 'hono';
|
|
4
4
|
import { type ConnectionCertificate, z, type ScopeStoreNeed, type StoreDriverRegistration } from '@substrat-run/contracts';
|
|
5
5
|
import type { Connection, ConnectionActivity, ConnectionActivitySource, ConnectionCredential, ConnectionProbe, PlatformActorId, ScopeId, TenantId } from '@substrat-run/contracts';
|
|
6
|
-
import type { CrossVerticalOptions, HostAdmin, OpsFailureInput, ScopeHost } from '@substrat-run/kernel';
|
|
6
|
+
import type { CrossVerticalOptions, HostAdmin, MailSender, OpsFailureInput, ScopeHost } from '@substrat-run/kernel';
|
|
7
7
|
import type { PlatformActorAuth, BuilderAuth, Principal, TenantServiceAuth } from './auth.js';
|
|
8
8
|
import { type VerticalClient } from './vertical-client.js';
|
|
9
9
|
import { type CopyJobs } from './copy-job.js';
|
|
10
10
|
import type { PlatformScopeStores } from './platform-scope-stores.js';
|
|
11
|
-
import type
|
|
11
|
+
import { type ScopeStoreEnsurerOptions, type StoreCanaryTarget, type StoreDriverApprovals } from './store-script.js';
|
|
12
12
|
import { type CanaryCall, type StoreReleaseBytes } from './store-releases.js';
|
|
13
13
|
import { type ConnectionCandidatePrepare, type VerticalConnectorDeclarationOf } from './connection-relay.js';
|
|
14
14
|
import type { DeployVerticalFn, FetchVerticalAssetFn, FetchVerticalModulesFn } from './deploy.js';
|
|
@@ -209,6 +209,8 @@ export interface ControlPlaneApiOptions {
|
|
|
209
209
|
/** The fleet rebuild (#2309): rebuild a script that exists, and create none. */
|
|
210
210
|
existingOnly?: boolean;
|
|
211
211
|
loadRelease: ScopeStoreEnsurerOptions['loadRelease'];
|
|
212
|
+
/** The driver's approvals, read by the caller under its hold on the driver (D-65). */
|
|
213
|
+
approvals: StoreDriverApprovals;
|
|
212
214
|
}) => Promise<void | {
|
|
213
215
|
uploaded: number;
|
|
214
216
|
}>;
|
|
@@ -221,6 +223,7 @@ export interface ControlPlaneApiOptions {
|
|
|
221
223
|
bytes: StoreReleaseBytes;
|
|
222
224
|
ensureCanary(input: StoreCanaryTarget & {
|
|
223
225
|
loadRelease: ScopeStoreEnsurerOptions['loadRelease'];
|
|
226
|
+
approvals: StoreDriverApprovals;
|
|
224
227
|
}): Promise<{
|
|
225
228
|
scriptName: string;
|
|
226
229
|
uploaded: boolean;
|
|
@@ -363,6 +366,12 @@ export interface ControlPlaneApiOptions {
|
|
|
363
366
|
* verify a Fortnox key yet" is a true statement; "your Fortnox key is fine" is not.
|
|
364
367
|
*/
|
|
365
368
|
connectionInspectors?: Record<string, ConnectionInspector>;
|
|
369
|
+
/**
|
|
370
|
+
* The mail sender each provider that sends as a tenant's own addresses registers (#2098), by
|
|
371
|
+
* provider — the same `{ provider → sender }` the email relay routes through. Behind
|
|
372
|
+
* `POST …/connections/:id/send-test` (#2343); a provider absent here 501s there.
|
|
373
|
+
*/
|
|
374
|
+
mailSenders?: Readonly<Record<string, MailSender>>;
|
|
366
375
|
/**
|
|
367
376
|
* The inspector for a vertical-defined provider (`<namespace>.<name>`, vertical-connectors.md
|
|
368
377
|
* §6.4), which `connectionInspectors` never names: built per (tenant, vertical), since its code
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,kBAAkB,EAAoB,MAAM,yBAAyB,CAAC;AAGxG,OAAO,KAAK,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AAE9G,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/api.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { sourceBuildRequest } from '@substrat-run/contracts';
|
|
2
2
|
import { PREVIEW_CONNECTIONS_REFUSAL } from './connection-relay.js';
|
|
3
|
-
import { connectionBindingOptions } from './connection-binding-options.js';
|
|
3
|
+
import { bindingChangeRefusal, connectionBindingOptions, refusesBindingChange } from './connection-binding-options.js';
|
|
4
4
|
import { resolveObservabilityWindow } from './observability-window.js';
|
|
5
5
|
import { Hono } from 'hono';
|
|
6
|
-
import { permissionKey, parseHostname, withLabel, RESERVED_LABEL_SEPARATOR, adminAction, lakeEventsInput, ownerTransferInput, memberInviteInput, memberRoleInput, ASSET_PART_PREFIX, assetHash, STORE_DRIVERS, storeCatalogOperation, channelName, connectLinkId, connectionActivity, connectionActivitySource, connectionCredential, connectionCertificate, connectionFilter, connectionHealthState, connectionProvider, connectorDispatchKind, PROVISION_SIBLING_KIND, ARCHIVE_SCOPE_KIND, PROVISION_TENANT_KIND, SET_ENTITLEMENTS_KIND, MODEL_USAGE_KIND, SWEEP_RUNS_KIND, toConnectionHealthEntry, CONNECTION_EXPIRY_WARNING_DAYS, CONNECTION_STALE_DAYS, connectionProbe, createTenantInput, entitlementGrantInput, hostname as hostnameSchema, hostnameRegion, hostnameStatus, identityLink, errorCode, issueStatus, issueStatusInput, FINDINGS_LIMIT_DEFAULT, FINDINGS_LIMIT_MAX, findingCursorOf, findingKind, findingRuleInput, findingStatus, findingStatusInput, listPageQuery, matchesOutboundHost, pageOf, platformRequestFilter, principalId as principalIdSchema, impersonationStamp, promotionAcknowledgement, bindAcknowledgement, rebindAcknowledgement, provisionableJurisdiction, publishVersionInput, queryScopeInput, readScopeTableInput, entityHistoryInput, eventFacetInput, eventCauseInput, eventEffectsInput, invocationEventsInput, deadLettersInput, lifecycleFlowInput, operationSeriesCount, operationSeriesInput, delegatedReadParams, createOrgInput, roleKey as roleKeySchema, peerSwitch, verticalSlug, scopeText, systemSwitch, DEFAULT_DENIAL_LIMIT, DENIAL_LIMIT_MAX, denialGroupBy, registerVerticalInput, platformScriptCollision, platformScriptRefusal, PLATFORM_SCRIPT_NAMES, serviceDimensions, sweepRunKind, sweepRunOutcome, scopeDump, dataSubjectId as dataSubjectIdSchema, scopeId as scopeIdSchema, scopeStatus, requestRollupReading, meterDaysReading, verticalOwnersReading, utcDay, storageShape, surfaceName, tenantId as tenantIdSchema, tenantStatus, versionOrigin, z, PROBLEM_CONTENT_TYPE, toProblem, validationIssuesFrom, redrainEventsInput, importCursorMove, importCursorAcknowledgementMissing, REDRAIN_BATCH, migrationsOnTop, substratError, errorCodeOf, envVarSpec, capabilityFilterQuery, capabilityId as capabilityIdSchema, PLATFORM_FEATURES_HEADER, PLATFORM_FEATURE_SCOPE_SWEEPER, PLATFORM_FEATURE_SOURCE_BUILDS, SOCIAL_RELAY_VERTICALS, isVerticalConnectorProvider, } from '@substrat-run/contracts';
|
|
6
|
+
import { permissionKey, parseHostname, withLabel, RESERVED_LABEL_SEPARATOR, adminAction, lakeEventsInput, ownerTransferInput, memberInviteInput, memberRoleInput, ASSET_PART_PREFIX, assetHash, STORE_DRIVERS, storeCatalogOperation, storeDriverBasisAdmits, channelName, connectLinkId, connectionActivity, connectionActivitySource, connectionCredential, connectionCertificate, connectionFilter, connectionHealthState, connectionProvider, connectorDispatchKind, PROVISION_SIBLING_KIND, ARCHIVE_SCOPE_KIND, PROVISION_TENANT_KIND, SET_ENTITLEMENTS_KIND, MODEL_USAGE_KIND, SWEEP_RUNS_KIND, toConnectionHealthEntry, CONNECTION_EXPIRY_WARNING_DAYS, CONNECTION_STALE_DAYS, connectionProbe, connectionSendTest, connectionSendTestResult, connectionSenders, createTenantInput, entitlementGrantInput, hostname as hostnameSchema, hostnameRegion, hostnameStatus, identityLink, errorCode, issueStatus, issueStatusInput, FINDINGS_LIMIT_DEFAULT, FINDINGS_LIMIT_MAX, findingCursorOf, findingKind, findingRuleInput, findingStatus, findingStatusInput, listPageQuery, matchesOutboundHost, pageOf, platformRequestFilter, principalId as principalIdSchema, impersonationStamp, promotionAcknowledgement, bindAcknowledgement, rebindAcknowledgement, provisionableJurisdiction, publishVersionInput, queryScopeInput, readScopeTableInput, entityHistoryInput, eventFacetInput, eventCauseInput, eventEffectsInput, invocationEventsInput, deadLettersInput, lifecycleFlowInput, operationSeriesCount, operationSeriesInput, delegatedReadParams, createOrgInput, roleKey as roleKeySchema, peerSwitch, verticalSlug, scopeText, systemSwitch, DEFAULT_DENIAL_LIMIT, DENIAL_LIMIT_MAX, denialGroupBy, registerVerticalInput, platformScriptCollision, platformScriptRefusal, PLATFORM_SCRIPT_NAMES, serviceDimensions, sweepRunKind, sweepRunOutcome, scopeDump, dataSubjectId as dataSubjectIdSchema, scopeId as scopeIdSchema, scopeStatus, requestRollupReading, meterDaysReading, verticalOwnersReading, utcDay, storageShape, surfaceName, tenantId as tenantIdSchema, tenantStatus, versionOrigin, z, PROBLEM_CONTENT_TYPE, toProblem, validationIssuesFrom, redrainEventsInput, importCursorMove, importCursorAcknowledgementMissing, REDRAIN_BATCH, migrationsOnTop, substratError, errorCodeOf, envVarSpec, capabilityFilterQuery, capabilityId as capabilityIdSchema, PLATFORM_FEATURES_HEADER, PLATFORM_FEATURE_SCOPE_SWEEPER, PLATFORM_FEATURE_SOURCE_BUILDS, SOCIAL_RELAY_VERTICALS, isVerticalConnectorProvider, } from '@substrat-run/contracts';
|
|
7
7
|
import { upstreamHandleIn, upstreamReferenceOf, withUpstreamReference } from '@substrat-run/contracts';
|
|
8
8
|
import { ENGINE_ENTITLEMENT_KEYS, bareSlug, declarableEntitlementKey, entitlementNamespaceRefusal, grantableEntitlements } from '@substrat-run/contracts';
|
|
9
9
|
import { SCOPE_COPY_LEASE_MS, copyRestoreFence, publishedMigrationDigest, replayStoreErasures } from '@substrat-run/kernel';
|
|
@@ -15,8 +15,9 @@ import { mintTenantToken } from './tenant-token.js';
|
|
|
15
15
|
import { beginTransfer, connectionGrantsForScope, transferScope, isScopeNotHeld, notHeldIsBenign } from './vertical-client.js';
|
|
16
16
|
import { COPY_JOB_MAX_MS, copyJobAt, copyJobHeld, copyJobId, copyJobLive, copyTagId, versionIdOfOrder, versionOrderOf, } from './copy-job.js';
|
|
17
17
|
import { runningVersionOf, runningVersionStoresOf, scopeStoreReachOf, scopeStoresOf, unrouteScope } from './platform-scope-stores.js';
|
|
18
|
+
import { liveApprovalOf } from './store-script.js';
|
|
18
19
|
import { STORE_REBUILD_REQUEST_SUBREQUESTS, runStoreFleetRebuild, storeRebuildBudget, storeRebuildStatus } from './store-rebuild.js';
|
|
19
|
-
import { PUSH_HOLD_TTL_MS, STORE_RELEASE_STORED_MAX_BYTES, approveStoredDriverRelease, runStoreDriverRevoke, runStoreDriverRollout, storeDriverRelease, storedReleaseLoader } from './store-releases.js';
|
|
20
|
+
import { PUSH_HOLD_TTL_MS, STORE_RELEASE_STORED_MAX_BYTES, approveStoredDriverRelease, readStoreDriverApprovals, runStoreDriverRevoke, runStoreDriverRollout, storeDriverRelease, storedReleaseLoader } from './store-releases.js';
|
|
20
21
|
import { assertNoUnreachableScopeCopies, claimScopeScriptReap, drainScopeScriptCopies, listAllScopeScriptCopies, recordStrandedStorage } from './scope-copy-cleanup.js';
|
|
21
22
|
import { BACKFILL_PAGE_MAX, backfillScopeScriptCopies } from './scope-copy-backfill.js';
|
|
22
23
|
import { oidcCallbackUrl, retireClientsOfReapedScope, wirePreviewAuth } from './preview-auth.js';
|
|
@@ -77,6 +78,8 @@ import { queryLakeEvents } from './lake-gateway.js';
|
|
|
77
78
|
import { LakeQueryError } from './r2-sql-engine.js';
|
|
78
79
|
import { isCustomHostname, validateBindableHostname, } from './custom-hostnames.js';
|
|
79
80
|
import { isSocialRelayVertical, reconcileScopeSocialRelay, relayHostnames, } from './social-relay.js';
|
|
81
|
+
/** The body of a connection's test message (#2343), plain enough to be the same in HTML and text. */
|
|
82
|
+
const SEND_TEST_TEXT = 'This is a test message from a mail connection. It reached you, so its sender address can send.';
|
|
80
83
|
/**
|
|
81
84
|
* Response headers carrying the CLI version advisory (`ControlPlaneApiOptions.cliAdvisory`).
|
|
82
85
|
* The CLI reads these by the same spelling (`packages/cli/src/version.ts`); exported so a
|
|
@@ -701,6 +704,9 @@ const TENANT_ROUTES = [
|
|
|
701
704
|
// #2100: the PUBLIC certificate a connection signs in with — same pin as the credential view.
|
|
702
705
|
{ method: 'GET', re: /\/tenants\/[^/]+\/connections\/[^/]+\/certificate$/, pin: 'path' },
|
|
703
706
|
{ method: 'POST', re: /\/tenants\/[^/]+\/connections\/[^/]+\/verify$/, pin: 'path' },
|
|
707
|
+
// #2343: a mail connection's test send. The dashboard sends it only to the signed-in person.
|
|
708
|
+
{ method: 'POST', re: /\/tenants\/[^/]+\/connections\/[^/]+\/send-test$/, pin: 'path' },
|
|
709
|
+
{ method: 'GET', re: /\/tenants\/[^/]+\/connections\/[^/]+\/senders$/, pin: 'path' },
|
|
704
710
|
// A vertical's mailed connect links (connections.md §3.5.4): the consent landing's liveness
|
|
705
711
|
// read, the callback's spend and its undo, and the integrations card's list and revoke.
|
|
706
712
|
{ method: 'GET', re: /\/tenants\/[^/]+\/connect-links$/, pin: 'path' },
|
|
@@ -2528,6 +2534,13 @@ export function createControlPlaneApi(options) {
|
|
|
2528
2534
|
if (!grants.some((g) => g.connectionId === row.id && g.permission === input.permission)) {
|
|
2529
2535
|
return refuseStatus(c, 409, `connection does not hold '${input.permission}' on scope ${scopeId}; grant it through the tenant's authorized connection flow, then bind`);
|
|
2530
2536
|
}
|
|
2537
|
+
// A change the held binding cannot take in place is the operator's to act on, not a failed write.
|
|
2538
|
+
const held = refusesBindingChange(row.provider)
|
|
2539
|
+
? (await inspectorOf(row)?.bindings?.(c.var.host, row))?.find((b) => b.scopeId === scopeId)
|
|
2540
|
+
: undefined;
|
|
2541
|
+
const changeRefused = bindingChangeRefusal(row.provider, held, input);
|
|
2542
|
+
if (changeRefused)
|
|
2543
|
+
return refuseStatus(c, 409, changeRefused);
|
|
2531
2544
|
const operationId = ulid();
|
|
2532
2545
|
const done = await auditedChange({
|
|
2533
2546
|
flow: 'connection-binding', operationId,
|
|
@@ -2567,6 +2580,70 @@ export function createControlPlaneApi(options) {
|
|
|
2567
2580
|
}
|
|
2568
2581
|
return c.json(connectionProbe.parse(await probe(c.var.host, row)));
|
|
2569
2582
|
});
|
|
2583
|
+
/** The addresses a mail connection may send as (#2343) — the send test's picker, whole and uncut. */
|
|
2584
|
+
app.get('/tenants/:tenantId/connections/:id/senders', async (c) => {
|
|
2585
|
+
const row = await inspectableConnection(c);
|
|
2586
|
+
if (!row)
|
|
2587
|
+
return refuse(c, 'not_found', 'unknown connection');
|
|
2588
|
+
const sender = options.mailSenders?.[row.provider];
|
|
2589
|
+
if (!sender)
|
|
2590
|
+
return refuseStatus(c, 501, `no mail sender registered for provider '${row.provider}'`);
|
|
2591
|
+
return c.json(connectionSenders.parse({ senders: await sender.senders(c.var.host, row) }));
|
|
2592
|
+
});
|
|
2593
|
+
/**
|
|
2594
|
+
* **Send a test message through a connection** (#2343) — the probe for mail, which cannot be
|
|
2595
|
+
* probed without sending. From one of the connection's own senders, to one recipient; the
|
|
2596
|
+
* dashboard's door sends only to the signed-in person's own address.
|
|
2597
|
+
*
|
|
2598
|
+
* Like `/verify`, a refusal is `200 { ok: false, error }` in the connector's own words (outside
|
|
2599
|
+
* the granted scope, throttled, sign-in failed), and the send rides the connector's recorded
|
|
2600
|
+
* egress, so health is written as for any send. A success is recorded by the connector as that
|
|
2601
|
+
* sender's verification. Not an audited admin-log mutation, the same class as a probe; who
|
|
2602
|
+
* asked is logged on the request record instead, never the recipient.
|
|
2603
|
+
*/
|
|
2604
|
+
app.post('/tenants/:tenantId/connections/:id/send-test', async (c) => {
|
|
2605
|
+
const row = await inspectableConnection(c);
|
|
2606
|
+
if (!row)
|
|
2607
|
+
return refuse(c, 'not_found', 'unknown connection');
|
|
2608
|
+
const sender = options.mailSenders?.[row.provider];
|
|
2609
|
+
if (!sender)
|
|
2610
|
+
return refuseStatus(c, 501, `no mail sender registered for provider '${row.provider}'`);
|
|
2611
|
+
const body = connectionSendTest.safeParse(await c.req.json().catch(() => null));
|
|
2612
|
+
if (!body.success)
|
|
2613
|
+
return refuse(c, 'validation_failed', 'from and to must each be one email address');
|
|
2614
|
+
const { from, to } = body.data;
|
|
2615
|
+
const senders = await sender.senders(c.var.host, row);
|
|
2616
|
+
if (!senders.some((s) => s.toLowerCase() === from.toLowerCase())) {
|
|
2617
|
+
return refuse(c, 'validation_failed', `'${from}' is not one of this connection's sender addresses`);
|
|
2618
|
+
}
|
|
2619
|
+
const principal = c.get('principal');
|
|
2620
|
+
console.log('connection.send-test', {
|
|
2621
|
+
tenantId: row.tenantId,
|
|
2622
|
+
connectionId: row.id,
|
|
2623
|
+
provider: row.provider,
|
|
2624
|
+
actor: c.get('actor'),
|
|
2625
|
+
onBehalfOf: principal.kind === 'tenant' ? (principal.onBehalfOf?.principal ?? null) : null,
|
|
2626
|
+
});
|
|
2627
|
+
try {
|
|
2628
|
+
await sender.send(c.var.host, row, {
|
|
2629
|
+
from: { email: from },
|
|
2630
|
+
to: [{ email: to }],
|
|
2631
|
+
subject: 'Substrat test message',
|
|
2632
|
+
html: `<p>${SEND_TEST_TEXT}</p>`,
|
|
2633
|
+
text: SEND_TEST_TEXT,
|
|
2634
|
+
attachments: [],
|
|
2635
|
+
});
|
|
2636
|
+
return c.json(connectionSendTestResult.parse({ ok: true, status: null, error: null }));
|
|
2637
|
+
}
|
|
2638
|
+
catch (e) {
|
|
2639
|
+
const status = e?.status;
|
|
2640
|
+
return c.json(connectionSendTestResult.parse({
|
|
2641
|
+
ok: false,
|
|
2642
|
+
status: typeof status === 'number' ? status : null,
|
|
2643
|
+
error: (e instanceof Error ? e.message : String(e)).slice(0, 600),
|
|
2644
|
+
}));
|
|
2645
|
+
}
|
|
2646
|
+
});
|
|
2570
2647
|
/**
|
|
2571
2648
|
* **What the connection has done** (#605) — the connector's dispatch ledger, projected.
|
|
2572
2649
|
*
|
|
@@ -7577,7 +7654,13 @@ export function createControlPlaneApi(options) {
|
|
|
7577
7654
|
throw new ControlPlaneError(409, `${operation} refused: the hold on scope store driver '${each}' lapsed during the upload; try again`);
|
|
7578
7655
|
}
|
|
7579
7656
|
}
|
|
7580
|
-
|
|
7657
|
+
const registration = (options.storeDrivers ?? STORE_DRIVERS).find((k) => k.driver === driver);
|
|
7658
|
+
if (!registration)
|
|
7659
|
+
throw new ControlPlaneError(403, `scope store driver '${driver}' is not registered`);
|
|
7660
|
+
// Read under the shared hold (D-65): a revoke takes the driver exclusively, so one written
|
|
7661
|
+
// before this read is seen, and none is written until this upload is done.
|
|
7662
|
+
const approvals = await readStoreDriverApprovals(host.admin, actor, registration);
|
|
7663
|
+
await options.ensureScopeStores({ vertical: slug, stores: stores.filter((store) => store.driver === driver), loadRelease, approvals });
|
|
7581
7664
|
}
|
|
7582
7665
|
}
|
|
7583
7666
|
catch (e) {
|
|
@@ -10988,20 +11071,8 @@ export function createControlPlaneApi(options) {
|
|
|
10988
11071
|
throw new ControlPlaneError(422, `not a store driver revoke: ${parsed.error.issues.map(safeIssue).join('; ')}`);
|
|
10989
11072
|
return parsed.data;
|
|
10990
11073
|
};
|
|
10991
|
-
|
|
10992
|
-
|
|
10993
|
-
const stored = await c.var.admin.getStoreDriverRelease(c.get('actor'), registration.driver, sha256);
|
|
10994
|
-
if (!stored)
|
|
10995
|
-
throw new ControlPlaneError(404, `release ${sha256} of '${registration.driver}' is not stored`);
|
|
10996
|
-
const pin = registration.releases.find((p) => p.sha256 === sha256);
|
|
10997
|
-
if (!pin) {
|
|
10998
|
-
throw new ControlPlaneError(422, `release ${sha256} of '${registration.driver}' is not pinned in its registration; it runs once a reviewed change pins it and the control plane is deployed`);
|
|
10999
|
-
}
|
|
11000
|
-
if (pin.sourceCommit !== stored.sourceCommit) {
|
|
11001
|
-
throw new ControlPlaneError(422, `release ${sha256} is pinned at ${pin.sourceCommit} and was stored as built from ${stored.sourceCommit}`);
|
|
11002
|
-
}
|
|
11003
|
-
return pin;
|
|
11004
|
-
};
|
|
11074
|
+
// Whether the release is stored, approved and keeps the schema is judged inside
|
|
11075
|
+
// `runStoreDriverRollout`, under its exclusive hold (D-65), never here before it.
|
|
11005
11076
|
const rollOut = async (c, registration, sha256, action) => {
|
|
11006
11077
|
const { flow, ensureScopeStores } = releaseFlow();
|
|
11007
11078
|
const actor = c.get('actor');
|
|
@@ -11016,22 +11087,11 @@ export function createControlPlaneApi(options) {
|
|
|
11016
11087
|
const result = await ensureScopeStores({ ...input, loadRelease });
|
|
11017
11088
|
return result ? result.uploaded > 0 : true;
|
|
11018
11089
|
},
|
|
11019
|
-
ensureCanary: (to) => flow.ensureCanary({ driver: registration.driver, to, loadRelease }),
|
|
11090
|
+
ensureCanary: (to, approvals) => flow.ensureCanary({ driver: registration.driver, to, loadRelease, approvals }),
|
|
11020
11091
|
callCanary: flow.callCanary,
|
|
11021
11092
|
});
|
|
11022
11093
|
return c.json(result, result.complete ? 200 : 202);
|
|
11023
11094
|
};
|
|
11024
|
-
/** Neither a rollout nor a rollback lowers the stored schema: a driver migrates forward only. */
|
|
11025
|
-
const assertSchemaKept = (registration, from, to) => {
|
|
11026
|
-
if (from === null || from === to.sha256)
|
|
11027
|
-
return;
|
|
11028
|
-
const leaving = registration.releases.find((p) => p.sha256 === from);
|
|
11029
|
-
if (!leaving)
|
|
11030
|
-
throw new ControlPlaneError(409, `the current release ${from} of '${registration.driver}' is not pinned, so its schema cannot be judged`);
|
|
11031
|
-
if (to.schema < leaving.schema) {
|
|
11032
|
-
throw new ControlPlaneError(409, `release ${to.sha256} has schema ${to.schema}, below the current release's ${leaving.schema}: objects may already have migrated`);
|
|
11033
|
-
}
|
|
11034
|
-
};
|
|
11035
11095
|
/** Where the platform's rebuild of every script of the driver stands (#2309), if it rebuilds them. */
|
|
11036
11096
|
const rebuildView = async (admin, actor, driver, known) => {
|
|
11037
11097
|
const fingerprint = known ?? (await options.storeReleases?.fingerprint?.());
|
|
@@ -11046,19 +11106,26 @@ export function createControlPlaneApi(options) {
|
|
|
11046
11106
|
const registration = registeredDriver(c.req.param('driver'));
|
|
11047
11107
|
const [state, approvals] = await Promise.all([
|
|
11048
11108
|
c.var.admin.storeDriverState(c.get('actor'), registration.driver),
|
|
11049
|
-
c.var.admin
|
|
11109
|
+
readStoreDriverApprovals(c.var.admin, c.get('actor'), registration),
|
|
11050
11110
|
]);
|
|
11051
11111
|
const rebuild = await rebuildView(c.var.admin, c.get('actor'), registration.driver);
|
|
11052
|
-
|
|
11112
|
+
// `pins` stays for released CLIs (RFC §5 step 3): the releases a move may go to now, one per live approval.
|
|
11113
|
+
const pins = approvals.records
|
|
11114
|
+
.filter((a) => liveApprovalOf(approvals, a.sha256) === a)
|
|
11115
|
+
.map((a) => ({ sha256: a.sha256, sourceCommit: a.sourceCommit, schema: a.schema }));
|
|
11116
|
+
return c.json({ driver: registration.driver, pins, approvals: approvals.records, ...state, ...(rebuild ? { rebuild } : {}) });
|
|
11053
11117
|
});
|
|
11054
|
-
// Store a release's bytes, before or after
|
|
11118
|
+
// Store a release's bytes, before or after a person approves them (D-65). Idempotent per bundle.
|
|
11055
11119
|
app.put('/store-drivers/:driver/releases', async (c) => {
|
|
11056
11120
|
const registration = registeredDriver(c.req.param('driver'));
|
|
11057
11121
|
const actor = await byAPerson(c);
|
|
11058
11122
|
const { flow } = releaseFlow();
|
|
11059
11123
|
const body = await readCappedJson(c.req.raw, STORE_RELEASE_REQUEST_MAX_BYTES);
|
|
11060
11124
|
const release = await storeDriverRelease({ admin: c.var.admin, actor, bytes: flow.bytes, driver: registration.driver, body });
|
|
11061
|
-
|
|
11125
|
+
const live = liveApprovalOf(await readStoreDriverApprovals(c.var.admin, actor, registration), release.sha256);
|
|
11126
|
+
const approved = live !== undefined && live.sourceCommit === release.sourceCommit;
|
|
11127
|
+
// `pinned` stays beside `approved` for released CLIs, meaning the same (RFC §5 step 3).
|
|
11128
|
+
return c.json({ release, pinned: approved, approved }, 201);
|
|
11062
11129
|
});
|
|
11063
11130
|
// The approval is a second, person-only write over bytes the platform already named by digest.
|
|
11064
11131
|
app.post('/store-drivers/:driver/releases/:sha256/approvals', async (c) => {
|
|
@@ -11066,17 +11133,20 @@ export function createControlPlaneApi(options) {
|
|
|
11066
11133
|
const actor = await byAPerson(c, approvalWords);
|
|
11067
11134
|
const sha256 = releaseSha(c.req.param('sha256'));
|
|
11068
11135
|
const body = parseApprovalBody(await readCappedJson(c.req.raw, 64 * 1024));
|
|
11136
|
+
// Refused here, not only by the readers (D-65 §3): a row its basis does not admit could never
|
|
11137
|
+
// run, yet as a live approval it would hold the one slot a corrected approval needs. Fixed text,
|
|
11138
|
+
// no input echoed.
|
|
11139
|
+
if (!storeDriverBasisAdmits(registration.isolationBasis, body.sourceReferences)) {
|
|
11140
|
+
throw new ControlPlaneError(422, 'not a store driver approval: sourceReferences: outside_isolation_basis');
|
|
11141
|
+
}
|
|
11069
11142
|
const flow = options.storeReleases;
|
|
11070
11143
|
if (!flow)
|
|
11071
11144
|
throw new ControlPlaneError(501, 'this control plane cannot read stored release bytes for approval');
|
|
11072
11145
|
const approval = await approveStoredDriverRelease({ admin: c.var.admin, actor, bytes: flow.bytes, driver: registration.driver, sha256, approval: body });
|
|
11073
11146
|
const state = await c.var.admin.storeDriverState(actor, registration.driver);
|
|
11074
11147
|
const approvals = state.current ? await c.var.admin.storeDriverApprovals(actor, registration.driver) : [];
|
|
11075
|
-
|
|
11076
|
-
|
|
11077
|
-
?? registration.releases.find((p) => p.sha256 === state.current.sha256)?.schema
|
|
11078
|
-
?? null
|
|
11079
|
-
: null;
|
|
11148
|
+
// The current release's approval facts, revoked or not: they are fixed at its first approval.
|
|
11149
|
+
const currentSchema = state.current ? approvals.find((a) => a.sha256 === state.current.sha256)?.schema ?? null : null;
|
|
11080
11150
|
return c.json({ approval, currentSchema }, 201);
|
|
11081
11151
|
});
|
|
11082
11152
|
app.post('/store-drivers/:driver/releases/:sha256/revoke', async (c) => {
|
|
@@ -11100,20 +11170,19 @@ export function createControlPlaneApi(options) {
|
|
|
11100
11170
|
if (!flow)
|
|
11101
11171
|
throw new ControlPlaneError(501, 'this control plane cannot move the store canary');
|
|
11102
11172
|
const loadRelease = storedReleaseLoader({ admin: c.var.admin, actor, bytes: flow.bytes });
|
|
11103
|
-
|
|
11173
|
+
// Called under the revoke's exclusive hold, so the approvals read here are the ones it judges.
|
|
11174
|
+
const approvals = await readStoreDriverApprovals(c.var.admin, actor, registration);
|
|
11175
|
+
await flow.ensureCanary({ driver: registration.driver, to, loadRelease, approvals });
|
|
11104
11176
|
},
|
|
11105
11177
|
});
|
|
11106
11178
|
return c.json(result, result.complete ? 200 : 202);
|
|
11107
11179
|
});
|
|
11108
|
-
// Roll a stored,
|
|
11180
|
+
// Roll a stored, approved release out: the canary, then every vertical that ever declared the
|
|
11109
11181
|
// driver. Posting the same release again converges a rollout that stopped.
|
|
11110
11182
|
app.post('/store-drivers/:driver/rollouts', async (c) => {
|
|
11111
11183
|
const registration = registeredDriver(c.req.param('driver'));
|
|
11112
11184
|
await byAPerson(c);
|
|
11113
11185
|
const { sha256 } = z.object({ sha256: z.string().regex(/^[0-9a-f]{64}$/) }).strict().parse(await c.req.json());
|
|
11114
|
-
const pin = await pinOf(c, registration, sha256);
|
|
11115
|
-
const { current } = await c.var.admin.storeDriverState(c.get('actor'), registration.driver);
|
|
11116
|
-
assertSchemaKept(registration, current?.sha256 ?? null, pin);
|
|
11117
11186
|
return rollOut(c, registration, sha256, 'rollout');
|
|
11118
11187
|
});
|
|
11119
11188
|
// Return to the immediately previous good release (D-63), never further and never a guess. After
|
|
@@ -11126,8 +11195,6 @@ export function createControlPlaneApi(options) {
|
|
|
11126
11195
|
const { current, rollbackTarget } = await c.var.admin.storeDriverState(c.get('actor'), registration.driver);
|
|
11127
11196
|
if (!current || !rollbackTarget)
|
|
11128
11197
|
throw new ControlPlaneError(409, `driver '${registration.driver}' has no previous good release to roll back to`);
|
|
11129
|
-
const pin = await pinOf(c, registration, rollbackTarget);
|
|
11130
|
-
assertSchemaKept(registration, current.sha256, pin);
|
|
11131
11198
|
return rollOut(c, registration, rollbackTarget, 'rollback');
|
|
11132
11199
|
});
|
|
11133
11200
|
// Run a pass of the platform's rebuild of every script of the driver now (#2309): the scheduled
|