@substrat-run/control-plane-api 0.163.0 → 0.165.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
@@ -1,7 +1,7 @@
1
1
  import type { ConnectionScopeBinding, ConnectionScopeBindingInput } from '@substrat-run/control-plane-client';
2
2
  import { Hono } from 'hono';
3
3
  import { type ConnectionCertificate, z, type ScopeStoreNeed, type StoreDriverRegistration } from '@substrat-run/contracts';
4
- import type { Connection, ConnectionActivity, ConnectionActivitySource, ConnectionCredential, ConnectionProbe, PlatformActorId } from '@substrat-run/contracts';
4
+ import type { Connection, ConnectionActivity, ConnectionActivitySource, ConnectionCredential, ConnectionProbe, PlatformActorId, ScopeId, TenantId } from '@substrat-run/contracts';
5
5
  import type { CrossVerticalOptions, HostAdmin, OpsFailureInput, ScopeHost } from '@substrat-run/kernel';
6
6
  import type { PlatformActorAuth, BuilderAuth, Principal, TenantServiceAuth } from './auth.js';
7
7
  import { type VerticalClient } from './vertical-client.js';
@@ -9,7 +9,7 @@ import { type CopyJobs } from './copy-job.js';
9
9
  import type { PlatformScopeStores } from './platform-scope-stores.js';
10
10
  import type { ScopeStoreEnsurerOptions, StoreCanaryTarget } from './store-script.js';
11
11
  import { type CanaryCall, type StoreReleaseBytes } from './store-releases.js';
12
- import { type ConnectionCandidatePrepare } from './connection-relay.js';
12
+ import { type ConnectionCandidatePrepare, type VerticalConnectorDeclarationOf } from './connection-relay.js';
13
13
  import type { DeployVerticalFn, FetchVerticalAssetFn, FetchVerticalModulesFn } from './deploy.js';
14
14
  import type { PatchScriptBindingsFn } from './wfp.js';
15
15
  import { type ObservabilityReader } from './observability.js';
@@ -349,6 +349,22 @@ export interface ControlPlaneApiOptions {
349
349
  * verify a Fortnox key yet" is a true statement; "your Fortnox key is fine" is not.
350
350
  */
351
351
  connectionInspectors?: Record<string, ConnectionInspector>;
352
+ /**
353
+ * The inspector for a vertical-defined provider (`<namespace>.<name>`, vertical-connectors.md
354
+ * §6.4), which `connectionInspectors` never names: built per (tenant, vertical), since its code
355
+ * runs in that vertical's deployment. Asked only for a dotted provider the record lacks. Absent,
356
+ * such a provider answers as an unregistered one does.
357
+ */
358
+ verticalConnectorInspector?: (provider: string, context: {
359
+ tenantId: TenantId;
360
+ vertical: string;
361
+ scopeId: ScopeId | null;
362
+ }) => ConnectionInspector | undefined;
363
+ /**
364
+ * A vertical-defined provider's serving declaration, which the tenant connection route grants
365
+ * from (`VerticalConnectorDeclarationOf`). Absent, that route refuses a dotted provider.
366
+ */
367
+ verticalConnectorDeclaration?: VerticalConnectorDeclarationOf;
352
368
  /**
353
369
  * Where this platform's compute actually runs — the coordinates a staff surface needs
354
370
  * to hand an operator a link INTO the provider's own console (the right script, the
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
@@ -2,8 +2,9 @@ import { PREVIEW_CONNECTIONS_REFUSAL } from './connection-relay.js';
2
2
  import { connectionBindingOptions } from './connection-binding-options.js';
3
3
  import { resolveObservabilityWindow } from './observability-window.js';
4
4
  import { Hono } from 'hono';
5
- import { permissionKey, parseHostname, withLabel, RESERVED_LABEL_SEPARATOR, adminAction, lakeEventsInput, ownerTransferInput, memberInviteInput, memberRoleInput, ASSET_PART_PREFIX, assetHash, STORE_DRIVERS, STORE_RELEASE_MAX_BYTES, 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, capabilityFilterQuery, capabilityId as capabilityIdSchema, PLATFORM_FEATURES_HEADER, PLATFORM_FEATURE_SCOPE_SWEEPER, SOCIAL_RELAY_VERTICALS, } from '@substrat-run/contracts';
5
+ import { permissionKey, parseHostname, withLabel, RESERVED_LABEL_SEPARATOR, adminAction, lakeEventsInput, ownerTransferInput, memberInviteInput, memberRoleInput, ASSET_PART_PREFIX, assetHash, STORE_DRIVERS, STORE_RELEASE_MAX_BYTES, 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, SOCIAL_RELAY_VERTICALS, isVerticalConnectorProvider, } from '@substrat-run/contracts';
6
6
  import { upstreamHandleIn, upstreamReferenceOf, withUpstreamReference } from '@substrat-run/contracts';
7
+ import { ENGINE_ENTITLEMENT_KEYS, bareSlug, declarableEntitlementKey, entitlementNamespaceRefusal, grantableEntitlements } from '@substrat-run/contracts';
7
8
  import { SCOPE_COPY_LEASE_MS, copyRestoreFence, publishedMigrationDigest, replayStoreErasures } from '@substrat-run/kernel';
8
9
  import { attributeFailure } from './failure-attribution.js';
9
10
  import { auditedChange, withAuditedOutcomes } from './audited-change.js';
@@ -28,7 +29,7 @@ import { NON_PROBLEM_REFUSAL, coded, mapError, relayed } from './errors.js';
28
29
  import { maskDump, maskRecords } from './mask.js';
29
30
  import { createPseudonymizer } from './pseudonymize.js';
30
31
  import { openDump, sealDump } from './seal.js';
31
- import { assertSandboxContract, verticalUploadBindings, migrationsDigestBindings, deployManifest, storedDeployManifest, pushedOwnerGrantProblems, pushedModuleRegistryProblems, pushedPermissionRegistry, deploymentRefFor, stableDeploymentRefFor, nextMigrationTag, upstreamStatusOf, } from './deploy.js';
32
+ import { assertSandboxContract, verticalUploadBindings, migrationsDigestBindings, deployManifest, storedDeployManifest, pushedOwnerGrantProblems, pushedConnectorProblems, pushedModuleRegistryProblems, pushedPermissionRegistry, deploymentRefFor, stableDeploymentRefFor, nextMigrationTag, upstreamStatusOf, } from './deploy.js';
32
33
  /**
33
34
  * Answer with a problem document (#113 phase 4).
34
35
  *
@@ -113,6 +114,18 @@ class ExportBreakRefused extends ControlPlaneError {
113
114
  this.breaks = breaks;
114
115
  }
115
116
  }
117
+ /**
118
+ * #2577: a serve whose upload carried a DO-class migration, refused its record because another serve
119
+ * changed the serving row's class set or tag during the upload. The runtime's migration tag is now
120
+ * past the record's, so a re-serve diffed from the record names an `old_tag` the runtime refuses:
121
+ * promoting again cannot land, and the record needs repair.
122
+ */
123
+ class ServeRecordBehindScript extends ControlPlaneError {
124
+ constructor(slug) {
125
+ super(409, `the serving record of '${slug}' changed while this serve's upload added a Durable Object class, so the ` +
126
+ "record's migration tag is behind the serving script's (#2577)");
127
+ }
128
+ }
116
129
  /**
117
130
  * A carry whose source store kept changing between its export and its bind (#1722, Codex #2008
118
131
  * r12): every re-carry from a fresh export found the source changed again before it could bind.
@@ -807,6 +820,7 @@ const TENANT_ROUTES = [
807
820
  { method: 'GET', re: /\/verticals\/[^/]+\/versions\/[^/]+\/registry$/, pin: 'owner' },
808
821
  { method: 'GET', re: /\/verticals\/[^/]+\/versions\/[^/]+\/migrations$/, pin: 'owner' },
809
822
  { method: 'GET', re: /\/verticals\/[^/]+\/versions\/[^/]+\/model$/, pin: 'owner' },
823
+ { method: 'GET', re: /\/verticals\/[^/]+\/versions\/[^/]+\/concept$/, pin: 'owner' },
810
824
  { method: 'GET', re: /\/verticals\/[^/]+\/versions\/[^/]+\/flow$/, pin: 'owner' },
811
825
  { method: 'GET', re: /\/verticals\/[^/]+\/versions\/[^/]+\/assets$/, pin: 'owner' },
812
826
  { method: 'GET', re: /\/verticals\/[^/]+\/versions\/[^/]+\/schedules$/, pin: 'owner' },
@@ -1219,6 +1233,7 @@ export function createControlPlaneApi(options) {
1219
1233
  // The emitted entity model of one version (#1214) — owner-narrowed the same way;
1220
1234
  // the dashboard's Model tab reads it.
1221
1235
  { method: 'GET', re: /\/verticals\/[^/]+\/versions\/[^/]+\/model$/ },
1236
+ { method: 'GET', re: /\/verticals\/[^/]+\/versions\/[^/]+\/concept$/ },
1222
1237
  { method: 'GET', re: /\/verticals\/[^/]+\/versions\/[^/]+\/schedules$/ },
1223
1238
  { method: 'GET', re: /\/verticals\/[^/]+\/channels$/ },
1224
1239
  { method: 'GET', re: /\/verticals\/[^/]+\/channels\/[^/]+\/history$/ },
@@ -2161,7 +2176,11 @@ export function createControlPlaneApi(options) {
2161
2176
  return refuse(c, 'validation_failed', 'body tenantId disagrees with the route tenant');
2162
2177
  }
2163
2178
  try {
2164
- return c.json(await relayConnectionUpsert(c.var.host, c.get('actor'), { ...body, tenantId }, { probeCandidate, prepareCandidate }));
2179
+ return c.json(await relayConnectionUpsert(c.var.host, c.get('actor'), { ...body, tenantId }, {
2180
+ probeCandidate,
2181
+ prepareCandidate,
2182
+ ...(options.verticalConnectorDeclaration ? { declarationOf: options.verticalConnectorDeclaration } : {}),
2183
+ }));
2165
2184
  }
2166
2185
  catch (err) {
2167
2186
  if (err instanceof ConnectionRelayError && err.status < 500) {
@@ -2271,12 +2290,20 @@ export function createControlPlaneApi(options) {
2271
2290
  * exactly like an absent one — and revoked rows are excluded: a withdrawn credential
2272
2291
  * has no secret left to probe with, so "revoked" is a 404 for these reads too.
2273
2292
  */
2293
+ /**
2294
+ * The inspector for `provider`: the record's entry, else, for a vertical-defined provider, the one
2295
+ * built for the vertical the connection belongs to (vertical-connectors.md §6.4).
2296
+ */
2297
+ const inspectorFor = (provider, context) => options.connectionInspectors?.[provider] ??
2298
+ (isVerticalConnectorProvider(provider) ? options.verticalConnectorInspector?.(provider, context) : undefined);
2299
+ /** {@link inspectorFor} for a connection row, which names its own tenant and vertical. */
2300
+ const inspectorOf = (row) => inspectorFor(row.provider, { tenantId: row.tenantId, vertical: row.vertical, scopeId: null });
2274
2301
  /**
2275
2302
  * The connect-time gate (#605), as the relay consumes it: ask the provider about a
2276
2303
  * candidate credential, or answer `undefined` when this platform has no probe for it —
2277
2304
  * in which case the upsert behaves exactly as it always did, storing unverified.
2278
2305
  */
2279
- const probeCandidate = async (provider, secret) => options.connectionInspectors?.[provider]?.probeCandidate?.(secret);
2306
+ const probeCandidate = async (provider, secret, context) => inspectorFor(provider, context)?.probeCandidate?.(secret);
2280
2307
  /** #2100: the provider's own half of a credential, made before the probe sees it. */
2281
2308
  const prepareCandidate = (provider) => options.connectionInspectors?.[provider]?.prepareCandidate;
2282
2309
  const inspectableConnection = async (c) => {
@@ -2292,7 +2319,7 @@ export function createControlPlaneApi(options) {
2292
2319
  const row = await inspectableConnection(c);
2293
2320
  if (!row)
2294
2321
  return refuse(c, 'not_found', 'unknown connection');
2295
- const read = options.connectionInspectors?.[row.provider]?.bindings;
2322
+ const read = inspectorOf(row)?.bindings;
2296
2323
  if (!read)
2297
2324
  return refuseStatus(c, 501, `no scope binding registered for provider '${row.provider}'`);
2298
2325
  const bindings = await read(c.var.host, row);
@@ -2313,7 +2340,7 @@ export function createControlPlaneApi(options) {
2313
2340
  const row = await inspectableConnection(c);
2314
2341
  if (!row)
2315
2342
  return refuse(c, 'not_found', 'unknown connection');
2316
- const bind = options.connectionInspectors?.[row.provider]?.bindScope;
2343
+ const bind = inspectorOf(row)?.bindScope;
2317
2344
  if (!bind)
2318
2345
  return refuseStatus(c, 501, `no scope binding registered for provider '${row.provider}'`);
2319
2346
  const scopeId = scopeIdSchema.parse(c.req.param('scopeId'));
@@ -2367,7 +2394,7 @@ export function createControlPlaneApi(options) {
2367
2394
  const row = await inspectableConnection(c);
2368
2395
  if (!row)
2369
2396
  return refuse(c, 'not_found', 'unknown connection');
2370
- const probe = options.connectionInspectors?.[row.provider]?.probe;
2397
+ const probe = inspectorOf(row)?.probe;
2371
2398
  if (!probe) {
2372
2399
  return refuseStatus(c, 501, `no probe registered for provider '${row.provider}'`);
2373
2400
  }
@@ -2389,7 +2416,7 @@ export function createControlPlaneApi(options) {
2389
2416
  const row = await inspectableConnection(c);
2390
2417
  if (!row)
2391
2418
  return refuse(c, 'not_found', 'unknown connection');
2392
- const activity = options.connectionInspectors?.[row.provider]?.activity;
2419
+ const activity = inspectorOf(row)?.activity;
2393
2420
  if (!activity) {
2394
2421
  return refuseStatus(c, 501, `no activity view registered for provider '${row.provider}'`);
2395
2422
  }
@@ -2410,7 +2437,7 @@ export function createControlPlaneApi(options) {
2410
2437
  const row = await inspectableConnection(c);
2411
2438
  if (!row)
2412
2439
  return refuse(c, 'not_found', 'unknown connection');
2413
- const credential = options.connectionInspectors?.[row.provider]?.credential;
2440
+ const credential = inspectorOf(row)?.credential;
2414
2441
  if (!credential) {
2415
2442
  return refuseStatus(c, 501, `no credential view registered for provider '${row.provider}'`);
2416
2443
  }
@@ -2425,7 +2452,7 @@ export function createControlPlaneApi(options) {
2425
2452
  const row = await inspectableConnection(c);
2426
2453
  if (!row)
2427
2454
  return refuse(c, 'not_found', 'unknown connection');
2428
- const certificate = await options.connectionInspectors?.[row.provider]?.certificate?.(c.var.host, row);
2455
+ const certificate = await inspectorOf(row)?.certificate?.(c.var.host, row);
2429
2456
  if (!certificate)
2430
2457
  return refuse(c, 'not_found', 'this connection signs in with no certificate');
2431
2458
  return c.json(connectionCertificate.parse(certificate));
@@ -5883,7 +5910,10 @@ export function createControlPlaneApi(options) {
5883
5910
  });
5884
5911
  // The write half of the governed pull (§8) — load a dump INTO an existing scope:
5885
5912
  // restore a backup, back out to a snapshot, or land a locally-built world on a
5886
- // hosted app. Staff-only like the export (not in BUILDER_ROUTES). No jurisdiction
5913
+ // hosted app. Staff, and a tenant's own credential (TENANT_ROUTES, path-pinned): the
5914
+ // dashboard's Import sends a file a team owner or admin uploaded (#2573), the dashboard
5915
+ // asking that role, since this plane sees the credential, not the person. Builders are
5916
+ // refused (not in BUILDER_ROUTES). The dump is input, judged on load. No jurisdiction
5887
5917
  // gate: this is ingress — the data ENTERS the scope's pinned region, it never
5888
5918
  // leaves one. Mirrors the export's delegation shape: the canonical `restoreScope`
5889
5919
  // writes the audit row (and the co-located bytes); when the scope's data lives in
@@ -5988,7 +6018,7 @@ export function createControlPlaneApi(options) {
5988
6018
  // A restore throw is driven by the caller-supplied dump (a shape the target cannot
5989
6019
  // load, a DDL the engine rejects), so DISCLOSE it as an actionable 422 rather than
5990
6020
  // collapsing to the generic 500 `internal error` mapError would produce (#321,
5991
- // secondary obs #2). The route is staff/owner-gated and the detail is about the
6021
+ // secondary obs #2). The route is staff- or own-tenant-gated and the detail is about the
5992
6022
  // dump the caller sent, not another tenant's state.
5993
6023
  const detail = e instanceof Error ? e.message : String(e);
5994
6024
  return refuseStatus(c, 422, 'restore failed — the backup could not be loaded', { detail });
@@ -6743,6 +6773,8 @@ export function createControlPlaneApi(options) {
6743
6773
  * vertical stays owner-only, and so do the reads narrowed on purpose (migration SQL,
6744
6774
  * the prod history).
6745
6775
  */
6776
+ /** The bare slug of every registered vertical: what a manager's SKU may not be (#2388). */
6777
+ const verticalSlugsOf = async (actor) => (await host.admin.listVerticals(actor)).map((v) => bareSlug(v.slug));
6746
6778
  /**
6747
6779
  * Whether a CONFINED principal may grant its own tenant `key` (#977): some vertical it
6748
6780
  * can read — its own, or a listed one — declares the key, or is the vertical whose
@@ -6750,14 +6782,40 @@ export function createControlPlaneApi(options) {
6750
6782
  * (`installEntitlements`), checked here so a credential cannot grant what no install
6751
6783
  * would. Read through the configured host: a check audits nothing.
6752
6784
  */
6785
+ /**
6786
+ * The refusal for REGISTERING a vertical whose bare slug is a plan SKU a manager declares (#2388),
6787
+ * so the manager's key does not change meaning under a later registration. A manager is a row that
6788
+ * holds the tenant-provisioner capability or declares `provisions` (the capability is granted after
6789
+ * the first push). Its own slug and the engine keys are always declarable, so they reserve nothing.
6790
+ * Only a new slug is judged; the sentence names no other vertical.
6791
+ */
6792
+ const slugClaimsSku = async (actor, slug) => {
6793
+ const all = await host.admin.listVerticals(actor);
6794
+ if (all.some((v) => v.slug === slug))
6795
+ return undefined;
6796
+ const name = bareSlug(slug);
6797
+ return all.some((v) => (v.tenantProvisioner || v.provisions?.length) &&
6798
+ bareSlug(v.slug) !== name &&
6799
+ !ENGINE_ENTITLEMENT_KEYS.includes(name) &&
6800
+ v.entitlements?.includes(name))
6801
+ ? `'${bareSlug(slug)}' is not available as a vertical name`
6802
+ : undefined;
6803
+ };
6804
+ /** The declared keys of a registry row that an install may grant: a manager's include plan SKUs. */
6805
+ const grantableOf = (v, slugs) => grantableEntitlements(v.slug, v.entitlements ?? [], v.tenantProvisioner ? slugs : undefined);
6753
6806
  const tenantGrantable = async (p, key) => {
6754
6807
  const pin = confinedTenant(p);
6755
6808
  if (pin === null)
6756
6809
  return true;
6757
- const visible = (await host.admin.listVerticals(p.actor)).filter((v) => v.ownerTenant === pin || v.listed);
6758
- return visible.some((v) => v.entitlements && v.entitlements.length > 0
6759
- ? v.entitlements.includes(key)
6760
- : (v.slug.split('/').pop() || v.slug) === key);
6810
+ const all = await host.admin.listVerticals(p.actor);
6811
+ const slugs = all.map((v) => bareSlug(v.slug));
6812
+ const visible = all.filter((v) => v.ownerTenant === pin || v.listed);
6813
+ return visible.some((v) => {
6814
+ // The install's own derivation (`installEntitlements`): the declared keys the vertical may
6815
+ // declare, else its bare slug.
6816
+ const grantable = grantableOf(v, slugs);
6817
+ return (grantable.length > 0 ? grantable : declarableEntitlementKey(v.slug, bareSlug(v.slug), v.tenantProvisioner ? slugs : undefined) ? [bareSlug(v.slug)] : []).includes(key);
6818
+ });
6761
6819
  };
6762
6820
  const notReadable = async (p, slug) => {
6763
6821
  const pin = confinedTenant(p);
@@ -6834,7 +6892,13 @@ export function createControlPlaneApi(options) {
6834
6892
  ? all.filter((v) => v.ownerTenant === p.tenantId)
6835
6893
  : all.filter((v) => (ownerTenant === undefined || v.ownerTenant === ownerTenant) &&
6836
6894
  (visibleTo === undefined || v.listed || v.ownerTenant === visibleTo));
6837
- return c.json(pageSlice(visible, page, (v) => v.slug));
6895
+ // The install catalog (`visibleTo`) carries what an install may grant, not what a row declared
6896
+ // before the namespace rule (#2388), so the dashboard's install and the grant paths agree.
6897
+ const slugs = visibleTo === undefined || p.kind === 'builder' ? [] : all.map((v) => bareSlug(v.slug));
6898
+ const rows = visibleTo === undefined || p.kind === 'builder'
6899
+ ? visible
6900
+ : visible.map((v) => (v.entitlements ? { ...v, entitlements: grantableOf(v, slugs) } : v));
6901
+ return c.json(pageSlice(rows, page, (v) => v.slug));
6838
6902
  });
6839
6903
  // -- the service→dimensions resolver (#1231's last item) -----------------------
6840
6904
  // What a Cloudflare service ref MEANS: each owned vertical's serving script and
@@ -6939,6 +7003,15 @@ export function createControlPlaneApi(options) {
6939
7003
  return refuse(c, 'forbidden', 'forbidden');
6940
7004
  input.ownerTenant = p.tenantId;
6941
7005
  }
7006
+ const claimed = await slugClaimsSku(c.get('actor'), input.slug);
7007
+ if (claimed)
7008
+ return refuseStatus(c, 422, claimed);
7009
+ // #2388: the same declaration rule as a push, for any principal that is not staff.
7010
+ if (p.kind !== 'staff') {
7011
+ const foreignKey = entitlementNamespaceRefusal(input.slug, input.entitlements ?? [], input.provisions?.length ? await verticalSlugsOf(c.get('actor')) : undefined);
7012
+ if (foreignKey)
7013
+ return refuseStatus(c, 422, foreignKey);
7014
+ }
6942
7015
  await c.var.admin.registerVertical(c.get('actor'), input);
6943
7016
  // Idempotent on the slug (a conflicting re-register throws below the seam), so
6944
7017
  // read back rather than echo the request.
@@ -7009,6 +7082,9 @@ export function createControlPlaneApi(options) {
7009
7082
  const ownerGrants = pushedOwnerGrantProblems(checked.data);
7010
7083
  if (ownerGrants.length > 0)
7011
7084
  return refuseManifest({ path: ['ownerGrants'], message: ownerGrants.join('; ') });
7085
+ const connectorProblems = pushedConnectorProblems(checked.data);
7086
+ if (connectorProblems.length > 0)
7087
+ return refuseManifest({ path: ['connectors'], message: connectorProblems.join('; ') });
7012
7088
  }
7013
7089
  await c.var.admin.publishVersion(c.get('actor'), input);
7014
7090
  const version = await c.var.admin.getVersion(c.get('actor'), input.id, slug);
@@ -7127,6 +7203,16 @@ export function createControlPlaneApi(options) {
7127
7203
  calls: parsed?.calls ?? null,
7128
7204
  });
7129
7205
  });
7206
+ // Published documentation only, never source files. Same owner/listed visibility as model.
7207
+ app.get('/verticals/:slug/versions/:id/concept', async (c) => {
7208
+ const p = c.get('principal');
7209
+ const slug = await resolveVerticalId(c, c.req.param('slug'));
7210
+ if (await notReadable(p, slug))
7211
+ return refuse(c, 'not_found', 'not found');
7212
+ const json = await c.var.admin.versionManifest(c.get('actor'), slug, c.req.param('id'));
7213
+ const parsed = json ? storedDeployManifest.parse(JSON.parse(json)) : null;
7214
+ return c.json({ concept: parsed?.concept ?? null });
7215
+ });
7130
7216
  app.get('/verticals/:slug/versions/:id/model', async (c) => {
7131
7217
  const p = c.get('principal');
7132
7218
  const slug = await resolveVerticalId(c, c.req.param('slug'));
@@ -7488,6 +7574,11 @@ export function createControlPlaneApi(options) {
7488
7574
  : undefined);
7489
7575
  await recordWireUpload(admin, actor, versionId, wire);
7490
7576
  const addedClasses = serving ? doClasses.some((cls) => !serving.doClasses.includes(cls)) : false;
7577
+ // #2577: `serving`'s class set and tag are what the upload's migration was diffed from, so the
7578
+ // record lands only while the row still holds them; a serve that moved only the ref and version
7579
+ // meanwhile is recorded over, as before. A refusal is not retried: the script already runs this
7580
+ // upload. When a migration rode it, the runtime's tag is past the record's and a retry cannot land.
7581
+ const migrated = !serving || addedClasses;
7491
7582
  await admin.setVerticalServing(actor, slug, {
7492
7583
  ref,
7493
7584
  versionId,
@@ -7500,6 +7591,8 @@ export function createControlPlaneApi(options) {
7500
7591
  ? nextMigrationTag(serving.migrationTag)
7501
7592
  : serving.migrationTag
7502
7593
  : 'v1',
7594
+ }, serving).catch((e) => {
7595
+ throw migrated && errorCodeOf(e) === 'precondition_failed' ? new ServeRecordBehindScript(slug) : e;
7503
7596
  });
7504
7597
  };
7505
7598
  /**
@@ -7811,7 +7904,11 @@ export function createControlPlaneApi(options) {
7811
7904
  // The serve, the adopt and the backfill reach every install of the vertical, whoever's, so
7812
7905
  // what failed is withheld from a confined caller (#2540), and the tenant the backstop would
7813
7906
  // have filed it under reads only the sentence.
7814
- const sentence = (reference) => `promoted, but the in-place serve failed — scopes still run the previous code; promote again to retry (reference ${reference})`;
7907
+ // #2577: a serve refused after its upload already runs the new code, so neither says which code runs.
7908
+ const sentence = (reference) => e instanceof ServeRecordBehindScript
7909
+ ? `promoted, but the in-place serve failed — another serve changed the serving record while this one ` +
7910
+ `added a Durable Object class, so promoting again cannot land until the record is repaired (reference ${reference})`
7911
+ : `promoted, but the in-place serve failed — promote again to retry (reference ${reference})`;
7815
7912
  const withheld = withheldFailure(c, {
7816
7913
  operation: 'promote.serve',
7817
7914
  stage: 'serve-in-place',
@@ -7906,6 +8003,10 @@ export function createControlPlaneApi(options) {
7906
8003
  const ownerGrants = pushedOwnerGrantProblems(manifest);
7907
8004
  if (ownerGrants.length > 0)
7908
8005
  return refuse(c, 'validation_failed', `manifest ownerGrants: ${ownerGrants.join('; ')}`);
8006
+ // vertical-connectors.md §7.1: a connector's standing grant is a declared, person-held key.
8007
+ const connectorProblems = pushedConnectorProblems(manifest);
8008
+ if (connectorProblems.length > 0)
8009
+ return refuse(c, 'validation_failed', `manifest connectors: ${connectorProblems.join('; ')}`);
7909
8010
  // §4 sandbox contract, before anything reaches the namespace.
7910
8011
  assertSandboxContract(manifest, options.storeDrivers);
7911
8012
  // #2339: a push whose migration digest is not the one its migrations give is refused here,
@@ -8050,6 +8151,17 @@ export function createControlPlaneApi(options) {
8050
8151
  if (!modules.some((m) => m.name === manifest.entry)) {
8051
8152
  return refuse(c, 'validation_failed', `entry module '${manifest.entry}' is not among the uploaded files`);
8052
8153
  }
8154
+ // #2388: a new version declares only keys its vertical may (its own, the engines', and those of
8155
+ // the verticals it provisions). The capability is granted after a first push, so the declaration
8156
+ // is judged here and honoured by the grant paths only for a tenant-provisioner. Earlier versions
8157
+ // are not re-judged.
8158
+ // Declaring `provisions` marks a manager: the capability itself is granted after a first push.
8159
+ const foreignKey = entitlementNamespaceRefusal(slug, manifest.entitlements ?? [], manifest.provisions?.length ? await verticalSlugsOf(c.get('actor')) : undefined);
8160
+ if (foreignKey)
8161
+ return refuseStatus(c, 422, foreignKey);
8162
+ const claimed = await slugClaimsSku(c.get('actor'), slug);
8163
+ if (claimed)
8164
+ return refuseStatus(c, 422, claimed);
8053
8165
  // The static-asset half of the §4 sandbox contract (self-serve-deploy.md §4.1). The
8054
8166
  // bytes are inert — no code, no authority — so they are ACCEPTED; the content-address
8055
8167
  // is not, so it is VERIFIED. The runtime's asset store dedups by hash across the whole
@@ -8179,6 +8291,9 @@ export function createControlPlaneApi(options) {
8179
8291
  ...(manifest.entitlements ? { entitlements: manifest.entitlements } : {}),
8180
8292
  ...(manifest.provides ? { provides: manifest.provides } : {}),
8181
8293
  ...(manifest.requires ? { requires: manifest.requires } : {}),
8294
+ // The declared vertical-defined connectors (vertical-connectors.md §7.2): what the dashboard's
8295
+ // door renders. The proxy never reads this copy; it reads the running version's manifest.
8296
+ ...(manifest.connectors ? { connectors: manifest.connectors } : {}),
8182
8297
  // The declared provisioner intent (#455) — a REQUEST the console reviews; the
8183
8298
  // tenant-provisioner grant itself is never touched by a push. Refreshes on every
8184
8299
  // re-push like the rest of the install spec, exactly because it grants nothing.
@@ -9271,6 +9386,47 @@ export function createControlPlaneApi(options) {
9271
9386
  const target = (await options.resolveVerticalVersion?.(slug, opts.versionId, actor)) ??
9272
9387
  options.verticals?.[slug] ??
9273
9388
  (await options.resolveVertical?.(slug, actor));
9389
+ // Both the source and pushed versions must opt in to carrying an ordinary setting. A
9390
+ // missing or invalid source declaration allows none. The parent deployment reads only
9391
+ // these keys; the control plane never copies its whole config store.
9392
+ const carrySettings = async (previewId, parent) => {
9393
+ if (!parent)
9394
+ return;
9395
+ const eligibleKeys = (json) => {
9396
+ if (!json)
9397
+ return new Set();
9398
+ try {
9399
+ const declared = JSON.parse(json).envSpec;
9400
+ const specs = z.array(envVarSpec).safeParse(declared);
9401
+ if (!specs.success)
9402
+ return new Set();
9403
+ return new Set(specs.data.filter((spec) => spec.previewCarry && !spec.secret && spec.kind !== 'model').map((spec) => spec.key));
9404
+ }
9405
+ catch {
9406
+ return new Set();
9407
+ }
9408
+ };
9409
+ const sourceJson = parent.verticalVersionId
9410
+ ? await c.var.admin.versionManifest(actor, slug, parent.verticalVersionId)
9411
+ : null;
9412
+ const sourceKeys = eligibleKeys(sourceJson);
9413
+ if (sourceKeys.size === 0)
9414
+ return;
9415
+ const incomingJson = await c.var.admin.versionManifest(actor, slug, opts.versionId);
9416
+ const keys = [...eligibleKeys(incomingJson)].filter((key) => sourceKeys.has(key));
9417
+ if (keys.length === 0)
9418
+ return;
9419
+ const sourceClient = await verticalForScope(c, parent);
9420
+ if (!sourceClient || !target) {
9421
+ throw new ControlPlaneError(501, 'the parent or preview deployment cannot deliver declared preview settings');
9422
+ }
9423
+ const requested = new Set(keys);
9424
+ const values = await retryTransient(() => sourceClient.readPreviewConfig({ tenantId, scopeId: parent.id, keys }));
9425
+ const entries = values.filter((entry) => requested.has(entry.key));
9426
+ if (entries.length > 0) {
9427
+ await retryTransient(() => target.configureInstance({ tenantId, scopeId: previewId, entries }));
9428
+ }
9429
+ };
9274
9430
  // The preview's own login (#1704), once it serves this version. Delivered into `target` —
9275
9431
  // THIS push's version deployment — and never "whatever the scope is bound to now", so an
9276
9432
  // older push can never overwrite a newer push's config store. A failure lands an
@@ -9312,8 +9468,8 @@ export function createControlPlaneApi(options) {
9312
9468
  };
9313
9469
  const notesFor = (parent) => parent
9314
9470
  ? [
9315
- "Settings: a preview does not carry over the app's per-install settings (the dashboard's Env tab); " +
9316
- "it runs on the deployment's defaults.",
9471
+ "Settings: only non-secret Env tab settings explicitly declared for preview carryover are delivered; " +
9472
+ "other settings use the deployment's defaults.",
9317
9473
  ]
9318
9474
  : [];
9319
9475
  // The `--<tag>` URL's base — the source's canonical hostname (fork) or the tenant-app
@@ -9400,6 +9556,7 @@ export function createControlPlaneApi(options) {
9400
9556
  const servedFork = async (p) => {
9401
9557
  const hostname = await bindPreviewHostname(c.var.admin, actor, baseHostname, tenantId, p.previewId, opts.tag, surface);
9402
9558
  await assertServesBoundVersion(p.previewId);
9559
+ await carrySettings(p.previewId, p.parent);
9403
9560
  const auth = await wireLogin(p.previewId, hostname, p.parent);
9404
9561
  return {
9405
9562
  scopeId: p.previewId,
@@ -9679,6 +9836,7 @@ export function createControlPlaneApi(options) {
9679
9836
  const parent = existing.forkedFrom
9680
9837
  ? ((await c.var.admin.getScopeRecord(actor, tenantId, existing.forkedFrom)) ?? null)
9681
9838
  : null;
9839
+ await carrySettings(existing.id, parent);
9682
9840
  const auth = await wireLogin(existing.id, hostname, parent);
9683
9841
  return {
9684
9842
  scopeId: existing.id,