@substrat-run/control-plane-api 0.4.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 ADDED
@@ -0,0 +1,44 @@
1
+ import { Hono } from 'hono';
2
+ import type { PlatformActorId } from '@substrat-run/contracts';
3
+ import type { ScopeHost } from '@substrat-run/kernel';
4
+ import type { PlatformActorAuth } from './auth.js';
5
+ export interface ControlPlaneApiOptions {
6
+ host: ScopeHost;
7
+ /**
8
+ * Resolves the platform actor from the request. No default: an unauthenticated
9
+ * control plane is not a sensible fallback, and a package that shipped one
10
+ * would eventually be deployed with it (control-plane.md §6).
11
+ */
12
+ authenticate: PlatformActorAuth;
13
+ }
14
+ type Vars = {
15
+ actor: PlatformActorId;
16
+ };
17
+ /**
18
+ * The audited HTTP surface over `HostAdmin` (control-plane.md §4.5).
19
+ *
20
+ * This is the OUT-OF-BAND control plane §3 argues for: it is not module code, it
21
+ * never receives a `ctx`, and it never runs in a scope's serialization domain —
22
+ * so `boundary-lint` is untouched (§7). It is one router over the kernel seam,
23
+ * mounted by whichever transport is hosting it (a Node server locally, a Worker
24
+ * holding the `controlPlane` binding on Cloudflare).
25
+ *
26
+ * Two rules hold everywhere below, and they are the reason this can exist at all:
27
+ *
28
+ * 1. **The actor comes from the authenticated request, never the body.** §4.4:
29
+ * every field of an audit row except before/after is stamped platform-side,
30
+ * "never supplied by the caller". A route that read an actor from JSON would
31
+ * make the entire trail forgeable, which is the one thing that must not be
32
+ * retrofitted (K-20). Note there is no route here that accepts an `actor`
33
+ * field at all — it is unrepresentable, not merely ignored.
34
+ * 2. **Reads are exposed; enforcement writes are not.** defineRole / assignRole /
35
+ * grant / grantToOrg / addMember / linkIdentity are on `HostAdmin` but get no
36
+ * route: the console's v1 job is the tenant registry, lifecycle, entitlements
37
+ * and history. `resolveIdentity` especially stays off — it is the auth
38
+ * adapter's read path, not an admin surface.
39
+ */
40
+ export declare function createControlPlaneApi(options: ControlPlaneApiOptions): Hono<{
41
+ Variables: Vars;
42
+ }>;
43
+ export {};
44
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAY5B,OAAO,KAAK,EAAE,eAAe,EAAqB,MAAM,yBAAyB,CAAC;AAClF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAGnD,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,SAAS,CAAC;IAChB;;;;OAIG;IACH,YAAY,EAAE,iBAAiB,CAAC;CACjC;AAED,KAAK,IAAI,GAAG;IAAE,KAAK,EAAE,eAAe,CAAA;CAAE,CAAC;AAkDvC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAAE,SAAS,EAAE,IAAI,CAAA;CAAE,CAAC,CAwJhG"}
package/dist/api.js ADDED
@@ -0,0 +1,202 @@
1
+ import { Hono } from 'hono';
2
+ import { adminAction, createTenantInput, jurisdiction, scopeId as scopeIdSchema, scopeStatus, storageShape, tenantId as tenantIdSchema, tenantStatus, z, } from '@substrat-run/contracts';
3
+ import { mapError } from './errors.js';
4
+ // -- request schemas ---------------------------------------------------------
5
+ // Parse, don't trust: every input crosses Zod at the boundary. The ids stay
6
+ // CALLER-SUPPLIED rather than minted here, exactly as the contract has them —
7
+ // that is what keeps `createTenant`/`provisionScope` idempotent (§3.3: "safe to
8
+ // re-run"). Minting server-side would be friendlier and would silently turn a
9
+ // retry into a second tenant. This surface is a transport; it does not invent
10
+ // semantics on top of HostAdmin.
11
+ const provisionScopeBody = z.object({
12
+ tenantId: tenantIdSchema,
13
+ scopeId: scopeIdSchema,
14
+ slug: z.string().optional(),
15
+ kind: z.string().optional(),
16
+ name: z.string().optional(),
17
+ vertical: z.string().nullable().optional(),
18
+ storageShape: storageShape.optional(),
19
+ jurisdiction: jurisdiction.optional(),
20
+ });
21
+ const setTenantStatusBody = z.object({ status: tenantStatus });
22
+ /** Repeatable query params arrive as `?status=active&status=suspended`. */
23
+ const listScopesQuery = z.object({
24
+ tenantId: tenantIdSchema.optional(),
25
+ status: z.array(scopeStatus).optional(),
26
+ vertical: z.string().optional(),
27
+ });
28
+ const listRolesQuery = z.object({
29
+ tenantId: tenantIdSchema.optional(),
30
+ // Free-form: a module id or 'vertical'. Not narrowed to the source union here
31
+ // — an unknown source should return nothing, not 400. The console offers only
32
+ // sources it has seen.
33
+ source: z.string().optional(),
34
+ });
35
+ const auditLogQuery = z.object({
36
+ tenantId: tenantIdSchema.optional(),
37
+ scopeId: scopeIdSchema.optional(),
38
+ actor: z.string().optional(),
39
+ action: z.array(adminAction).optional(),
40
+ since: z.string().optional(),
41
+ until: z.string().optional(),
42
+ limit: z.coerce.number().int().positive().max(1000).optional(),
43
+ cursor: z.string().optional(),
44
+ order: z.enum(['asc', 'desc']).optional(),
45
+ });
46
+ /**
47
+ * The audited HTTP surface over `HostAdmin` (control-plane.md §4.5).
48
+ *
49
+ * This is the OUT-OF-BAND control plane §3 argues for: it is not module code, it
50
+ * never receives a `ctx`, and it never runs in a scope's serialization domain —
51
+ * so `boundary-lint` is untouched (§7). It is one router over the kernel seam,
52
+ * mounted by whichever transport is hosting it (a Node server locally, a Worker
53
+ * holding the `controlPlane` binding on Cloudflare).
54
+ *
55
+ * Two rules hold everywhere below, and they are the reason this can exist at all:
56
+ *
57
+ * 1. **The actor comes from the authenticated request, never the body.** §4.4:
58
+ * every field of an audit row except before/after is stamped platform-side,
59
+ * "never supplied by the caller". A route that read an actor from JSON would
60
+ * make the entire trail forgeable, which is the one thing that must not be
61
+ * retrofitted (K-20). Note there is no route here that accepts an `actor`
62
+ * field at all — it is unrepresentable, not merely ignored.
63
+ * 2. **Reads are exposed; enforcement writes are not.** defineRole / assignRole /
64
+ * grant / grantToOrg / addMember / linkIdentity are on `HostAdmin` but get no
65
+ * route: the console's v1 job is the tenant registry, lifecycle, entitlements
66
+ * and history. `resolveIdentity` especially stays off — it is the auth
67
+ * adapter's read path, not an admin surface.
68
+ */
69
+ export function createControlPlaneApi(options) {
70
+ const { host, authenticate } = options;
71
+ const admin = host.admin;
72
+ const app = new Hono();
73
+ // Fail closed, before any route runs: no actor, no reach.
74
+ app.use('*', async (c, next) => {
75
+ const actor = await authenticate(c.req.raw);
76
+ if (!actor)
77
+ return c.json({ error: 'unauthenticated' }, 401);
78
+ c.set('actor', actor);
79
+ await next();
80
+ });
81
+ // One error boundary for every route: adapters throw plain Errors, and each
82
+ // one is a fail-closed refusal that must reach the caller as a status, not a
83
+ // stack trace.
84
+ app.onError((err, c) => {
85
+ if (err instanceof z.ZodError) {
86
+ return c.json({ error: 'invalid request', issues: err.issues }, 400);
87
+ }
88
+ const { status, body } = mapError(err);
89
+ return c.json(body, status);
90
+ });
91
+ // -- tenant registry (§4.1) ------------------------------------------------
92
+ app.get('/tenants', async (c) => c.json(await admin.listTenants()));
93
+ app.post('/tenants', async (c) => {
94
+ const input = createTenantInput.parse(await c.req.json());
95
+ await admin.createTenant(c.get('actor'), input);
96
+ // Idempotent (§4.1): re-creating an existing tenant is a no-op, not an error,
97
+ // so this reads back rather than reporting a create that may not have happened.
98
+ return c.json(await admin.getTenant(input.id), 201);
99
+ });
100
+ app.get('/tenants/:tenantId', async (c) => {
101
+ const tenantId = tenantIdSchema.parse(c.req.param('tenantId'));
102
+ const tenant = await admin.getTenant(tenantId);
103
+ if (!tenant)
104
+ return c.json({ error: `unknown tenant: ${tenantId}` }, 404);
105
+ return c.json(tenant);
106
+ });
107
+ app.patch('/tenants/:tenantId/status', async (c) => {
108
+ const tenantId = tenantIdSchema.parse(c.req.param('tenantId'));
109
+ const { status } = setTenantStatusBody.parse(await c.req.json());
110
+ // The live weapon (§7): `suspended` fails getScope closed for EVERY scope
111
+ // under the tenant. The blast radius is the console's to show; the audit row
112
+ // is this layer's to guarantee.
113
+ await admin.setTenantStatus(c.get('actor'), tenantId, status);
114
+ return c.json(await admin.getTenant(tenantId));
115
+ });
116
+ // -- entitlements (§4.3) ---------------------------------------------------
117
+ app.get('/tenants/:tenantId/entitlements', async (c) => c.json(await admin.listEntitlements(tenantIdSchema.parse(c.req.param('tenantId')))));
118
+ app.put('/tenants/:tenantId/entitlements/:key', async (c) => {
119
+ const tenantId = tenantIdSchema.parse(c.req.param('tenantId'));
120
+ await admin.grantEntitlement(c.get('actor'), tenantId, c.req.param('key'));
121
+ return c.json(await admin.listEntitlements(tenantId));
122
+ });
123
+ app.delete('/tenants/:tenantId/entitlements/:key', async (c) => {
124
+ const tenantId = tenantIdSchema.parse(c.req.param('tenantId'));
125
+ await admin.revokeEntitlement(c.get('actor'), tenantId, c.req.param('key'));
126
+ return c.json(await admin.listEntitlements(tenantId));
127
+ });
128
+ // -- the scope directory (§3.2/§4.2) ---------------------------------------
129
+ app.get('/scopes', async (c) => {
130
+ const filter = listScopesQuery.parse({
131
+ tenantId: c.req.query('tenantId'),
132
+ status: c.req.queries('status'),
133
+ vertical: c.req.query('vertical'),
134
+ });
135
+ return c.json(await admin.listScopes(filter));
136
+ });
137
+ app.post('/scopes', async (c) => {
138
+ const input = provisionScopeBody.parse(await c.req.json());
139
+ await host.provisionScope(c.get('actor'), input);
140
+ const record = await admin.getScopeRecord(input.tenantId, input.scopeId);
141
+ return c.json(record, 201);
142
+ });
143
+ app.get('/tenants/:tenantId/scopes/:scopeId', async (c) => {
144
+ const tenantId = tenantIdSchema.parse(c.req.param('tenantId'));
145
+ const scopeId = scopeIdSchema.parse(c.req.param('scopeId'));
146
+ const record = await admin.getScopeRecord(tenantId, scopeId);
147
+ // Absent, or present under another tenant — indistinguishable on purpose (K-3).
148
+ if (!record)
149
+ return c.json({ error: `unknown scope for tenant: (${tenantId}, ${scopeId})` }, 404);
150
+ return c.json(record);
151
+ });
152
+ // The four lifecycle transitions, one route each — mirroring the four audited
153
+ // actions rather than collapsing into a PATCH that would accept a target
154
+ // status the transition graph forbids. The graph is enforced below the seam;
155
+ // an illegal transition surfaces as a 409.
156
+ const transitions = {
157
+ suspend: (a, t, s) => admin.suspendScope(a, t, s),
158
+ unsuspend: (a, t, s) => admin.unsuspendScope(a, t, s),
159
+ archive: (a, t, s) => admin.archiveScope(a, t, s),
160
+ unarchive: (a, t, s) => admin.unarchiveScope(a, t, s),
161
+ };
162
+ for (const [action, run] of Object.entries(transitions)) {
163
+ app.post(`/tenants/:tenantId/scopes/:scopeId/${action}`, async (c) => {
164
+ const tenantId = tenantIdSchema.parse(c.req.param('tenantId'));
165
+ const scopeId = scopeIdSchema.parse(c.req.param('scopeId'));
166
+ await run(c.get('actor'), tenantId, scopeId);
167
+ return c.json(await admin.getScopeRecord(tenantId, scopeId));
168
+ });
169
+ }
170
+ // -- roles, read only (§4.5 console item 4) --------------------------------
171
+ // The READ lands; `defineRole` deliberately does not. Creating a role over
172
+ // HTTP is a permission change, and the permission diff is a human checkpoint
173
+ // (D-22/D-29) — that surface needs its own decision, not a route added because
174
+ // the verb was adjacent.
175
+ app.get('/roles', async (c) => {
176
+ const filter = listRolesQuery.parse({
177
+ tenantId: c.req.query('tenantId'),
178
+ source: c.req.query('source'),
179
+ });
180
+ return c.json(await admin.listRoles(filter));
181
+ });
182
+ // -- the admin log (§4.4/§4.5) ---------------------------------------------
183
+ app.get('/admin-log', async (c) => {
184
+ const filter = auditLogQuery.parse({
185
+ tenantId: c.req.query('tenantId'),
186
+ scopeId: c.req.query('scopeId'),
187
+ actor: c.req.query('actor'),
188
+ action: c.req.queries('action'),
189
+ since: c.req.query('since'),
190
+ until: c.req.query('until'),
191
+ limit: c.req.query('limit'),
192
+ cursor: c.req.query('cursor'),
193
+ order: c.req.query('order'),
194
+ });
195
+ const entries = await admin.auditLog(filter);
196
+ // The cursor IS the last entry's id (ULID order is chronological), so the
197
+ // page carries its own continuation and the console never assembles one.
198
+ return c.json({ entries, nextCursor: entries.at(-1)?.id ?? null });
199
+ });
200
+ return app;
201
+ }
202
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
package/dist/auth.d.ts ADDED
@@ -0,0 +1,39 @@
1
+ import { type PlatformActorId } from '@substrat-run/contracts';
2
+ /**
3
+ * The identity seam for the control plane (control-plane.md §6).
4
+ *
5
+ * The data model needs to know THAT there is an actor, not HOW it authenticated
6
+ * — so this whole surface is buildable and testable against a stub while real
7
+ * staff auth (SSO, MFA, short sessions, a small closed population) is designed.
8
+ * D-16 commits to identity being a swappable adapter; this is that being cashed
9
+ * in for platform staff rather than tenant users.
10
+ *
11
+ * Returning null means "not authenticated" and the request is refused. There is
12
+ * no "anonymous actor" — §4.4's whole point is that a surface which can act
13
+ * without a durable record of WHO acted is worse than no surface, and an actor
14
+ * the log cannot name is exactly that.
15
+ */
16
+ export type PlatformActorAuth = (request: Request) => Promise<PlatformActorId | null> | PlatformActorId | null;
17
+ /** Header the dev stub reads. Mirrors the demos' `x-principal` dev affordance. */
18
+ export declare const DEV_ACTOR_HEADER = "x-platform-actor";
19
+ /**
20
+ * A dev stub that trusts an `x-platform-actor` header verbatim.
21
+ *
22
+ * UNSAFE_ by name, deliberately — it is the same convention the kernel uses for
23
+ * `UNSAFE_allowAllChecker`, and for the same reason: an unsafe default that is
24
+ * merely *documented* gets shipped, while one that must be typed out in the
25
+ * caller's own code gets noticed in review.
26
+ *
27
+ * **Never expose this on a non-local listener.** control-plane.md §6: real auth
28
+ * gates EXPOSING the console, not BUILDING it — nothing with cross-tenant reach
29
+ * goes anywhere non-local on a stub. The demos' `x-principal` header is a dev
30
+ * affordance for ONE tenant's principal; this header names a subject with reach
31
+ * across every tenant on the platform, so "a super-admin on top of it is a
32
+ * liability, not a milestone".
33
+ *
34
+ * It still parses: a header that is not a ULID is rejected rather than written
35
+ * into the audit log, because a malformed actor makes the trail unreadable
36
+ * exactly when it matters.
37
+ */
38
+ export declare function UNSAFE_devPlatformActorAuth(): PlatformActorAuth;
39
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAEhF;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,eAAe,GAAG,IAAI,CAAC;AAE/G,kFAAkF;AAClF,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAEnD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,2BAA2B,IAAI,iBAAiB,CAO/D"}
package/dist/auth.js ADDED
@@ -0,0 +1,32 @@
1
+ import { platformActorId } from '@substrat-run/contracts';
2
+ /** Header the dev stub reads. Mirrors the demos' `x-principal` dev affordance. */
3
+ export const DEV_ACTOR_HEADER = 'x-platform-actor';
4
+ /**
5
+ * A dev stub that trusts an `x-platform-actor` header verbatim.
6
+ *
7
+ * UNSAFE_ by name, deliberately — it is the same convention the kernel uses for
8
+ * `UNSAFE_allowAllChecker`, and for the same reason: an unsafe default that is
9
+ * merely *documented* gets shipped, while one that must be typed out in the
10
+ * caller's own code gets noticed in review.
11
+ *
12
+ * **Never expose this on a non-local listener.** control-plane.md §6: real auth
13
+ * gates EXPOSING the console, not BUILDING it — nothing with cross-tenant reach
14
+ * goes anywhere non-local on a stub. The demos' `x-principal` header is a dev
15
+ * affordance for ONE tenant's principal; this header names a subject with reach
16
+ * across every tenant on the platform, so "a super-admin on top of it is a
17
+ * liability, not a milestone".
18
+ *
19
+ * It still parses: a header that is not a ULID is rejected rather than written
20
+ * into the audit log, because a malformed actor makes the trail unreadable
21
+ * exactly when it matters.
22
+ */
23
+ export function UNSAFE_devPlatformActorAuth() {
24
+ return (request) => {
25
+ const raw = request.headers.get(DEV_ACTOR_HEADER);
26
+ if (!raw)
27
+ return null;
28
+ const parsed = platformActorId.safeParse(raw);
29
+ return parsed.success ? parsed.data : null;
30
+ };
31
+ }
32
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAwB,MAAM,yBAAyB,CAAC;AAkBhF,kFAAkF;AAClF,MAAM,CAAC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAEnD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,2BAA2B;IACzC,OAAO,CAAC,OAAO,EAAE,EAAE;QACjB,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAClD,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,MAAM,MAAM,GAAG,eAAe,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAC9C,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7C,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { ContentfulStatusCode } from 'hono/utils/http-status';
2
+ export interface ApiError {
3
+ status: ContentfulStatusCode;
4
+ body: {
5
+ error: string;
6
+ };
7
+ }
8
+ export declare function mapError(err: unknown): ApiError;
9
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AA6CnE,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,oBAAoB,CAAC;IAC7B,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACzB;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ,CAM/C"}
package/dist/errors.js ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Map an adapter throw onto an HTTP status.
3
+ *
4
+ * `HostAdmin` throws plain `Error`s — deliberately, so their messages survive
5
+ * the Cloudflare RPC hop intact (a ZodError would not). That leaves this layer
6
+ * matching on message text, which is the weakest seam in this package and worth
7
+ * naming rather than hiding:
8
+ *
9
+ * - It is less brittle than it looks. Every pattern below is a message the
10
+ * CONTRACT SUITE asserts on (`/unknown tenant/`, `/illegal scope transition/`,
11
+ * `/already taken/`, `/not active/`), against both adapters. Changing one
12
+ * turns a contract test red, not just this mapping.
13
+ * - It is still text. The durable fix is typed errors on `HostAdmin` — a tagged
14
+ * union the adapters throw and this reads. That is a kernel change, and it is
15
+ * not worth blocking the transport on.
16
+ *
17
+ * Anything unmatched is a 500 with a GENERIC body: an unrecognised throw is, by
18
+ * definition, one whose message we have not reviewed for what it discloses, and
19
+ * this surface has cross-tenant reach.
20
+ */
21
+ /**
22
+ * ORDER IS SIGNIFICANT — first match wins, so every specific pattern must precede
23
+ * the general one it would otherwise be swallowed by. `cannot provision scope
24
+ * under unknown tenant` contains `unknown tenant:`, and listing the general one
25
+ * first turned a precondition conflict into a 404 claiming POST /scopes does not
26
+ * exist. That is the message-matching fragility this file admits to above, caught
27
+ * by the test below rather than by reading.
28
+ */
29
+ const STATUS_PATTERNS = [
30
+ // Well-formed, but conflicts with current state or references something absent.
31
+ // The addressed collection exists; the request cannot be applied to it.
32
+ [/cannot provision scope under unknown tenant/, 409],
33
+ [/already taken/, 409],
34
+ [/illegal scope transition/, 409],
35
+ [/non-active tenant/, 409],
36
+ [/not active \(status:/, 409],
37
+ // The ADDRESSED resource does not exist — including the K-3 fail-closed case
38
+ // where it exists under a DIFFERENT tenant and must read as absent.
39
+ [/unknown tenant:/, 404],
40
+ [/unknown scope for tenant/, 404],
41
+ [/scope has no tenant record/, 404],
42
+ ];
43
+ export function mapError(err) {
44
+ const message = err instanceof Error ? err.message : String(err);
45
+ for (const [pattern, status] of STATUS_PATTERNS) {
46
+ if (pattern.test(message))
47
+ return { status, body: { error: message } };
48
+ }
49
+ return { status: 500, body: { error: 'internal error' } };
50
+ }
51
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AACH;;;;;;;GAOG;AACH,MAAM,eAAe,GAA8C;IACjE,gFAAgF;IAChF,wEAAwE;IACxE,CAAC,6CAA6C,EAAE,GAAG,CAAC;IACpD,CAAC,eAAe,EAAE,GAAG,CAAC;IACtB,CAAC,0BAA0B,EAAE,GAAG,CAAC;IACjC,CAAC,mBAAmB,EAAE,GAAG,CAAC;IAC1B,CAAC,sBAAsB,EAAE,GAAG,CAAC;IAC7B,6EAA6E;IAC7E,oEAAoE;IACpE,CAAC,iBAAiB,EAAE,GAAG,CAAC;IACxB,CAAC,0BAA0B,EAAE,GAAG,CAAC;IACjC,CAAC,4BAA4B,EAAE,GAAG,CAAC;CACpC,CAAC;AAOF,MAAM,UAAU,QAAQ,CAAC,GAAY;IACnC,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,KAAK,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,eAAe,EAAE,CAAC;QAChD,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC;IACzE,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAAE,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { createControlPlaneApi } from './api.js';
2
+ export type { ControlPlaneApiOptions } from './api.js';
3
+ export { DEV_ACTOR_HEADER, UNSAFE_devPlatformActorAuth } from './auth.js';
4
+ export type { PlatformActorAuth } from './auth.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjD,YAAY,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,MAAM,WAAW,CAAC;AAC1E,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { createControlPlaneApi } from './api.js';
2
+ export { DEV_ACTOR_HEADER, UNSAFE_devPlatformActorAuth } from './auth.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAEjD,OAAO,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,MAAM,WAAW,CAAC"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@substrat-run/control-plane-api",
3
+ "version": "0.4.0",
4
+ "description": "HTTP surface over HostAdmin — the audited control-plane transport (control-plane.md §4.5)",
5
+ "license": "AGPL-3.0-only",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/substrat-run/substrat.git",
9
+ "directory": "packages/control-plane-api"
10
+ },
11
+ "homepage": "https://github.com/substrat-run/substrat",
12
+ "type": "module",
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsc -p tsconfig.json",
26
+ "typecheck": "tsc -p tsconfig.json --noEmit",
27
+ "test": "vitest run",
28
+ "dev": "tsx dev/server.mts"
29
+ },
30
+ "dependencies": {
31
+ "@substrat-run/contracts": "workspace:^",
32
+ "@substrat-run/kernel": "workspace:^",
33
+ "hono": "^4.6.0"
34
+ },
35
+ "devDependencies": {
36
+ "@hono/node-server": "^1.13.0",
37
+ "@substrat-run/adapter-sqlite": "workspace:^",
38
+ "tsx": "^4.19.0",
39
+ "typescript": "^5.6.0",
40
+ "vitest": "^3.0.0"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ }
45
+ }