@substrat-run/control-plane-api 0.166.0 → 0.167.1

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 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 { ScopeStoreEnsurerOptions, StoreCanaryTarget } from './store-script.js';
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
@@ -499,6 +508,11 @@ type Vars = {
499
508
  vertical: string | null;
500
509
  tenantId: OpsFailureInput['tenantId'];
501
510
  };
511
+ /**
512
+ * #113: the id the host worker minted for this request and answers in `requestId` and the
513
+ * `x-substrat-request-id` header. A failure's reference reuses it, so the caller quotes one id.
514
+ */
515
+ substratInvocationId?: string;
502
516
  };
503
517
  /** #2446: the most scopes one promote reads the role state of (`roleStateScopeCap`). */
504
518
  export declare const ROLE_STATE_SCOPE_CAP = 500;
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
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
@@ -84,6 +87,8 @@ import { isSocialRelayVertical, reconcileScopeSocialRelay, relayHostnames, } fro
84
87
  */
85
88
  export const CLI_MIN_VERSION_HEADER = 'x-substrat-cli-min-version';
86
89
  export const CLI_LATEST_VERSION_HEADER = 'x-substrat-cli-latest-version';
90
+ /** A failure's reference: the request's own id where the host minted one, else a fresh ULID. */
91
+ const referenceOf = (c) => c.get('substratInvocationId') ?? ulid();
87
92
  /**
88
93
  * The directory's classification of a scope that is not primary (#2005), which a verb sends so
89
94
  * the store it writes is marked a copy whatever its own marker says; undefined for a primary one.
@@ -701,6 +706,9 @@ const TENANT_ROUTES = [
701
706
  // #2100: the PUBLIC certificate a connection signs in with — same pin as the credential view.
702
707
  { method: 'GET', re: /\/tenants\/[^/]+\/connections\/[^/]+\/certificate$/, pin: 'path' },
703
708
  { method: 'POST', re: /\/tenants\/[^/]+\/connections\/[^/]+\/verify$/, pin: 'path' },
709
+ // #2343: a mail connection's test send. The dashboard sends it only to the signed-in person.
710
+ { method: 'POST', re: /\/tenants\/[^/]+\/connections\/[^/]+\/send-test$/, pin: 'path' },
711
+ { method: 'GET', re: /\/tenants\/[^/]+\/connections\/[^/]+\/senders$/, pin: 'path' },
704
712
  // A vertical's mailed connect links (connections.md §3.5.4): the consent landing's liveness
705
713
  // read, the callback's spend and its undo, and the integrations card's list and revoke.
706
714
  { method: 'GET', re: /\/tenants\/[^/]+\/connect-links$/, pin: 'path' },
@@ -1108,7 +1116,7 @@ export function createControlPlaneApi(options) {
1108
1116
  // row with no tenant, which only staff read, and to the log line, under the same reference.
1109
1117
  let reference = c.get('failureReference');
1110
1118
  if (!reference) {
1111
- reference = ulid();
1119
+ reference = referenceOf(c);
1112
1120
  console.error('control-plane.unhandled', { method: c.req.method, path: c.req.path, reference, tenantId, scopeId, detail: raw });
1113
1121
  }
1114
1122
  recordWithheld({ ...base, tenantId, reference }, raw, answered, cause);
@@ -1440,7 +1448,7 @@ export function createControlPlaneApi(options) {
1440
1448
  * second row for it; a route that answers the failure as a 5xx of its own also sets `failureRecorded`.
1441
1449
  */
1442
1450
  const withheldFailure = (c, entry, cause, answered) => {
1443
- const reference = ulid();
1451
+ const reference = referenceOf(c);
1444
1452
  console.error('control-plane.unhandled', {
1445
1453
  method: c.req.method,
1446
1454
  path: c.req.path,
@@ -1571,7 +1579,7 @@ export function createControlPlaneApi(options) {
1571
1579
  // says what is wrong, and the tail line is what ties it to the request that hit it.
1572
1580
  if (status >= 500) {
1573
1581
  const subject = await subjectOf();
1574
- const reference = ulid();
1582
+ const reference = referenceOf(c);
1575
1583
  c.set('failureReference', reference);
1576
1584
  console.error('control-plane.unhandled', {
1577
1585
  method: c.req.method,
@@ -2528,6 +2536,13 @@ export function createControlPlaneApi(options) {
2528
2536
  if (!grants.some((g) => g.connectionId === row.id && g.permission === input.permission)) {
2529
2537
  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
2538
  }
2539
+ // A change the held binding cannot take in place is the operator's to act on, not a failed write.
2540
+ const held = refusesBindingChange(row.provider)
2541
+ ? (await inspectorOf(row)?.bindings?.(c.var.host, row))?.find((b) => b.scopeId === scopeId)
2542
+ : undefined;
2543
+ const changeRefused = bindingChangeRefusal(row.provider, held, input);
2544
+ if (changeRefused)
2545
+ return refuseStatus(c, 409, changeRefused);
2531
2546
  const operationId = ulid();
2532
2547
  const done = await auditedChange({
2533
2548
  flow: 'connection-binding', operationId,
@@ -2567,6 +2582,70 @@ export function createControlPlaneApi(options) {
2567
2582
  }
2568
2583
  return c.json(connectionProbe.parse(await probe(c.var.host, row)));
2569
2584
  });
2585
+ /** The addresses a mail connection may send as (#2343) — the send test's picker, whole and uncut. */
2586
+ app.get('/tenants/:tenantId/connections/:id/senders', async (c) => {
2587
+ const row = await inspectableConnection(c);
2588
+ if (!row)
2589
+ return refuse(c, 'not_found', 'unknown connection');
2590
+ const sender = options.mailSenders?.[row.provider];
2591
+ if (!sender)
2592
+ return refuseStatus(c, 501, `no mail sender registered for provider '${row.provider}'`);
2593
+ return c.json(connectionSenders.parse({ senders: await sender.senders(c.var.host, row) }));
2594
+ });
2595
+ /**
2596
+ * **Send a test message through a connection** (#2343) — the probe for mail, which cannot be
2597
+ * probed without sending. From one of the connection's own senders, to one recipient; the
2598
+ * dashboard's door sends only to the signed-in person's own address.
2599
+ *
2600
+ * Like `/verify`, a refusal is `200 { ok: false, error }` in the connector's own words (outside
2601
+ * the granted scope, throttled, sign-in failed), and the send rides the connector's recorded
2602
+ * egress, so health is written as for any send. A success is recorded by the connector as that
2603
+ * sender's verification. Not an audited admin-log mutation, the same class as a probe; who
2604
+ * asked is logged on the request record instead, never the recipient.
2605
+ */
2606
+ app.post('/tenants/:tenantId/connections/:id/send-test', async (c) => {
2607
+ const row = await inspectableConnection(c);
2608
+ if (!row)
2609
+ return refuse(c, 'not_found', 'unknown connection');
2610
+ const sender = options.mailSenders?.[row.provider];
2611
+ if (!sender)
2612
+ return refuseStatus(c, 501, `no mail sender registered for provider '${row.provider}'`);
2613
+ const body = connectionSendTest.safeParse(await c.req.json().catch(() => null));
2614
+ if (!body.success)
2615
+ return refuse(c, 'validation_failed', 'from and to must each be one email address');
2616
+ const { from, to } = body.data;
2617
+ const senders = await sender.senders(c.var.host, row);
2618
+ if (!senders.some((s) => s.toLowerCase() === from.toLowerCase())) {
2619
+ return refuse(c, 'validation_failed', `'${from}' is not one of this connection's sender addresses`);
2620
+ }
2621
+ const principal = c.get('principal');
2622
+ console.log('connection.send-test', {
2623
+ tenantId: row.tenantId,
2624
+ connectionId: row.id,
2625
+ provider: row.provider,
2626
+ actor: c.get('actor'),
2627
+ onBehalfOf: principal.kind === 'tenant' ? (principal.onBehalfOf?.principal ?? null) : null,
2628
+ });
2629
+ try {
2630
+ await sender.send(c.var.host, row, {
2631
+ from: { email: from },
2632
+ to: [{ email: to }],
2633
+ subject: 'Substrat test message',
2634
+ html: `<p>${SEND_TEST_TEXT}</p>`,
2635
+ text: SEND_TEST_TEXT,
2636
+ attachments: [],
2637
+ });
2638
+ return c.json(connectionSendTestResult.parse({ ok: true, status: null, error: null }));
2639
+ }
2640
+ catch (e) {
2641
+ const status = e?.status;
2642
+ return c.json(connectionSendTestResult.parse({
2643
+ ok: false,
2644
+ status: typeof status === 'number' ? status : null,
2645
+ error: (e instanceof Error ? e.message : String(e)).slice(0, 600),
2646
+ }));
2647
+ }
2648
+ });
2570
2649
  /**
2571
2650
  * **What the connection has done** (#605) — the connector's dispatch ledger, projected.
2572
2651
  *
@@ -4296,7 +4375,7 @@ export function createControlPlaneApi(options) {
4296
4375
  // backstop would file is filed here as a pair, the tenant's row with the sentences only.
4297
4376
  const tenantStores = storeSentences.length ? ` (stores: ${storeSentences.join('; ')})` : '';
4298
4377
  if (e.status >= 500 && e.status !== 501 && tenantStores !== stores) {
4299
- const reference = ulid();
4378
+ const reference = referenceOf(c);
4300
4379
  recordWithheld({
4301
4380
  actor,
4302
4381
  operation: `${c.req.method} ${c.req.routePath}`,
@@ -4317,7 +4396,7 @@ export function createControlPlaneApi(options) {
4317
4396
  const sentence = (reference) => `the reconcile of this scope failed (${e.status})${stores} (reference ${reference})`;
4318
4397
  if (e.status < 500) {
4319
4398
  // A refusal, which the 5xx backstop leaves unrecorded for staff too: logged, no row.
4320
- const reference = ulid();
4399
+ const reference = referenceOf(c);
4321
4400
  console.warn('control-plane.withheld', {
4322
4401
  method: c.req.method,
4323
4402
  path: c.req.path,
@@ -7577,7 +7656,13 @@ export function createControlPlaneApi(options) {
7577
7656
  throw new ControlPlaneError(409, `${operation} refused: the hold on scope store driver '${each}' lapsed during the upload; try again`);
7578
7657
  }
7579
7658
  }
7580
- await options.ensureScopeStores({ vertical: slug, stores: stores.filter((store) => store.driver === driver), loadRelease });
7659
+ const registration = (options.storeDrivers ?? STORE_DRIVERS).find((k) => k.driver === driver);
7660
+ if (!registration)
7661
+ throw new ControlPlaneError(403, `scope store driver '${driver}' is not registered`);
7662
+ // Read under the shared hold (D-65): a revoke takes the driver exclusively, so one written
7663
+ // before this read is seen, and none is written until this upload is done.
7664
+ const approvals = await readStoreDriverApprovals(host.admin, actor, registration);
7665
+ await options.ensureScopeStores({ vertical: slug, stores: stores.filter((store) => store.driver === driver), loadRelease, approvals });
7581
7666
  }
7582
7667
  }
7583
7668
  catch (e) {
@@ -10988,20 +11073,8 @@ export function createControlPlaneApi(options) {
10988
11073
  throw new ControlPlaneError(422, `not a store driver revoke: ${parsed.error.issues.map(safeIssue).join('; ')}`);
10989
11074
  return parsed.data;
10990
11075
  };
10991
- /** The pin a release is run under, or a refusal: the platform runs only what this repo pins. */
10992
- const pinOf = async (c, registration, sha256) => {
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
- };
11076
+ // Whether the release is stored, approved and keeps the schema is judged inside
11077
+ // `runStoreDriverRollout`, under its exclusive hold (D-65), never here before it.
11005
11078
  const rollOut = async (c, registration, sha256, action) => {
11006
11079
  const { flow, ensureScopeStores } = releaseFlow();
11007
11080
  const actor = c.get('actor');
@@ -11016,22 +11089,11 @@ export function createControlPlaneApi(options) {
11016
11089
  const result = await ensureScopeStores({ ...input, loadRelease });
11017
11090
  return result ? result.uploaded > 0 : true;
11018
11091
  },
11019
- ensureCanary: (to) => flow.ensureCanary({ driver: registration.driver, to, loadRelease }),
11092
+ ensureCanary: (to, approvals) => flow.ensureCanary({ driver: registration.driver, to, loadRelease, approvals }),
11020
11093
  callCanary: flow.callCanary,
11021
11094
  });
11022
11095
  return c.json(result, result.complete ? 200 : 202);
11023
11096
  };
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
11097
  /** Where the platform's rebuild of every script of the driver stands (#2309), if it rebuilds them. */
11036
11098
  const rebuildView = async (admin, actor, driver, known) => {
11037
11099
  const fingerprint = known ?? (await options.storeReleases?.fingerprint?.());
@@ -11046,19 +11108,26 @@ export function createControlPlaneApi(options) {
11046
11108
  const registration = registeredDriver(c.req.param('driver'));
11047
11109
  const [state, approvals] = await Promise.all([
11048
11110
  c.var.admin.storeDriverState(c.get('actor'), registration.driver),
11049
- c.var.admin.storeDriverApprovals(c.get('actor'), registration.driver),
11111
+ readStoreDriverApprovals(c.var.admin, c.get('actor'), registration),
11050
11112
  ]);
11051
11113
  const rebuild = await rebuildView(c.var.admin, c.get('actor'), registration.driver);
11052
- return c.json({ driver: registration.driver, pins: registration.releases, approvals, ...state, ...(rebuild ? { rebuild } : {}) });
11114
+ // `pins` stays for released CLIs (RFC §5 step 3): the releases a move may go to now, one per live approval.
11115
+ const pins = approvals.records
11116
+ .filter((a) => liveApprovalOf(approvals, a.sha256) === a)
11117
+ .map((a) => ({ sha256: a.sha256, sourceCommit: a.sourceCommit, schema: a.schema }));
11118
+ return c.json({ driver: registration.driver, pins, approvals: approvals.records, ...state, ...(rebuild ? { rebuild } : {}) });
11053
11119
  });
11054
- // Store a release's bytes, before or after its pin lands (D-63). Idempotent per bundle.
11120
+ // Store a release's bytes, before or after a person approves them (D-65). Idempotent per bundle.
11055
11121
  app.put('/store-drivers/:driver/releases', async (c) => {
11056
11122
  const registration = registeredDriver(c.req.param('driver'));
11057
11123
  const actor = await byAPerson(c);
11058
11124
  const { flow } = releaseFlow();
11059
11125
  const body = await readCappedJson(c.req.raw, STORE_RELEASE_REQUEST_MAX_BYTES);
11060
11126
  const release = await storeDriverRelease({ admin: c.var.admin, actor, bytes: flow.bytes, driver: registration.driver, body });
11061
- return c.json({ release, pinned: registration.releases.some((p) => p.sha256 === release.sha256 && p.sourceCommit === release.sourceCommit) }, 201);
11127
+ const live = liveApprovalOf(await readStoreDriverApprovals(c.var.admin, actor, registration), release.sha256);
11128
+ const approved = live !== undefined && live.sourceCommit === release.sourceCommit;
11129
+ // `pinned` stays beside `approved` for released CLIs, meaning the same (RFC §5 step 3).
11130
+ return c.json({ release, pinned: approved, approved }, 201);
11062
11131
  });
11063
11132
  // The approval is a second, person-only write over bytes the platform already named by digest.
11064
11133
  app.post('/store-drivers/:driver/releases/:sha256/approvals', async (c) => {
@@ -11066,17 +11135,20 @@ export function createControlPlaneApi(options) {
11066
11135
  const actor = await byAPerson(c, approvalWords);
11067
11136
  const sha256 = releaseSha(c.req.param('sha256'));
11068
11137
  const body = parseApprovalBody(await readCappedJson(c.req.raw, 64 * 1024));
11138
+ // Refused here, not only by the readers (D-65 §3): a row its basis does not admit could never
11139
+ // run, yet as a live approval it would hold the one slot a corrected approval needs. Fixed text,
11140
+ // no input echoed.
11141
+ if (!storeDriverBasisAdmits(registration.isolationBasis, body.sourceReferences)) {
11142
+ throw new ControlPlaneError(422, 'not a store driver approval: sourceReferences: outside_isolation_basis');
11143
+ }
11069
11144
  const flow = options.storeReleases;
11070
11145
  if (!flow)
11071
11146
  throw new ControlPlaneError(501, 'this control plane cannot read stored release bytes for approval');
11072
11147
  const approval = await approveStoredDriverRelease({ admin: c.var.admin, actor, bytes: flow.bytes, driver: registration.driver, sha256, approval: body });
11073
11148
  const state = await c.var.admin.storeDriverState(actor, registration.driver);
11074
11149
  const approvals = state.current ? await c.var.admin.storeDriverApprovals(actor, registration.driver) : [];
11075
- const currentSchema = state.current
11076
- ? approvals.find((a) => a.sha256 === state.current.sha256)?.schema
11077
- ?? registration.releases.find((p) => p.sha256 === state.current.sha256)?.schema
11078
- ?? null
11079
- : null;
11150
+ // The current release's approval facts, revoked or not: they are fixed at its first approval.
11151
+ const currentSchema = state.current ? approvals.find((a) => a.sha256 === state.current.sha256)?.schema ?? null : null;
11080
11152
  return c.json({ approval, currentSchema }, 201);
11081
11153
  });
11082
11154
  app.post('/store-drivers/:driver/releases/:sha256/revoke', async (c) => {
@@ -11100,20 +11172,19 @@ export function createControlPlaneApi(options) {
11100
11172
  if (!flow)
11101
11173
  throw new ControlPlaneError(501, 'this control plane cannot move the store canary');
11102
11174
  const loadRelease = storedReleaseLoader({ admin: c.var.admin, actor, bytes: flow.bytes });
11103
- await flow.ensureCanary({ driver: registration.driver, to, loadRelease });
11175
+ // Called under the revoke's exclusive hold, so the approvals read here are the ones it judges.
11176
+ const approvals = await readStoreDriverApprovals(c.var.admin, actor, registration);
11177
+ await flow.ensureCanary({ driver: registration.driver, to, loadRelease, approvals });
11104
11178
  },
11105
11179
  });
11106
11180
  return c.json(result, result.complete ? 200 : 202);
11107
11181
  });
11108
- // Roll a stored, pinned release out: the canary, then every vertical that ever declared the
11182
+ // Roll a stored, approved release out: the canary, then every vertical that ever declared the
11109
11183
  // driver. Posting the same release again converges a rollout that stopped.
11110
11184
  app.post('/store-drivers/:driver/rollouts', async (c) => {
11111
11185
  const registration = registeredDriver(c.req.param('driver'));
11112
11186
  await byAPerson(c);
11113
11187
  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
11188
  return rollOut(c, registration, sha256, 'rollout');
11118
11189
  });
11119
11190
  // Return to the immediately previous good release (D-63), never further and never a guess. After
@@ -11126,8 +11197,6 @@ export function createControlPlaneApi(options) {
11126
11197
  const { current, rollbackTarget } = await c.var.admin.storeDriverState(c.get('actor'), registration.driver);
11127
11198
  if (!current || !rollbackTarget)
11128
11199
  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
11200
  return rollOut(c, registration, rollbackTarget, 'rollback');
11132
11201
  });
11133
11202
  // Run a pass of the platform's rebuild of every script of the driver now (#2309): the scheduled