@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 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
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
@@ -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
- await options.ensureScopeStores({ vertical: slug, stores: stores.filter((store) => store.driver === driver), loadRelease });
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
- /** 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
- };
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.storeDriverApprovals(c.get('actor'), registration.driver),
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
- return c.json({ driver: registration.driver, pins: registration.releases, approvals, ...state, ...(rebuild ? { rebuild } : {}) });
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 its pin lands (D-63). Idempotent per bundle.
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
- return c.json({ release, pinned: registration.releases.some((p) => p.sha256 === release.sha256 && p.sourceCommit === release.sourceCommit) }, 201);
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
- 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;
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
- await flow.ensureCanary({ driver: registration.driver, to, loadRelease });
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, pinned release out: the canary, then every vertical that ever declared the
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