@substrat-run/control-plane-api 0.57.0 → 0.58.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 +1 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +54 -7
- package/dist/api.js.map +1 -1
- package/dist/connection-relay.d.ts +32 -0
- package/dist/connection-relay.d.ts.map +1 -0
- package/dist/connection-relay.js +100 -0
- package/dist/connection-relay.js.map +1 -0
- package/dist/index.d.ts +1 -0
- 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 +1 -1
- package/dist/platform-drain.d.ts.map +1 -1
- package/dist/platform-drain.js +5 -1
- package/dist/platform-drain.js.map +1 -1
- package/dist/vertical-client.d.ts +27 -1
- package/dist/vertical-client.d.ts.map +1 -1
- package/dist/vertical-client.js +20 -0
- package/dist/vertical-client.js.map +1 -1
- package/package.json +6 -6
package/dist/api.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Hono } from 'hono';
|
|
|
2
2
|
import type { PlatformActorId } from '@substrat-run/contracts';
|
|
3
3
|
import type { ScopeHost } from '@substrat-run/kernel';
|
|
4
4
|
import type { PlatformActorAuth, BuilderAuth, Principal } from './auth.js';
|
|
5
|
-
import type
|
|
5
|
+
import { type VerticalClient } from './vertical-client.js';
|
|
6
6
|
import type { DeployVerticalFn, FetchVerticalAssetFn, FetchVerticalModulesFn } from './deploy.js';
|
|
7
7
|
import type { PatchScriptBindingsFn } from './wfp.js';
|
|
8
8
|
import type { ObservabilityReader } from './observability.js';
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAiC5B,OAAO,KAAK,EAGV,eAAe,EAMhB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAmB,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3E,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/api.js
CHANGED
|
@@ -2,6 +2,7 @@ import { Hono } from 'hono';
|
|
|
2
2
|
import { adminAction, ASSET_PART_PREFIX, assetHash, channelName, createTenantInput, entitlementGrantInput, hostname as hostnameSchema, hostnameRegion, hostnameStatus, identityLink, listPageQuery, pageOf, principalId as principalIdSchema, promotionAcknowledgement, provisionableJurisdiction, publishVersionInput, queryScopeInput, readScopeTableInput, registerVerticalInput, scopeDump, dataSubjectId as dataSubjectIdSchema, scopeId as scopeIdSchema, scopeStatus, storageShape, surfaceName, tenantId as tenantIdSchema, tenantStatus, versionOrigin, z, } from '@substrat-run/contracts';
|
|
3
3
|
import { migrationProgress, ulid } from '@substrat-run/kernel';
|
|
4
4
|
import { TENANT_HEADER } from './auth.js';
|
|
5
|
+
import { connectionGrantsForScope } from './vertical-client.js';
|
|
5
6
|
import { ControlPlaneError } from './client.js';
|
|
6
7
|
import { provisionSiblingScope } from './platform-drain.js';
|
|
7
8
|
import { mapError } from './errors.js';
|
|
@@ -610,6 +611,12 @@ export function createControlPlaneApi(options) {
|
|
|
610
611
|
const tenantId = tenantIdSchema.parse(c.req.param('tenantId'));
|
|
611
612
|
return c.json(await admin.listIdentityLinks(c.get('actor'), tenantId));
|
|
612
613
|
});
|
|
614
|
+
// The tenant's live connection grants (#592) — the readable "what may this connection
|
|
615
|
+
// invoke" (connections.md §6.2.4 Q2), and the rows provision/reconcile deliver from.
|
|
616
|
+
app.get('/tenants/:tenantId/connection-grants', async (c) => {
|
|
617
|
+
const tenantId = tenantIdSchema.parse(c.req.param('tenantId'));
|
|
618
|
+
return c.json(await admin.listConnectionGrants(c.get('actor'), tenantId));
|
|
619
|
+
});
|
|
613
620
|
// -- the scope directory (§3.2/§4.2) ---------------------------------------
|
|
614
621
|
app.get('/scopes', async (c) => {
|
|
615
622
|
const p = c.get('principal');
|
|
@@ -1021,8 +1028,18 @@ export function createControlPlaneApi(options) {
|
|
|
1021
1028
|
// #406: re-gathered and re-delivered like entitlements, so a reconcile also repairs a
|
|
1022
1029
|
// dropped identity-link delivery — and is the channel a link/unlink after provision rides.
|
|
1023
1030
|
const identityLinks = (await admin.listIdentityLinks(actor, tenantId)).map(({ tenantId: _tenantId, ...link }) => link);
|
|
1031
|
+
// #592: connection grants ride the same authoritative gather — the back-fill for a
|
|
1032
|
+
// scope provisioned before `grantToConnection` ran, and how a revoked connection's
|
|
1033
|
+
// grants stop being delivered (they are absent from the directory's live rows).
|
|
1034
|
+
const connectionGrants = connectionGrantsForScope(await admin.listConnectionGrants(actor, tenantId), scope.vertical, scopeId);
|
|
1024
1035
|
try {
|
|
1025
|
-
const result = await vertical.reconcileInstance({
|
|
1036
|
+
const result = await vertical.reconcileInstance({
|
|
1037
|
+
tenantId,
|
|
1038
|
+
scopeId,
|
|
1039
|
+
entitlements,
|
|
1040
|
+
identityLinks,
|
|
1041
|
+
connectionGrants,
|
|
1042
|
+
});
|
|
1026
1043
|
return c.json(result);
|
|
1027
1044
|
}
|
|
1028
1045
|
catch (e) {
|
|
@@ -1180,6 +1197,30 @@ export function createControlPlaneApi(options) {
|
|
|
1180
1197
|
// the vertical must never even be asked to wipe a primary scope — and re-checked
|
|
1181
1198
|
// below the seam by deleteSnapshot, which also wipes the co-located storage,
|
|
1182
1199
|
// removes hostnames + the directory row, and writes the audit entry.
|
|
1200
|
+
/**
|
|
1201
|
+
* Wipe a scope's co-located bytes via the vertical's `/internal/delete-scope`,
|
|
1202
|
+
* tolerating the ONE refusal that must never pin a directory row forever: a script
|
|
1203
|
+
* that never implemented the verb (answers 501 — the standalone-app shape; the
|
|
1204
|
+
* retired auth-server lineage is the canonical case). Those bytes are unreachable
|
|
1205
|
+
* through every platform verb — export and delete alike — so they stay in the
|
|
1206
|
+
* script's own storage and die with the script at orphan cleanup (#248): the same
|
|
1207
|
+
* stranded-not-deleted posture as rebind's `abandonData`. Anything else (a real
|
|
1208
|
+
* 5xx, a timeout) still aborts — an implemented wipe that failed is a retry, not a
|
|
1209
|
+
* shrug. Returns true when the storage was stranded.
|
|
1210
|
+
*/
|
|
1211
|
+
const deleteScopeStorageOrStrand = async (vertical, scopeId) => {
|
|
1212
|
+
if (!vertical)
|
|
1213
|
+
return false;
|
|
1214
|
+
try {
|
|
1215
|
+
await vertical.deleteScope({ scopeId });
|
|
1216
|
+
return false;
|
|
1217
|
+
}
|
|
1218
|
+
catch (e) {
|
|
1219
|
+
if (e instanceof ControlPlaneError && e.status === 501)
|
|
1220
|
+
return true;
|
|
1221
|
+
throw e;
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1183
1224
|
app.delete('/tenants/:tenantId/scopes/:scopeId', async (c) => {
|
|
1184
1225
|
const tenantId = tenantIdSchema.parse(c.req.param('tenantId'));
|
|
1185
1226
|
const scopeId = scopeIdSchema.parse(c.req.param('scopeId'));
|
|
@@ -1196,10 +1237,9 @@ export function createControlPlaneApi(options) {
|
|
|
1196
1237
|
// storage-before-row ordering deleteSnapshot itself keeps, so a crash
|
|
1197
1238
|
// between the two converges on retry.
|
|
1198
1239
|
const vertical = await verticalForScope(c, scope);
|
|
1199
|
-
|
|
1200
|
-
await vertical.deleteScope({ scopeId });
|
|
1240
|
+
const storageStranded = await deleteScopeStorageOrStrand(vertical, scopeId);
|
|
1201
1241
|
await options.host.deleteSnapshot(actor, tenantId, scopeId);
|
|
1202
|
-
return c.json({ deleted: scopeId });
|
|
1242
|
+
return c.json({ deleted: scopeId, ...(storageStranded ? { storageStranded: true } : {}) });
|
|
1203
1243
|
}
|
|
1204
1244
|
catch (e) {
|
|
1205
1245
|
if (e instanceof ControlPlaneError) {
|
|
@@ -1457,13 +1497,14 @@ export function createControlPlaneApi(options) {
|
|
|
1457
1497
|
}
|
|
1458
1498
|
try {
|
|
1459
1499
|
const vertical = await verticalForScope(c, scope);
|
|
1460
|
-
|
|
1461
|
-
|
|
1500
|
+
// By this point the backup contract has resolved (a copy landed, or the caller
|
|
1501
|
+
// explicitly declined one), so stranding is a bookkeeping fact, not data loss.
|
|
1502
|
+
const storageStranded = await deleteScopeStorageOrStrand(vertical, scopeId);
|
|
1462
1503
|
await admin.reapScope(actor, tenantId, scopeId, {
|
|
1463
1504
|
...(backup ? { backupRef: backupRefOf(backup) } : {}),
|
|
1464
1505
|
});
|
|
1465
1506
|
const reaped = await admin.getScopeRecord(actor, tenantId, scopeId);
|
|
1466
|
-
return c.json({ ...reaped, backup });
|
|
1507
|
+
return c.json({ ...reaped, backup, ...(storageStranded ? { storageStranded: true } : {}) });
|
|
1467
1508
|
}
|
|
1468
1509
|
catch (e) {
|
|
1469
1510
|
if (e instanceof ControlPlaneError) {
|
|
@@ -1882,6 +1923,11 @@ export function createControlPlaneApi(options) {
|
|
|
1882
1923
|
// so its auth adapter resolves logins from local storage, and offboarding becomes an
|
|
1883
1924
|
// unlink + re-deliver instead of a source edit + deploy.
|
|
1884
1925
|
const identityLinks = (await admin.listIdentityLinks(c.get('actor'), input.tenantId)).map(({ tenantId: _tenantId, ...link }) => link);
|
|
1926
|
+
// #592: connection grants too — tenant-wide rows materialize for the NEW scope, so an
|
|
1927
|
+
// install provisioned after `grantToConnection` holds the same `connection:<id>` tuple
|
|
1928
|
+
// as one provisioned before it, and the connector return path works without a human
|
|
1929
|
+
// replaying grants per install.
|
|
1930
|
+
const connectionGrants = connectionGrantsForScope(await admin.listConnectionGrants(c.get('actor'), input.tenantId), slug, input.scopeId);
|
|
1885
1931
|
// #301 PR-2: per-tenant relational stores the vertical DECLARED, minted here (before
|
|
1886
1932
|
// the callback — the vertical migrates the store inside the K-31 ready-gate, so it
|
|
1887
1933
|
// must exist and be bound first). Idempotent like the endpoint: a retried provision
|
|
@@ -1913,6 +1959,7 @@ export function createControlPlaneApi(options) {
|
|
|
1913
1959
|
...input,
|
|
1914
1960
|
entitlements,
|
|
1915
1961
|
identityLinks,
|
|
1962
|
+
connectionGrants,
|
|
1916
1963
|
...(tenantStores.length ? { tenantStores } : {}),
|
|
1917
1964
|
}));
|
|
1918
1965
|
return c.json(instance, 201);
|