@substrat-run/control-plane-api 0.87.0 → 0.89.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.map +1 -1
- package/dist/api.js +76 -3
- package/dist/api.js.map +1 -1
- package/dist/client.d.ts +9 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +35 -1
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +2 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +85 -58
- package/dist/errors.js.map +1 -1
- package/dist/vertical-client.d.ts +11 -1
- package/dist/vertical-client.d.ts.map +1 -1
- package/dist/vertical-client.js +34 -0
- package/dist/vertical-client.js.map +1 -1
- package/package.json +6 -6
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;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/api.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
import { adminAction, ASSET_PART_PREFIX, assetHash, channelName, connectionActivity, connectionActivitySource, connectionCredential, connectionFilter, connectionProbe, createTenantInput, entitlementGrantInput, hostname as hostnameSchema, hostnameRegion, hostnameStatus, identityLink, listPageQuery, matchesOutboundHost, pageOf, platformRequestFilter, 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';
|
|
2
|
+
import { adminAction, ASSET_PART_PREFIX, assetHash, channelName, connectionActivity, connectionActivitySource, connectionCredential, connectionFilter, connectionProbe, createTenantInput, entitlementGrantInput, hostname as hostnameSchema, hostnameRegion, hostnameStatus, identityLink, listPageQuery, matchesOutboundHost, pageOf, platformRequestFilter, principalId as principalIdSchema, promotionAcknowledgement, provisionableJurisdiction, publishVersionInput, queryScopeInput, readScopeTableInput, DEFAULT_DENIAL_LIMIT, DENIAL_LIMIT_MAX, registerVerticalInput, scopeDump, dataSubjectId as dataSubjectIdSchema, scopeId as scopeIdSchema, scopeStatus, storageShape, surfaceName, tenantId as tenantIdSchema, tenantStatus, versionOrigin, z, PROBLEM_CONTENT_TYPE, toProblem, } from '@substrat-run/contracts';
|
|
3
3
|
import { migrationProgress, ulid } from '@substrat-run/kernel';
|
|
4
4
|
import { TENANT_HEADER } from './auth.js';
|
|
5
5
|
import { connectionGrantsForScope } from './vertical-client.js';
|
|
@@ -11,6 +11,19 @@ import { mapError } from './errors.js';
|
|
|
11
11
|
import { maskDump, maskRecords } from './mask.js';
|
|
12
12
|
import { openDump, sealDump } from './seal.js';
|
|
13
13
|
import { assertSandboxContract, deployManifest, storedDeployManifest, deploymentRefFor, stableDeploymentRefFor, nextMigrationTag, upstreamStatusOf, } from './deploy.js';
|
|
14
|
+
/**
|
|
15
|
+
* Answer with a problem document (#113 phase 4).
|
|
16
|
+
*
|
|
17
|
+
* `c.body` rather than `c.json`, because the media type is the contract: a response
|
|
18
|
+
* labelled `application/json` is not an RFC 9457 problem however well-shaped its body is,
|
|
19
|
+
* and `/openapi.json` has documented `application/problem+json` on every error response
|
|
20
|
+
* since phase 1 — this is the half that makes the document true.
|
|
21
|
+
*/
|
|
22
|
+
function problem(c, answer) {
|
|
23
|
+
return c.body(JSON.stringify(answer.body), answer.status, {
|
|
24
|
+
'content-type': PROBLEM_CONTENT_TYPE,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
14
27
|
import { backfillDeclaredStores, blobStoreBindings, collectBlobStoreHandles, collectTenantStoreHandles, missingStoresForTenant, tenantStoreBindings, } from './tenant-stores.js';
|
|
15
28
|
import { mintPushToken, pushActorFor } from './push-token.js';
|
|
16
29
|
import { namespacesForScript } from './do-namespaces.js';
|
|
@@ -222,6 +235,19 @@ const auditLogQuery = z.object({
|
|
|
222
235
|
// everything) — only the HTTP egress vectors are bounded by default.
|
|
223
236
|
...listPageQuery.shape,
|
|
224
237
|
});
|
|
238
|
+
/**
|
|
239
|
+
* The K-35 denial-log filter (#867). Bounded by default like every other HTTP read
|
|
240
|
+
* here: the log's volume is attacker-influenceable by design (a probing client mints
|
|
241
|
+
* rows), so an unbounded `GET` is exactly the wrong default.
|
|
242
|
+
*/
|
|
243
|
+
const denialLogQuery = z.object({
|
|
244
|
+
actor: z.string().optional(),
|
|
245
|
+
permission: z.string().optional(),
|
|
246
|
+
operation: z.string().optional(),
|
|
247
|
+
since: z.string().optional(),
|
|
248
|
+
until: z.string().optional(),
|
|
249
|
+
limit: z.coerce.number().int().min(1).max(DENIAL_LIMIT_MAX).default(DEFAULT_DENIAL_LIMIT),
|
|
250
|
+
});
|
|
225
251
|
const opsFailuresQuery = z.object({
|
|
226
252
|
tenantId: tenantIdSchema.optional(),
|
|
227
253
|
scopeId: scopeIdSchema.optional(),
|
|
@@ -406,8 +432,12 @@ export function createControlPlaneApi(options) {
|
|
|
406
432
|
// one is a fail-closed refusal that must reach the caller as a status, not a
|
|
407
433
|
// stack trace.
|
|
408
434
|
app.onError((err, c) => {
|
|
435
|
+
// A parse failure goes through the same builder as everything else (#113 phase 4):
|
|
436
|
+
// `toProblem` maps zod's `issues` onto the declared `errors: [{ path, message }]`
|
|
437
|
+
// extension, which is the shape the model documents and a client can act on. The raw
|
|
438
|
+
// `issues` array it used to echo was zod's own, undocumented, and read by nobody.
|
|
409
439
|
if (err instanceof z.ZodError) {
|
|
410
|
-
return c
|
|
440
|
+
return problem(c, { status: 400, body: toProblem(err, c.req.path) });
|
|
411
441
|
}
|
|
412
442
|
const { status, body } = mapError(err);
|
|
413
443
|
// A 5xx is the PLATFORM failing — an unmapped throw, or a downstream vertical's
|
|
@@ -445,7 +475,7 @@ export function createControlPlaneApi(options) {
|
|
|
445
475
|
stack: err instanceof Error ? err.stack : undefined,
|
|
446
476
|
});
|
|
447
477
|
}
|
|
448
|
-
return c
|
|
478
|
+
return problem(c, { status, body });
|
|
449
479
|
});
|
|
450
480
|
// -- tenant registry (§4.1) ------------------------------------------------
|
|
451
481
|
app.get('/tenants', async (c) => {
|
|
@@ -1394,6 +1424,49 @@ export function createControlPlaneApi(options) {
|
|
|
1394
1424
|
? await vertical.queryScope(scopeId, input)
|
|
1395
1425
|
: await admin.queryScope(c.get('actor'), tenantId, scopeId, input));
|
|
1396
1426
|
});
|
|
1427
|
+
// The K-35 denial log (#867) — the third of the platform's three logs and the last to
|
|
1428
|
+
// get a reader. `/admin-log` holds staff mutations and the K-24 access log staff
|
|
1429
|
+
// reads; these are the refusals, and they live in the SCOPE's database rather than the
|
|
1430
|
+
// directory because a denial rolls its own operation back. Same delegation ladder as
|
|
1431
|
+
// the table reads: a hosted scope is asked through its vertical, a co-located one
|
|
1432
|
+
// locally. Builders read only their own tenant — a foreign one is hidden as 404 (K-3),
|
|
1433
|
+
// matching every other scope-addressed read here.
|
|
1434
|
+
//
|
|
1435
|
+
// Two routes, not one with a flag. The bucketed view returns a different shape, and it
|
|
1436
|
+
// is the one an operator opens first — "who has been probing for access they don't
|
|
1437
|
+
// hold" is a question about counts.
|
|
1438
|
+
app.get('/tenants/:tenantId/scopes/:scopeId/denials', async (c) => {
|
|
1439
|
+
const tenantId = tenantIdSchema.parse(c.req.param('tenantId'));
|
|
1440
|
+
const scopeId = scopeIdSchema.parse(c.req.param('scopeId'));
|
|
1441
|
+
const principal = c.get('principal');
|
|
1442
|
+
if (principal.kind === 'builder' && principal.tenantId !== tenantId) {
|
|
1443
|
+
return c.json({ error: `unknown scope for tenant: (${tenantId}, ${scopeId})` }, 404);
|
|
1444
|
+
}
|
|
1445
|
+
const filter = denialLogQuery.parse(c.req.query());
|
|
1446
|
+
const scope = await admin.getScopeRecord(c.get('actor'), tenantId, scopeId);
|
|
1447
|
+
if (!scope)
|
|
1448
|
+
return c.json({ error: `unknown scope for tenant: (${tenantId}, ${scopeId})` }, 404);
|
|
1449
|
+
const vertical = await verticalForScope(c, scope);
|
|
1450
|
+
return c.json(vertical
|
|
1451
|
+
? await vertical.listDenials(scopeId, filter)
|
|
1452
|
+
: await admin.listDenials(c.get('actor'), tenantId, scopeId, filter));
|
|
1453
|
+
});
|
|
1454
|
+
app.get('/tenants/:tenantId/scopes/:scopeId/denials/summary', async (c) => {
|
|
1455
|
+
const tenantId = tenantIdSchema.parse(c.req.param('tenantId'));
|
|
1456
|
+
const scopeId = scopeIdSchema.parse(c.req.param('scopeId'));
|
|
1457
|
+
const principal = c.get('principal');
|
|
1458
|
+
if (principal.kind === 'builder' && principal.tenantId !== tenantId) {
|
|
1459
|
+
return c.json({ error: `unknown scope for tenant: (${tenantId}, ${scopeId})` }, 404);
|
|
1460
|
+
}
|
|
1461
|
+
const filter = denialLogQuery.parse(c.req.query());
|
|
1462
|
+
const scope = await admin.getScopeRecord(c.get('actor'), tenantId, scopeId);
|
|
1463
|
+
if (!scope)
|
|
1464
|
+
return c.json({ error: `unknown scope for tenant: (${tenantId}, ${scopeId})` }, 404);
|
|
1465
|
+
const vertical = await verticalForScope(c, scope);
|
|
1466
|
+
return c.json(vertical
|
|
1467
|
+
? await vertical.summarizeDenials(scopeId, filter)
|
|
1468
|
+
: await admin.summarizeDenials(c.get('actor'), tenantId, scopeId, filter));
|
|
1469
|
+
});
|
|
1397
1470
|
// Deliver per-instance CONFIG to the scope's own storage (vertical-auth-detach.md
|
|
1398
1471
|
// §2.2) — the missing "delivery" step behind the dashboard's Env tab. Same K-3
|
|
1399
1472
|
// addressing + bound-version resolution as introspection: the scope's DO lives in the
|