@substrat-run/control-plane-api 0.153.0 → 0.155.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 +8 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +128 -27
- package/dist/api.js.map +1 -1
- package/dist/connection-binding-options.d.ts +13 -0
- package/dist/connection-binding-options.d.ts.map +1 -0
- package/dist/connection-binding-options.js +35 -0
- package/dist/connection-binding-options.js.map +1 -0
- package/dist/custom-hostnames.d.ts +11 -0
- package/dist/custom-hostnames.d.ts.map +1 -1
- package/dist/custom-hostnames.js +9 -1
- package/dist/custom-hostnames.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 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/platform-drain.d.ts +8 -0
- package/dist/platform-drain.d.ts.map +1 -1
- package/dist/platform-drain.js +2 -1
- 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/social-relay.d.ts +115 -0
- package/dist/social-relay.d.ts.map +1 -0
- package/dist/social-relay.js +118 -0
- package/dist/social-relay.js.map +1 -0
- package/dist/vertical-client.d.ts +65 -2
- package/dist/vertical-client.d.ts.map +1 -1
- package/dist/vertical-client.js +147 -5
- package/dist/vertical-client.js.map +1 -1
- package/package.json +8 -6
package/dist/api.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { type DoNamespaceReader } from './do-namespaces.js';
|
|
|
17
17
|
import type { DirectoryBackupStore, ScopeBackupStore } from './backups.js';
|
|
18
18
|
import { type LakeQueryEngine } from './lake-gateway.js';
|
|
19
19
|
import { type CustomHostnameProvisioner } from './custom-hostnames.js';
|
|
20
|
+
import { type SocialRelayOptions } from './social-relay.js';
|
|
20
21
|
/**
|
|
21
22
|
* What one provider can answer about a live connection (#605) — the seam behind
|
|
22
23
|
* `POST /tenants/:t/connections/:id/verify` and `GET …/activity`.
|
|
@@ -372,6 +373,13 @@ export interface ControlPlaneApiOptions {
|
|
|
372
373
|
* self-host / dev shape, where there is no CF-for-SaaS zone).
|
|
373
374
|
*/
|
|
374
375
|
provisionHostname?: CustomHostnameProvisioner;
|
|
376
|
+
/**
|
|
377
|
+
* The platform's social relay (#1544, `social-relay.ts`): how an Auth Server install is given
|
|
378
|
+
* its own client there. When present, a hostname of such an install becoming active
|
|
379
|
+
* reconciles that install, and the two staff routes under `social-relay` can reconcile one
|
|
380
|
+
* install or all of them. Absent ⇒ nothing here ever calls the relay.
|
|
381
|
+
*/
|
|
382
|
+
socialRelay?: SocialRelayOptions;
|
|
375
383
|
/**
|
|
376
384
|
* The platform's base domains — the wildcard-covered zones a PLATFORM hostname is
|
|
377
385
|
* minted under (`substrat.run`, `global.substrat.run`, …). A bind AT or UNDER one of
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/api.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { PREVIEW_CONNECTIONS_REFUSAL } from './connection-relay.js';
|
|
2
|
+
import { connectionBindingOptions } from './connection-binding-options.js';
|
|
2
3
|
import { resolveObservabilityWindow } from './observability-window.js';
|
|
3
4
|
import { Hono } from 'hono';
|
|
4
|
-
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, 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, 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, } 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, 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, 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
6
|
import { SCOPE_COPY_LEASE_MS, copyRestoreFence, publishedMigrationDigest, replayStoreErasures } from '@substrat-run/kernel';
|
|
6
7
|
import { attributeFailure } from './failure-attribution.js';
|
|
7
8
|
import { auditedChange, withAuditedOutcomes } from './audited-change.js';
|
|
8
9
|
import { BIND_EXPORT_BREAK_REFUSAL, CARRIED_AWAY_KEY, bindExportBreakRefusal, carriedAwayDump, dumpMetaValue, metaValueIn, crossVerticalHealth, exportBreakRefusal, isBindExportBreakRefusal, isExportBreakRefusal, isPrimaryScope, promoteReviewDigest, rebindReviewDigest, assertRebindReviewCurrent, migrationProgress, ulid, auditWarningOf, } from '@substrat-run/kernel';
|
|
9
10
|
import { TENANT_HEADER, confinedTenant } from './auth.js';
|
|
10
11
|
import { mintTenantToken } from './tenant-token.js';
|
|
11
|
-
import { connectionGrantsForScope } from './vertical-client.js';
|
|
12
|
+
import { connectionGrantsForScope, transferScope } from './vertical-client.js';
|
|
12
13
|
import { runningVersionOf, runningVersionStoresOf, scopeStoreReachOf, scopeStoresOf, unrouteScope } from './platform-scope-stores.js';
|
|
13
14
|
import { STORE_REBUILD_REQUEST_SUBREQUESTS, runStoreFleetRebuild, storeRebuildBudget, storeRebuildStatus } from './store-rebuild.js';
|
|
14
15
|
import { PUSH_HOLD_TTL_MS, runStoreDriverRollout, storeDriverRelease, storedReleaseLoader } from './store-releases.js';
|
|
@@ -25,7 +26,7 @@ import { NON_PROBLEM_REFUSAL, coded, mapError, relayed } from './errors.js';
|
|
|
25
26
|
import { maskDump, maskRecords } from './mask.js';
|
|
26
27
|
import { createPseudonymizer } from './pseudonymize.js';
|
|
27
28
|
import { openDump, sealDump } from './seal.js';
|
|
28
|
-
import { assertSandboxContract, verticalUploadBindings, migrationsDigestBindings, deployManifest, storedDeployManifest, pushedPermissionRegistry, deploymentRefFor, stableDeploymentRefFor, nextMigrationTag, upstreamStatusOf, } from './deploy.js';
|
|
29
|
+
import { assertSandboxContract, verticalUploadBindings, migrationsDigestBindings, deployManifest, storedDeployManifest, pushedOwnerGrantProblems, pushedPermissionRegistry, deploymentRefFor, stableDeploymentRefFor, nextMigrationTag, upstreamStatusOf, } from './deploy.js';
|
|
29
30
|
/**
|
|
30
31
|
* Answer with a problem document (#113 phase 4).
|
|
31
32
|
*
|
|
@@ -68,6 +69,7 @@ import { STORAGE_PAGE_DEFAULT, STORAGE_PAGE_MAX, readStoragePage } from './stora
|
|
|
68
69
|
import { queryLakeEvents } from './lake-gateway.js';
|
|
69
70
|
import { LakeQueryError } from './r2-sql-engine.js';
|
|
70
71
|
import { isCustomHostname, validateBindableHostname, } from './custom-hostnames.js';
|
|
72
|
+
import { isSocialRelayVertical, reconcileScopeSocialRelay, relayHostnames, } from './social-relay.js';
|
|
71
73
|
/**
|
|
72
74
|
* Response headers carrying the CLI version advisory (`ControlPlaneApiOptions.cliAdvisory`).
|
|
73
75
|
* The CLI reads these by the same spelling (`packages/cli/src/version.ts`); exported so a
|
|
@@ -2038,23 +2040,10 @@ export function createControlPlaneApi(options) {
|
|
|
2038
2040
|
if (target.status !== 'active' || !target.vertical || target.vertical !== row.vertical) {
|
|
2039
2041
|
return refuseStatus(c, 409, 'connection and target must share an active vertical scope');
|
|
2040
2042
|
}
|
|
2041
|
-
const
|
|
2043
|
+
const schema = connectionBindingOptions(row.provider).extend({
|
|
2042
2044
|
operation: z.string().regex(/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_-]+$/),
|
|
2043
2045
|
permission: permissionKey,
|
|
2044
|
-
};
|
|
2045
|
-
const schema = row.provider === 'planima'
|
|
2046
|
-
? z.object({ ...fields,
|
|
2047
|
-
organizationId: z.number().int().positive().nullable().optional(),
|
|
2048
|
-
currency: z.string().regex(/^[A-Z]{3}$/).optional(),
|
|
2049
|
-
window: z.object({ fromYear: z.number().int(), toYear: z.number().int() })
|
|
2050
|
-
.refine((w) => w.toYear >= w.fromYear, 'window ends before it starts').nullable().optional(),
|
|
2051
|
-
horizonYears: z.number().int().min(0).optional(),
|
|
2052
|
-
}).strict()
|
|
2053
|
-
: row.provider === 'microsoft365'
|
|
2054
|
-
// #2101: the channel folder, by its drive path. The connector resolves it and holds the
|
|
2055
|
-
// fence to it; this only bounds the input.
|
|
2056
|
-
? z.object({ ...fields, folder: z.string().trim().min(1).max(400) }).strict()
|
|
2057
|
-
: z.object(fields).strict();
|
|
2046
|
+
});
|
|
2058
2047
|
const input = schema.parse(await c.req.json());
|
|
2059
2048
|
const grants = await c.var.host.connectionGrantsInScope(row.tenantId, scopeId);
|
|
2060
2049
|
if (!grants.some((g) => g.connectionId === row.id && g.permission === input.permission)) {
|
|
@@ -2276,6 +2265,48 @@ export function createControlPlaneApi(options) {
|
|
|
2276
2265
|
open: (items) => c.var.admin.openSubjectPayloads(c.get('actor'), tenantId, scopeId, items),
|
|
2277
2266
|
});
|
|
2278
2267
|
const verticalForScope = async (c, scope) => (await deploymentForScope(c, scope))?.client;
|
|
2268
|
+
/**
|
|
2269
|
+
* Reconcile one install's client at the social relay (#1544). Skips — never throws for — an
|
|
2270
|
+
* install the relay is not for: not configured here, not an Auth Server, or no deployment to
|
|
2271
|
+
* deliver to. A relay or delivery failure DOES throw, so a staff route can report it.
|
|
2272
|
+
*/
|
|
2273
|
+
const reconcileRelayFor = async (c, tenantId, scopeId) => {
|
|
2274
|
+
if (!options.socialRelay)
|
|
2275
|
+
return { status: 'skipped', reason: 'no social relay is configured on this control plane' };
|
|
2276
|
+
const actor = c.get('actor');
|
|
2277
|
+
return reconcileScopeSocialRelay({
|
|
2278
|
+
relay: options.socialRelay,
|
|
2279
|
+
getScope: (t, s) => c.var.admin.getScopeRecord(actor, tenantIdSchema.parse(t), scopeIdSchema.parse(s)),
|
|
2280
|
+
activeHostnames: async (s) => relayHostnames(await c.var.admin.listHostnames(actor, { scopeId: scopeIdSchema.parse(s), status: 'active' }), {
|
|
2281
|
+
isCustom: custom,
|
|
2282
|
+
provisioner: options.provisionHostname,
|
|
2283
|
+
}),
|
|
2284
|
+
deploymentOf: (scope) => verticalForScope(c, scope),
|
|
2285
|
+
}, tenantId, scopeId);
|
|
2286
|
+
};
|
|
2287
|
+
/**
|
|
2288
|
+
* After a hostname of an Auth Server install became active: register it at the relay. Best
|
|
2289
|
+
* effort, and awaited rather than deferred — the route that activated the hostname has done its
|
|
2290
|
+
* job either way, and a failure here is logged and repaired by the next trigger or a staff run.
|
|
2291
|
+
* Awaited so two activations of one install never reconcile concurrently.
|
|
2292
|
+
*/
|
|
2293
|
+
const relayAfterActivation = async (c, before, after) => {
|
|
2294
|
+
if (!options.socialRelay || !after || after.status !== 'active' || before?.status === 'active')
|
|
2295
|
+
return;
|
|
2296
|
+
if (!isSocialRelayVertical(after.verticalSlug))
|
|
2297
|
+
return;
|
|
2298
|
+
try {
|
|
2299
|
+
const outcome = await reconcileRelayFor(c, after.tenantId, after.scopeId);
|
|
2300
|
+
console.log('social-relay.reconciled', { scopeId: after.scopeId, trigger: 'hostname-active', ...outcome });
|
|
2301
|
+
}
|
|
2302
|
+
catch (e) {
|
|
2303
|
+
console.error('social-relay.reconcile.failed', {
|
|
2304
|
+
scopeId: after.scopeId,
|
|
2305
|
+
trigger: 'hostname-active',
|
|
2306
|
+
detail: e instanceof Error ? e.message : String(e),
|
|
2307
|
+
});
|
|
2308
|
+
}
|
|
2309
|
+
};
|
|
2279
2310
|
/**
|
|
2280
2311
|
* `verticalForScope`, saying WHICH rung of the ladder chose the client (#1653).
|
|
2281
2312
|
*
|
|
@@ -2664,7 +2695,8 @@ export function createControlPlaneApi(options) {
|
|
|
2664
2695
|
await c.var.admin.recordScopeScriptCopy(actor, tenantId, scopeId, servingRef, moveId, { role: 'destination', loadStamp: restoredStamp, leaseMs: copyLeaseMs });
|
|
2665
2696
|
}
|
|
2666
2697
|
const erasureEpoch = await c.var.admin.scopeErasureEpoch(actor, tenantId, scopeId);
|
|
2667
|
-
|
|
2698
|
+
// #2450: a page at a time into the destination's stage, so a scope of any size moves.
|
|
2699
|
+
const { dump, loadStamp: sourceStamp, revision: sourceRevision } = await transferScope(source, dest, { from: scopeId }, { retry: retryTransient });
|
|
2668
2700
|
const restored = await restoreCarryingSwitches(actor, dest, tenantId, scopeId, dump, {
|
|
2669
2701
|
scopeId, exact: true, loadStamp: restoredStamp,
|
|
2670
2702
|
...(moveId ? { fence: copyRestoreFence(moveId, recordedAt, copyLeaseMs, erasureEpoch) } : {}),
|
|
@@ -2801,7 +2833,10 @@ export function createControlPlaneApi(options) {
|
|
|
2801
2833
|
// cleanup. Name it before any bytes are restored so reap and erasure can find it.
|
|
2802
2834
|
await c.var.admin.recordScopeScriptCopy(actor, scope.tenantId, scope.id, to, moveId, { role: 'destination', loadStamp: restoredStamp, leaseMs: copyLeaseMs });
|
|
2803
2835
|
const erasureEpoch = await c.var.admin.scopeErasureEpoch(actor, scope.tenantId, scope.id);
|
|
2804
|
-
|
|
2836
|
+
// #2450: a page at a time into the destination's stage, so a scope of any size moves. The
|
|
2837
|
+
// stage moves neither the destination's load stamp nor its write revision, so the marker read
|
|
2838
|
+
// next still describes the store the restore below commits into.
|
|
2839
|
+
const { dump, loadStamp: sourceStamp, revision: sourceRevision, meta } = await transferScope(source, dest, { from: scope.id }, { retry: retryTransient });
|
|
2805
2840
|
// What the destination holds now, read before the binding is checked again. The restore
|
|
2806
2841
|
// sends it back and is refused if the store moved since: another carry into the same script
|
|
2807
2842
|
// loaded it, or it went live under another carry's bind and took a write. That is a CI retry
|
|
@@ -2814,7 +2849,7 @@ export function createControlPlaneApi(options) {
|
|
|
2814
2849
|
throw new ControlPlaneError(412, `scope ${scope.id} was re-pointed while its data was being copied out of '${from}', so nothing was ` +
|
|
2815
2850
|
`moved or bound (#1722). Reload the scope and retry.`);
|
|
2816
2851
|
}
|
|
2817
|
-
if (dumpMetaValue(
|
|
2852
|
+
if (dumpMetaValue(meta, CARRIED_AWAY_KEY) !== null) {
|
|
2818
2853
|
throw new ControlPlaneError(412, `the copy of scope ${scope.id} in '${from}' was already carried to another script and wiped, so nothing ` +
|
|
2819
2854
|
`was moved or bound (#1722). Reload the scope and retry.`);
|
|
2820
2855
|
}
|
|
@@ -3186,8 +3221,8 @@ export function createControlPlaneApi(options) {
|
|
|
3186
3221
|
await releaseIfLive(c, scope, versionId, carried, carried.dest, carried.to);
|
|
3187
3222
|
let sourceMarker = { loadStamp: carried.sourceStamp, revision: carried.sourceRevision };
|
|
3188
3223
|
if (await isCarriedAway(carried.dest, scope)) {
|
|
3189
|
-
const {
|
|
3190
|
-
if (dumpMetaValue(
|
|
3224
|
+
const { dump, loadStamp: stamp, revision, meta } = await transferScope(carried.source, carried.dest, { from: scope.id }, { retry: retryTransient });
|
|
3225
|
+
if (dumpMetaValue(meta, CARRIED_AWAY_KEY) !== null) {
|
|
3191
3226
|
throw new ControlPlaneError(500, `both copies of scope ${scope.id} hold the carried_away tombstone; nothing was wiped`);
|
|
3192
3227
|
}
|
|
3193
3228
|
// The re-carry is a copy move of its own (#1722): in the ledger and leased before it
|
|
@@ -6364,6 +6399,10 @@ export function createControlPlaneApi(options) {
|
|
|
6364
6399
|
if (!registry.success)
|
|
6365
6400
|
return refuseManifest(registry.error.issues[0], ['registry']);
|
|
6366
6401
|
}
|
|
6402
|
+
// #2430: an owner grant is a person's, so it holds no key the registry reserves.
|
|
6403
|
+
const ownerGrants = pushedOwnerGrantProblems(checked.data);
|
|
6404
|
+
if (ownerGrants.length > 0)
|
|
6405
|
+
return refuseManifest({ path: ['ownerGrants'], message: ownerGrants.join('; ') });
|
|
6367
6406
|
}
|
|
6368
6407
|
await c.var.admin.publishVersion(c.get('actor'), input);
|
|
6369
6408
|
const version = await c.var.admin.getVersion(c.get('actor'), input.id, slug);
|
|
@@ -7066,6 +7105,10 @@ export function createControlPlaneApi(options) {
|
|
|
7066
7105
|
if (typeof raw !== 'string')
|
|
7067
7106
|
return refuse(c, 'validation_failed', 'missing manifest part');
|
|
7068
7107
|
const manifest = deployManifest.parse(JSON.parse(raw));
|
|
7108
|
+
// #2430: an owner grant is a person's, so it holds no key the registry reserves.
|
|
7109
|
+
const ownerGrants = pushedOwnerGrantProblems(manifest);
|
|
7110
|
+
if (ownerGrants.length > 0)
|
|
7111
|
+
return refuse(c, 'validation_failed', `manifest ownerGrants: ${ownerGrants.join('; ')}`);
|
|
7069
7112
|
// §4 sandbox contract, before anything reaches the namespace.
|
|
7070
7113
|
assertSandboxContract(manifest, options.storeDrivers);
|
|
7071
7114
|
// #2339: a push whose migration digest is not the one its migrations give is refused here,
|
|
@@ -8042,8 +8085,52 @@ export function createControlPlaneApi(options) {
|
|
|
8042
8085
|
// Re-read so the response carries the post-issuance status + DNS records the caller
|
|
8043
8086
|
// (dashboard / CLI) renders — a custom bind comes back `verifying` with records.
|
|
8044
8087
|
const out = (await c.var.admin.listHostnames(c.get('actor'), { scopeId: input.scopeId })).find((h) => h.hostname === input.hostname);
|
|
8088
|
+
// An install's platform hostname binds active here, which is what makes this the
|
|
8089
|
+
// install-time trigger for an Auth Server's relay client (#1544).
|
|
8090
|
+
await relayAfterActivation(c, undefined, out);
|
|
8045
8091
|
return c.json(out ?? bound, 201);
|
|
8046
8092
|
});
|
|
8093
|
+
// -- platform-provided social sign-in (#1544) --------------------------------
|
|
8094
|
+
// Staff only: on neither BUILDER_ROUTES nor TENANT_ROUTES, so both default-deny them. The
|
|
8095
|
+
// automatic trigger is a hostname becoming active (`relayAfterActivation`); these are the
|
|
8096
|
+
// levers for everything else — one install, or every Auth Server install after the relay
|
|
8097
|
+
// gained or lost a provider.
|
|
8098
|
+
app.post('/tenants/:tenantId/scopes/:scopeId/social-relay', async (c) => {
|
|
8099
|
+
if (!options.socialRelay)
|
|
8100
|
+
return refuseStatus(c, 501, 'no social relay is configured on this control plane');
|
|
8101
|
+
const tenantId = tenantIdSchema.parse(c.req.param('tenantId'));
|
|
8102
|
+
const scopeId = scopeIdSchema.parse(c.req.param('scopeId'));
|
|
8103
|
+
return c.json(await reconcileRelayFor(c, tenantId, scopeId));
|
|
8104
|
+
});
|
|
8105
|
+
app.post('/social-relay/reconcile', async (c) => {
|
|
8106
|
+
if (!options.socialRelay)
|
|
8107
|
+
return refuseStatus(c, 501, 'no social relay is configured on this control plane');
|
|
8108
|
+
const results = [];
|
|
8109
|
+
for (const vertical of SOCIAL_RELAY_VERTICALS) {
|
|
8110
|
+
const scopes = await c.var.admin.listScopes(c.get('actor'), { vertical, status: ['active'] });
|
|
8111
|
+
// One install at a time: each is a relay call and a delivery, and a fleet in parallel
|
|
8112
|
+
// would be a burst at the relay's single Durable Object for no gain.
|
|
8113
|
+
for (const scope of scopes) {
|
|
8114
|
+
try {
|
|
8115
|
+
results.push({ tenantId: scope.tenantId, scopeId: scope.id, ...(await reconcileRelayFor(c, scope.tenantId, scope.id)) });
|
|
8116
|
+
}
|
|
8117
|
+
catch (e) {
|
|
8118
|
+
results.push({
|
|
8119
|
+
tenantId: scope.tenantId,
|
|
8120
|
+
scopeId: scope.id,
|
|
8121
|
+
status: 'failed',
|
|
8122
|
+
reason: e instanceof Error ? e.message : String(e),
|
|
8123
|
+
});
|
|
8124
|
+
}
|
|
8125
|
+
}
|
|
8126
|
+
}
|
|
8127
|
+
return c.json({
|
|
8128
|
+
delivered: results.filter((r) => r.status === 'delivered').length,
|
|
8129
|
+
skipped: results.filter((r) => r.status === 'skipped').length,
|
|
8130
|
+
failed: results.filter((r) => r.status === 'failed').length,
|
|
8131
|
+
results,
|
|
8132
|
+
});
|
|
8133
|
+
});
|
|
8047
8134
|
// Re-poll a custom hostname's issuance ("check again" in the dashboard). Tenant-narrowed
|
|
8048
8135
|
// like the other hostname routes; a platform mint or an already-active row is a no-op
|
|
8049
8136
|
// re-affirm. Idempotent and safe to hammer — it just reflects Cloudflare's current state.
|
|
@@ -8056,7 +8143,9 @@ export function createControlPlaneApi(options) {
|
|
|
8056
8143
|
if (!row)
|
|
8057
8144
|
return refuse(c, 'not_found', `unknown hostname: ${name.toLowerCase()}`);
|
|
8058
8145
|
await runIssuance(c.var.admin, c.get('actor'), row);
|
|
8059
|
-
|
|
8146
|
+
const after = await hostnameRow(c, name);
|
|
8147
|
+
await relayAfterActivation(c, row, after);
|
|
8148
|
+
return c.json(after);
|
|
8060
8149
|
});
|
|
8061
8150
|
app.patch('/hostnames/:hostname/status', async (c) => {
|
|
8062
8151
|
const { status, note } = setHostnameStatusBody.parse(await c.req.json());
|
|
@@ -8066,8 +8155,10 @@ export function createControlPlaneApi(options) {
|
|
|
8066
8155
|
if (confinedTenant(c.get('principal')) !== null && !(await tenantHostname(c, name))) {
|
|
8067
8156
|
return refuse(c, 'not_found', `unknown hostname: ${name.toLowerCase()}`);
|
|
8068
8157
|
}
|
|
8158
|
+
const before = await hostnameRow(c, name);
|
|
8069
8159
|
await c.var.admin.setHostnameStatus(c.get('actor'), name, status, note);
|
|
8070
8160
|
const row = (await c.var.admin.listHostnames(c.get('actor'), {})).find((h) => h.hostname === name.toLowerCase());
|
|
8161
|
+
await relayAfterActivation(c, before, row);
|
|
8071
8162
|
return c.json(row);
|
|
8072
8163
|
});
|
|
8073
8164
|
// Unbind (hard-delete) a hostname row — what the orphan cleanup uses on rows
|
|
@@ -8458,6 +8549,8 @@ export function createControlPlaneApi(options) {
|
|
|
8458
8549
|
let row;
|
|
8459
8550
|
/** `fenced`: the ledger stamp and lease of this fork's restore (#1722), when it has one. */
|
|
8460
8551
|
let restore;
|
|
8552
|
+
/** #2450: the fork's data, moved into the preview's copy once the ledger names it. */
|
|
8553
|
+
let forkData = null;
|
|
8461
8554
|
/** #2009: the directory's classification of the row below, which every restore of it carries. */
|
|
8462
8555
|
const markCopyOfRow = () => {
|
|
8463
8556
|
const lineage = copyLineageOf({ kind: row.kind ?? '', forkedFrom: row.forkedFrom ?? null });
|
|
@@ -8472,7 +8565,14 @@ export function createControlPlaneApi(options) {
|
|
|
8472
8565
|
throw new ControlPlaneError(501, 'preview needs dispatch resolution for the PR version');
|
|
8473
8566
|
}
|
|
8474
8567
|
const canonical = await c.var.admin.exportScope(actor, tenantId, source.id);
|
|
8475
|
-
|
|
8568
|
+
// #2450: from a vertical, the fork's data moves a page at a time into the preview's own stage
|
|
8569
|
+
// there, so a scope of any size forks. Unstamped, as the whole export was: a fork writes
|
|
8570
|
+
// nothing into its source. Moved once, after the row and the ledger name the preview's copy
|
|
8571
|
+
// (K-31: no copied data without a record) and ahead of the restore's own retries, so a
|
|
8572
|
+
// restore blip reloads the same stage and an export fault is not recorded as a restore's.
|
|
8573
|
+
forkData = sourceClient
|
|
8574
|
+
? async () => (await transferScope(sourceClient, target, { from: source.id, to: previewId }, { retry: retryTransient, stamp: false })).dump
|
|
8575
|
+
: async () => canonical.tables;
|
|
8476
8576
|
// Directory row FIRST as `provisioning` (K-31 two-phase): a crash before the data
|
|
8477
8577
|
// copy leaves an inert row that — carrying `forkedFrom` + `expiresAt` — the GC sweep
|
|
8478
8578
|
// reaps, never copied data with no record.
|
|
@@ -8495,7 +8595,7 @@ export function createControlPlaneApi(options) {
|
|
|
8495
8595
|
// pushes a fresh version per try) — #559 (2).
|
|
8496
8596
|
// #2009: a load classifies nothing by itself, so the restore carries the directory's
|
|
8497
8597
|
// classification of the row below, as every restore onto a non-primary scope does.
|
|
8498
|
-
restore = (fenced) => target.restoreScope(tenantId, previewId,
|
|
8598
|
+
restore = (fenced, dump) => target.restoreScope(tenantId, previewId, dump ?? [], { sourceScopeId: source.id, exact: true, ...markCopyOfRow(), ...fenced });
|
|
8499
8599
|
}
|
|
8500
8600
|
else {
|
|
8501
8601
|
// A clean-room preview (#509 (b)): an EMPTY scope, no source to export. No `forkedFrom`
|
|
@@ -8541,7 +8641,8 @@ export function createControlPlaneApi(options) {
|
|
|
8541
8641
|
fenced = { loadStamp: restoredStamp, fence: copyRestoreFence(moveId, recordedAt, copyLeaseMs, erasureEpoch) };
|
|
8542
8642
|
}
|
|
8543
8643
|
const load = restore;
|
|
8544
|
-
await
|
|
8644
|
+
const dump = forkData ? await forkData() : undefined;
|
|
8645
|
+
await restoreOrRecord(() => load(fenced, dump));
|
|
8545
8646
|
}
|
|
8546
8647
|
await c.var.admin.activateScope(actor, tenantId, previewId);
|
|
8547
8648
|
// Bind the PR version. A private vertical's push self-admitted, so this is accepted; a
|