@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 +18 -2
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +178 -20
- package/dist/api.js.map +1 -1
- package/dist/connection-binding-options.d.ts.map +1 -1
- package/dist/connection-binding-options.js +5 -1
- package/dist/connection-binding-options.js.map +1 -1
- package/dist/connection-relay.d.ts +26 -2
- package/dist/connection-relay.d.ts.map +1 -1
- package/dist/connection-relay.js +17 -4
- package/dist/connection-relay.js.map +1 -1
- package/dist/deploy.d.ts +1 -1
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +1 -1
- package/dist/deploy.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/platform-drain.d.ts +6 -0
- package/dist/platform-drain.d.ts.map +1 -1
- package/dist/platform-drain.js +9 -6
- package/dist/platform-drain.js.map +1 -1
- package/dist/platform-entry.generated.d.ts +1 -1
- package/dist/platform-entry.generated.d.ts.map +1 -1
- package/dist/platform-entry.generated.js +2 -2
- package/dist/platform-entry.generated.js.map +1 -1
- package/dist/vertical-client.d.ts +10 -0
- package/dist/vertical-client.d.ts.map +1 -1
- package/dist/vertical-client.js +5 -0
- package/dist/vertical-client.js.map +1 -1
- package/dist/vertical-connectors.d.ts +102 -0
- package/dist/vertical-connectors.d.ts.map +1 -0
- package/dist/vertical-connectors.js +428 -0
- package/dist/vertical-connectors.js.map +1 -0
- package/package.json +8 -8
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":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AAE9G,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAsBL,KAAK,qBAAqB,EAqF1B,CAAC,
|
|
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 }, {
|
|
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) =>
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
9316
|
-
"
|
|
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,
|