@substrat-run/control-plane-api 0.114.0 → 0.117.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 +31 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +594 -106
- package/dist/api.js.map +1 -1
- package/dist/auth.d.ts +35 -3
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +11 -0
- package/dist/auth.js.map +1 -1
- package/dist/cf-observability.d.ts.map +1 -1
- package/dist/cf-observability.js +26 -2
- package/dist/cf-observability.js.map +1 -1
- package/dist/client.d.ts +35 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +47 -3
- package/dist/client.js.map +1 -1
- package/dist/deploy.d.ts +3 -2
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +5 -4
- package/dist/deploy.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +32 -11
- package/dist/errors.js.map +1 -1
- package/dist/identity-tenants.d.ts +30 -0
- package/dist/identity-tenants.d.ts.map +1 -0
- package/dist/identity-tenants.js +23 -0
- package/dist/identity-tenants.js.map +1 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/observability.d.ts +6 -0
- package/dist/observability.d.ts.map +1 -1
- package/dist/platform-drain.d.ts +6 -3
- package/dist/platform-drain.d.ts.map +1 -1
- package/dist/platform-drain.js +22 -4
- package/dist/platform-drain.js.map +1 -1
- package/dist/push-token.d.ts.map +1 -1
- package/dist/push-token.js +9 -23
- package/dist/push-token.js.map +1 -1
- package/dist/scope-deployment.d.ts +34 -0
- package/dist/scope-deployment.d.ts.map +1 -0
- package/dist/scope-deployment.js +19 -0
- package/dist/scope-deployment.js.map +1 -0
- package/dist/storage-meter.d.ts +32 -0
- package/dist/storage-meter.d.ts.map +1 -0
- package/dist/storage-meter.js +69 -0
- package/dist/storage-meter.js.map +1 -0
- package/dist/tenant-token.d.ts +41 -0
- package/dist/tenant-token.d.ts.map +1 -0
- package/dist/tenant-token.js +108 -0
- package/dist/tenant-token.js.map +1 -0
- package/dist/token-codec.d.ts +30 -0
- package/dist/token-codec.d.ts.map +1 -0
- package/dist/token-codec.js +63 -0
- package/dist/token-codec.js.map +1 -0
- package/dist/vertical-client.d.ts +42 -4
- package/dist/vertical-client.d.ts.map +1 -1
- package/dist/vertical-client.js +83 -4
- package/dist/vertical-client.js.map +1 -1
- package/package.json +6 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-deployment.d.ts","sourceRoot":"","sources":["../src/scope-deployment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAAG,gBAAgB,GAAG,eAAe,GAAG,MAAM,CAAC;AAE7E,wDAAwD;AACxD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,cAAc,CAAC;IACvB,GAAG,EAAE,kBAAkB,CAAC;CACzB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,kBAAkB,EACvB,KAAK,EAAE;IAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACvE,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,GACzC,MAAM,GAAG,IAAI,CAEf"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { runningVersionOf } from '@substrat-run/kernel';
|
|
2
|
+
/**
|
|
3
|
+
* The version the deployment chosen for `scope` runs — the one a provision receipt may
|
|
4
|
+
* name after a reconcile through it (#1653).
|
|
5
|
+
*
|
|
6
|
+
* - `serving-script`: what the vertical's serving script serves (`runningVersionOf`), or
|
|
7
|
+
* the bound version when the pointer names another script or could not be read.
|
|
8
|
+
* - `bound-version`: the bound version — that deployment IS it. This is where a serving
|
|
9
|
+
* ref that does not resolve falls to, and why the answer has to come from here: the
|
|
10
|
+
* hook that ran is the bound version's, and a receipt naming the served one would mark
|
|
11
|
+
* the scope repaired while the served version's hook never ran.
|
|
12
|
+
* - `slug`: the bound version. A static binding or a slug's deployment runs whatever it
|
|
13
|
+
* runs, and the platform cannot name it; this is the answer every receipt gave before
|
|
14
|
+
* #1653, kept so a scope reached only this way is not asked again on every pass.
|
|
15
|
+
*/
|
|
16
|
+
export function versionReachedAt(via, scope, serving) {
|
|
17
|
+
return via === 'serving-script' ? runningVersionOf(scope, serving) : scope.verticalVersionId;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=scope-deployment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-deployment.js","sourceRoot":"","sources":["../src/scope-deployment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAuB,MAAM,sBAAsB,CAAC;AAkB7E;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB,CAC9B,GAAuB,EACvB,KAAuE,EACvE,OAA0C;IAE1C,OAAO,GAAG,KAAK,gBAAgB,CAAC,CAAC,CAAC,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC;AAC/F,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ScopeId, type ScopeStatus, type StorageMeterReading, type TenantId } from '@substrat-run/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* The on-demand storage reading (#1524), without any HTTP around it.
|
|
4
|
+
*
|
|
5
|
+
* Every scope it reads is a Durable Object it wakes, so every limit here is a cost
|
|
6
|
+
* limit. The directory listing that supplies `scopes` is a cheap read and is taken
|
|
7
|
+
* whole, which keeps `total` honest. Only the wakes are bounded:
|
|
8
|
+
* - **per page**: at most `limit` scopes, capped at `STORAGE_PAGE_MAX`. A tenant with
|
|
9
|
+
* thousands of scopes is read a page at a time, and each page is asked for by a person.
|
|
10
|
+
* - **in flight**: at most `STORAGE_READ_CONCURRENCY` reads run at once within a page.
|
|
11
|
+
*/
|
|
12
|
+
export declare const STORAGE_PAGE_DEFAULT = 50;
|
|
13
|
+
export declare const STORAGE_PAGE_MAX = 200;
|
|
14
|
+
export declare const STORAGE_READ_CONCURRENCY = 8;
|
|
15
|
+
export interface StorageScope {
|
|
16
|
+
scopeId: ScopeId;
|
|
17
|
+
status: ScopeStatus;
|
|
18
|
+
}
|
|
19
|
+
export interface StoragePageInput<S extends StorageScope> {
|
|
20
|
+
tenantId: TenantId;
|
|
21
|
+
readAt: string;
|
|
22
|
+
/** Every scope row the tenant has, in any order. Reaped rows are counted, never read. */
|
|
23
|
+
scopes: readonly S[];
|
|
24
|
+
/** Resume after this scope id, the previous page's `nextCursor`. */
|
|
25
|
+
cursor?: ScopeId;
|
|
26
|
+
limit?: number;
|
|
27
|
+
concurrency?: number;
|
|
28
|
+
/** One scope's database size. A throw is recorded against that scope, never the page. */
|
|
29
|
+
read: (scope: S) => Promise<number>;
|
|
30
|
+
}
|
|
31
|
+
export declare function readStoragePage<S extends StorageScope>(input: StoragePageInput<S>): Promise<StorageMeterReading>;
|
|
32
|
+
//# sourceMappingURL=storage-meter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-meter.d.ts","sourceRoot":"","sources":["../src/storage-meter.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,OAAO,EACZ,KAAK,WAAW,EAEhB,KAAK,mBAAmB,EACxB,KAAK,QAAQ,EACd,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,YAAY;IACtD,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,yFAAyF;IACzF,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC;IACrB,oEAAoE;IACpE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yFAAyF;IACzF,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACrC;AAED,wBAAsB,eAAe,CAAC,CAAC,SAAS,YAAY,EAC1D,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,GACzB,OAAO,CAAC,mBAAmB,CAAC,CAoD9B"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { STORAGE_EXCLUSIONS, storageMeterReading, } from '@substrat-run/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* The on-demand storage reading (#1524), without any HTTP around it.
|
|
4
|
+
*
|
|
5
|
+
* Every scope it reads is a Durable Object it wakes, so every limit here is a cost
|
|
6
|
+
* limit. The directory listing that supplies `scopes` is a cheap read and is taken
|
|
7
|
+
* whole, which keeps `total` honest. Only the wakes are bounded:
|
|
8
|
+
* - **per page**: at most `limit` scopes, capped at `STORAGE_PAGE_MAX`. A tenant with
|
|
9
|
+
* thousands of scopes is read a page at a time, and each page is asked for by a person.
|
|
10
|
+
* - **in flight**: at most `STORAGE_READ_CONCURRENCY` reads run at once within a page.
|
|
11
|
+
*/
|
|
12
|
+
export const STORAGE_PAGE_DEFAULT = 50;
|
|
13
|
+
export const STORAGE_PAGE_MAX = 200;
|
|
14
|
+
export const STORAGE_READ_CONCURRENCY = 8;
|
|
15
|
+
export async function readStoragePage(input) {
|
|
16
|
+
const limit = Math.max(1, Math.min(Math.floor(input.limit ?? STORAGE_PAGE_DEFAULT), STORAGE_PAGE_MAX));
|
|
17
|
+
const concurrency = Math.max(1, Math.min(Math.floor(input.concurrency ?? STORAGE_READ_CONCURRENCY), STORAGE_READ_CONCURRENCY));
|
|
18
|
+
const readable = input.scopes
|
|
19
|
+
.filter((s) => s.status !== 'reaped')
|
|
20
|
+
.sort((a, b) => (a.scopeId < b.scopeId ? -1 : a.scopeId > b.scopeId ? 1 : 0));
|
|
21
|
+
const reaped = input.scopes.length - readable.length;
|
|
22
|
+
const after = input.cursor;
|
|
23
|
+
const remaining = after === undefined ? readable : readable.filter((s) => s.scopeId > after);
|
|
24
|
+
const page = remaining.slice(0, limit);
|
|
25
|
+
const nextCursor = remaining.length > page.length ? page[page.length - 1].scopeId : null;
|
|
26
|
+
const results = new Array(page.length);
|
|
27
|
+
let next = 0;
|
|
28
|
+
const worker = async () => {
|
|
29
|
+
for (let i = next++; i < page.length; i = next++) {
|
|
30
|
+
const scope = page[i];
|
|
31
|
+
try {
|
|
32
|
+
const bytes = await input.read(scope);
|
|
33
|
+
if (!Number.isInteger(bytes) || bytes < 0)
|
|
34
|
+
throw new Error(`not a size: ${String(bytes)}`);
|
|
35
|
+
results[i] = { scopeId: scope.scopeId, status: scope.status, bytes };
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
39
|
+
results[i] = { scopeId: scope.scopeId, status: scope.status, bytes: null, error: message || 'read failed' };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, page.length) }, worker));
|
|
44
|
+
let bytes = 0;
|
|
45
|
+
let failed = 0;
|
|
46
|
+
for (const r of results) {
|
|
47
|
+
if (r.bytes === null)
|
|
48
|
+
failed += 1;
|
|
49
|
+
else
|
|
50
|
+
bytes += r.bytes;
|
|
51
|
+
}
|
|
52
|
+
return storageMeterReading.parse({
|
|
53
|
+
tenantId: input.tenantId,
|
|
54
|
+
readAt: input.readAt,
|
|
55
|
+
basis: 'scope-databases',
|
|
56
|
+
excluded: [...STORAGE_EXCLUSIONS],
|
|
57
|
+
bytes,
|
|
58
|
+
scopes: results,
|
|
59
|
+
read: results.length - failed,
|
|
60
|
+
failed,
|
|
61
|
+
total: readable.length,
|
|
62
|
+
reaped,
|
|
63
|
+
nextCursor,
|
|
64
|
+
// The one field that licenses the word "total": this page is the whole tenant, and
|
|
65
|
+
// every read in it answered.
|
|
66
|
+
complete: after === undefined && nextCursor === null && failed === 0,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=storage-meter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-meter.js","sourceRoot":"","sources":["../src/storage-meter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type PlatformActorId, type TenantId } from '@substrat-run/contracts';
|
|
2
|
+
import { type TenantServiceAuth } from './auth.js';
|
|
3
|
+
interface TenantTokenClaim {
|
|
4
|
+
v: 1;
|
|
5
|
+
tenantId: string;
|
|
6
|
+
/** Informational — no expiry in v1, exactly as push tokens have none. */
|
|
7
|
+
iat: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Mint a tenant token.
|
|
11
|
+
*
|
|
12
|
+
* The tenant NEED NOT EXIST in the directory yet, and the claim deliberately carries
|
|
13
|
+
* no slug. Both follow from the same fact: a tenant token narrows WHOSE rows a caller
|
|
14
|
+
* may touch, and a tenant with no rows is narrowed to nothing. What such a credential
|
|
15
|
+
* can do is create the directory row for its own id — `POST /tenants` is body-pinned
|
|
16
|
+
* to the same tenant — which is precisely the sign-up bootstrap dashboard.md §4 names
|
|
17
|
+
* as the one act that cannot be tenant-narrowed, because there is no tenant yet.
|
|
18
|
+
* Requiring the row here would break it: the dashboard's first call for a new team IS
|
|
19
|
+
* `ensureTenant`, and a mint that 404s first makes that unreachable.
|
|
20
|
+
*
|
|
21
|
+
* A slug would also be a staleness hazard rather than a saving: the only thing that
|
|
22
|
+
* reads one is the builder's bare-slug prefixing (#417), and a tenant principal
|
|
23
|
+
* resolves a slug through the `x-substrat-tenant` header the plane already pins.
|
|
24
|
+
*/
|
|
25
|
+
export declare function mintTenantToken(secret: string, identity: {
|
|
26
|
+
tenantId: TenantId;
|
|
27
|
+
}): Promise<string>;
|
|
28
|
+
/** Verify a tenant token string → its claim, or null (bad prefix/shape/signature). */
|
|
29
|
+
export declare function verifyTenantToken(secret: string, token: string): Promise<TenantTokenClaim | null>;
|
|
30
|
+
/**
|
|
31
|
+
* A `TenantServiceAuth` over tenant tokens: reads `x-service-token`, handles only
|
|
32
|
+
* `stt1.…` values (anything else → null, so every other reader is untouched), verifies,
|
|
33
|
+
* and returns the tenant the token is pinned to.
|
|
34
|
+
*
|
|
35
|
+
* `actor` is the audited subject the HOST supplies — the same fixed service actor a
|
|
36
|
+
* platform service token already resolves to. It is NOT read from the token, for the
|
|
37
|
+
* reason the module docblock gives.
|
|
38
|
+
*/
|
|
39
|
+
export declare function tenantTokenAuth(secret: string, actor: PlatformActorId): TenantServiceAuth;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=tenant-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-token.d.ts","sourceRoot":"","sources":["../src/tenant-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,eAAe,EAAE,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAC1G,OAAO,EAAwB,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAmDzE,UAAU,gBAAgB;IACxB,CAAC,EAAE,CAAC,CAAC;IACL,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE;IAAE,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAGvG;AAED,sFAAsF;AACtF,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAavG;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,iBAAiB,CAQzF"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { tenantId as tenantIdSchema } from '@substrat-run/contracts';
|
|
2
|
+
import { SERVICE_TOKEN_HEADER } from './auth.js';
|
|
3
|
+
import { hasTokenPrefix, openToken, signToken } from './token-codec.js';
|
|
4
|
+
/**
|
|
5
|
+
* Tenant tokens — the dashboard's tenant-scoped service credential (#977).
|
|
6
|
+
*
|
|
7
|
+
* The dashboard reached the shared control plane over the platform `SERVICE_TOKEN`,
|
|
8
|
+
* which authenticates as `{ kind: 'staff' }` and has FLEET-WIDE reach. Its narrowing to
|
|
9
|
+
* one tenant was a constructor argument in the dashboard's own worker
|
|
10
|
+
* (`TenantNarrowedControlPlane`) plus a caller-asserted `x-substrat-tenant` header the
|
|
11
|
+
* plane read only for slug resolution. So the narrowing was the dashboard's promise
|
|
12
|
+
* about itself, not a property of the credential: one wrong tenant id at any of its
|
|
13
|
+
* ~90 call sites reached another customer's data with a staff-level token and nothing
|
|
14
|
+
* server-side to refuse it. That is the vulnerability this closes.
|
|
15
|
+
*
|
|
16
|
+
* A tenant token is the same stateless shape push tokens already establish
|
|
17
|
+
* (`push-token.ts`), with a different claim and a different meaning:
|
|
18
|
+
*
|
|
19
|
+
* `stt1.<b64url payload>.<b64url HMAC-SHA256 sig>`
|
|
20
|
+
*
|
|
21
|
+
* It authenticates as `{ kind: 'tenant' }` — staff-equivalent CAPABILITY, confined to
|
|
22
|
+
* ONE tenant, which is what a dashboard actually needs. The confinement is enforced by
|
|
23
|
+
* the control plane (`createControlPlaneApi`'s allowlist + pin middleware), so a
|
|
24
|
+
* dashboard bug cannot spend it outside its tenant however it asks.
|
|
25
|
+
*
|
|
26
|
+
* **The claim carries no actor, deliberately.** Who a row is audited as stays the
|
|
27
|
+
* host's decision (`tenantTokenAuth(secret, actor)`, exactly as `serviceTokenAuth`
|
|
28
|
+
* takes it), so a minted token can never name its own audit subject — and the audit
|
|
29
|
+
* row keeps naming what it named before this existed. Making the audit row carry the
|
|
30
|
+
* customer's own `PrincipalId` is the SECOND half of #977 and is deliberately not
|
|
31
|
+
* here: it makes `PlatformActorId` stop being the type of an audit actor, which is
|
|
32
|
+
* kernel-facing and gets its own diff and its own review.
|
|
33
|
+
*
|
|
34
|
+
* **A dedicated secret**, never `pushTokenSecret` and never `PLATFORM_SECRET`. A
|
|
35
|
+
* shared signing key would make a compromised CI push token and a compromised
|
|
36
|
+
* dashboard credential the same incident: whoever holds the key mints either class,
|
|
37
|
+
* and rotating to contain one revokes the other. `PLATFORM_SECRET` is worse still —
|
|
38
|
+
* it is injected into every pushed vertical, so any customer's code could mint a
|
|
39
|
+
* credential for any tenant. Rotating the dedicated secret invalidates every issued
|
|
40
|
+
* tenant token at once (the dashboard re-mints on its next request, so that is a blip
|
|
41
|
+
* rather than an outage) — which is the whole revocation story in v1.
|
|
42
|
+
*
|
|
43
|
+
* Presented in the SAME `x-service-token` header, discriminated by the `stt1.` prefix.
|
|
44
|
+
* A collision is not a risk to reason about: the platform service token is random hex
|
|
45
|
+
* and a push token starts `spt1.`, so no other credential this plane accepts can begin
|
|
46
|
+
* `stt1.`, and the prefix is inside the signed input (`token-codec.ts`) so a push
|
|
47
|
+
* token's payload cannot be replayed as a tenant token even under an equal secret.
|
|
48
|
+
*/
|
|
49
|
+
const PREFIX = 'stt1';
|
|
50
|
+
/**
|
|
51
|
+
* Mint a tenant token.
|
|
52
|
+
*
|
|
53
|
+
* The tenant NEED NOT EXIST in the directory yet, and the claim deliberately carries
|
|
54
|
+
* no slug. Both follow from the same fact: a tenant token narrows WHOSE rows a caller
|
|
55
|
+
* may touch, and a tenant with no rows is narrowed to nothing. What such a credential
|
|
56
|
+
* can do is create the directory row for its own id — `POST /tenants` is body-pinned
|
|
57
|
+
* to the same tenant — which is precisely the sign-up bootstrap dashboard.md §4 names
|
|
58
|
+
* as the one act that cannot be tenant-narrowed, because there is no tenant yet.
|
|
59
|
+
* Requiring the row here would break it: the dashboard's first call for a new team IS
|
|
60
|
+
* `ensureTenant`, and a mint that 404s first makes that unreachable.
|
|
61
|
+
*
|
|
62
|
+
* A slug would also be a staleness hazard rather than a saving: the only thing that
|
|
63
|
+
* reads one is the builder's bare-slug prefixing (#417), and a tenant principal
|
|
64
|
+
* resolves a slug through the `x-substrat-tenant` header the plane already pins.
|
|
65
|
+
*/
|
|
66
|
+
export async function mintTenantToken(secret, identity) {
|
|
67
|
+
const claim = { v: 1, tenantId: identity.tenantId, iat: Date.now() };
|
|
68
|
+
return signToken(PREFIX, secret, claim);
|
|
69
|
+
}
|
|
70
|
+
/** Verify a tenant token string → its claim, or null (bad prefix/shape/signature). */
|
|
71
|
+
export async function verifyTenantToken(secret, token) {
|
|
72
|
+
const opened = await openToken(PREFIX, secret, token);
|
|
73
|
+
if (opened === null)
|
|
74
|
+
return null;
|
|
75
|
+
try {
|
|
76
|
+
const claim = opened;
|
|
77
|
+
if (claim.v !== 1)
|
|
78
|
+
return null;
|
|
79
|
+
// Parse, don't trust: the signature proves WE minted it, the parse proves the
|
|
80
|
+
// fields are still the ones a pin is made of (a format bump fails closed).
|
|
81
|
+
tenantIdSchema.parse(claim.tenantId);
|
|
82
|
+
return claim;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* A `TenantServiceAuth` over tenant tokens: reads `x-service-token`, handles only
|
|
90
|
+
* `stt1.…` values (anything else → null, so every other reader is untouched), verifies,
|
|
91
|
+
* and returns the tenant the token is pinned to.
|
|
92
|
+
*
|
|
93
|
+
* `actor` is the audited subject the HOST supplies — the same fixed service actor a
|
|
94
|
+
* platform service token already resolves to. It is NOT read from the token, for the
|
|
95
|
+
* reason the module docblock gives.
|
|
96
|
+
*/
|
|
97
|
+
export function tenantTokenAuth(secret, actor) {
|
|
98
|
+
return async (request) => {
|
|
99
|
+
const presented = request.headers.get(SERVICE_TOKEN_HEADER);
|
|
100
|
+
if (!presented || !hasTokenPrefix(PREFIX, presented))
|
|
101
|
+
return null;
|
|
102
|
+
const claim = await verifyTenantToken(secret, presented);
|
|
103
|
+
if (!claim)
|
|
104
|
+
return null;
|
|
105
|
+
return { actor, tenantId: tenantIdSchema.parse(claim.tenantId) };
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=tenant-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-token.js","sourceRoot":"","sources":["../src/tenant-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,cAAc,EAAuC,MAAM,yBAAyB,CAAC;AAC1G,OAAO,EAAE,oBAAoB,EAA0B,MAAM,WAAW,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,MAAM,MAAM,GAAG,MAAM,CAAC;AAStB;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAc,EAAE,QAAgC;IACpF,MAAM,KAAK,GAAqB,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IACvF,OAAO,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;AAC1C,CAAC;AAED,sFAAsF;AACtF,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAc,EAAE,KAAa;IACnE,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IACtD,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACjC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAA0B,CAAC;QACzC,IAAI,KAAK,CAAC,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAC/B,8EAA8E;QAC9E,2EAA2E;QAC3E,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACrC,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,KAAsB;IACpE,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;QACvB,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAC5D,IAAI,CAAC,SAAS,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,SAAS,CAAC;YAAE,OAAO,IAAI,CAAC;QAClE,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACzD,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;IACnE,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The wire codec the platform's stateless HMAC credentials share — push tokens
|
|
3
|
+
* (`push-token.ts`) and tenant tokens (`tenant-token.ts`).
|
|
4
|
+
*
|
|
5
|
+
* Shape: `<prefix>.<b64url payload>.<b64url HMAC-SHA256 sig>`, verified against a
|
|
6
|
+
* secret the holder never sees. One implementation rather than one per credential,
|
|
7
|
+
* because the two must not drift: a verification that differs between them is a
|
|
8
|
+
* difference neither credential's own suite would notice, and the whole point of the
|
|
9
|
+
* prefix is that a reader discriminates WITHOUT trying every secret it holds.
|
|
10
|
+
*
|
|
11
|
+
* The prefix is inside the signed input, never only in front of it. So a payload
|
|
12
|
+
* minted under one prefix cannot be replayed under another even where two deployments
|
|
13
|
+
* happen to configure the same secret for both — the signature is over the pair.
|
|
14
|
+
*
|
|
15
|
+
* Web Crypto only (`globalThis.crypto`): the same API in Node, workerd and a browser,
|
|
16
|
+
* which is what lets this live in a package both a Worker and a Node host import.
|
|
17
|
+
*/
|
|
18
|
+
/** Does this presented value claim to be a `<prefix>.…` token at all? */
|
|
19
|
+
export declare function hasTokenPrefix(prefix: string, presented: string): boolean;
|
|
20
|
+
/** Sign a claim into a `<prefix>.<payload>.<sig>` token. */
|
|
21
|
+
export declare function signToken(prefix: string, secret: string, claim: unknown): Promise<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Verify a token's prefix, shape and signature → its decoded payload, or null.
|
|
24
|
+
*
|
|
25
|
+
* The payload comes back as `unknown` deliberately: the signature proves WE minted it,
|
|
26
|
+
* and nothing more. Whether the fields are still the ones the caller needs is the
|
|
27
|
+
* caller's own parse — a format bump must fail closed, not arrive half-typed.
|
|
28
|
+
*/
|
|
29
|
+
export declare function openToken(prefix: string, secret: string, token: string): Promise<unknown | null>;
|
|
30
|
+
//# sourceMappingURL=token-codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-codec.d.ts","sourceRoot":"","sources":["../src/token-codec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAcH,yEAAyE;AACzE,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAEzE;AAED,4DAA4D;AAC5D,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAO/F;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAqBtG"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The wire codec the platform's stateless HMAC credentials share — push tokens
|
|
3
|
+
* (`push-token.ts`) and tenant tokens (`tenant-token.ts`).
|
|
4
|
+
*
|
|
5
|
+
* Shape: `<prefix>.<b64url payload>.<b64url HMAC-SHA256 sig>`, verified against a
|
|
6
|
+
* secret the holder never sees. One implementation rather than one per credential,
|
|
7
|
+
* because the two must not drift: a verification that differs between them is a
|
|
8
|
+
* difference neither credential's own suite would notice, and the whole point of the
|
|
9
|
+
* prefix is that a reader discriminates WITHOUT trying every secret it holds.
|
|
10
|
+
*
|
|
11
|
+
* The prefix is inside the signed input, never only in front of it. So a payload
|
|
12
|
+
* minted under one prefix cannot be replayed under another even where two deployments
|
|
13
|
+
* happen to configure the same secret for both — the signature is over the pair.
|
|
14
|
+
*
|
|
15
|
+
* Web Crypto only (`globalThis.crypto`): the same API in Node, workerd and a browser,
|
|
16
|
+
* which is what lets this live in a package both a Worker and a Node host import.
|
|
17
|
+
*/
|
|
18
|
+
const enc = new TextEncoder();
|
|
19
|
+
const b64url = (bytes) => btoa(String.fromCharCode(...bytes)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
20
|
+
const b64urlToBytes = (s) => Uint8Array.from(atob(s.replace(/-/g, '+').replace(/_/g, '/')), (c) => c.charCodeAt(0));
|
|
21
|
+
async function hmacKey(secret, usage) {
|
|
22
|
+
return crypto.subtle.importKey('raw', enc.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, [usage]);
|
|
23
|
+
}
|
|
24
|
+
/** Does this presented value claim to be a `<prefix>.…` token at all? */
|
|
25
|
+
export function hasTokenPrefix(prefix, presented) {
|
|
26
|
+
return presented.startsWith(`${prefix}.`);
|
|
27
|
+
}
|
|
28
|
+
/** Sign a claim into a `<prefix>.<payload>.<sig>` token. */
|
|
29
|
+
export async function signToken(prefix, secret, claim) {
|
|
30
|
+
const payload = b64url(enc.encode(JSON.stringify(claim)));
|
|
31
|
+
const signingInput = `${prefix}.${payload}`;
|
|
32
|
+
const sig = new Uint8Array(await crypto.subtle.sign('HMAC', await hmacKey(secret, 'sign'), enc.encode(signingInput)));
|
|
33
|
+
return `${signingInput}.${b64url(sig)}`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Verify a token's prefix, shape and signature → its decoded payload, or null.
|
|
37
|
+
*
|
|
38
|
+
* The payload comes back as `unknown` deliberately: the signature proves WE minted it,
|
|
39
|
+
* and nothing more. Whether the fields are still the ones the caller needs is the
|
|
40
|
+
* caller's own parse — a format bump must fail closed, not arrive half-typed.
|
|
41
|
+
*/
|
|
42
|
+
export async function openToken(prefix, secret, token) {
|
|
43
|
+
const parts = token.split('.');
|
|
44
|
+
if (parts.length !== 3 || parts[0] !== prefix)
|
|
45
|
+
return null;
|
|
46
|
+
const [, payload, sig] = parts;
|
|
47
|
+
let ok = false;
|
|
48
|
+
try {
|
|
49
|
+
ok = await crypto.subtle.verify('HMAC', await hmacKey(secret, 'verify'), b64urlToBytes(sig), enc.encode(`${prefix}.${payload}`));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (!ok)
|
|
55
|
+
return null;
|
|
56
|
+
try {
|
|
57
|
+
return JSON.parse(new TextDecoder().decode(b64urlToBytes(payload)));
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=token-codec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-codec.js","sourceRoot":"","sources":["../src/token-codec.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AttachmentRecord, ConnectionId,
|
|
2
|
-
import type { OpenedAttachment } from '@substrat-run/kernel';
|
|
1
|
+
import type { AttachmentRecord, ConnectionId, EntityRef, EntitlementGrant, ModuleId, PermissionKey, PlatformRequest, PlatformRequestFilter, PlatformRequestId, PlatformRequestStatus, PlatformRequestFailure, PrincipalId, SystemSwitchOutcome, ConnectionGrantRecord, ProjectedConnectionGrant, ProjectedConnectionKey, ProjectedIdentityLink, OwnerSeat, OwnerClaimLink, QueryScopeInput, ReadScopeTableInput, EntityHistoryInput, EventFacetInput, EventCauseInput, EventEffectsInput, EffectsTree, InvocationEventsInput, InvocationEvents, DeadLettersInput, DeadLetter, CauseChain, EventFacetResult, HistoryEntry, Page, ScopeDumpTable, ScopeId, ScopeQueryResult, DenialFilter, DenialSummary, PermissionDenial, ScopeTable, ScopeTablePage, TenantId, TenantStoreHandle, Visibility } from '@substrat-run/contracts';
|
|
2
|
+
import type { OpenedAttachment, UndrainedEvents } from '@substrat-run/kernel';
|
|
3
3
|
/**
|
|
4
4
|
* The platform's client for calling a VERTICAL (K-31).
|
|
5
5
|
*
|
|
@@ -334,6 +334,27 @@ export declare class VerticalClient {
|
|
|
334
334
|
permission: PermissionKey;
|
|
335
335
|
expiresAt?: string;
|
|
336
336
|
}): Promise<void>;
|
|
337
|
+
/**
|
|
338
|
+
* Move one module's schedule kill switch in the deployment serving the scope (#1666) —
|
|
339
|
+
* the far end of `revokeFromSystem` / `restoreToSystem` for a hosted scope, whose
|
|
340
|
+
* `system:<module>` grants live there and nowhere the platform can reach.
|
|
341
|
+
*
|
|
342
|
+
* ONE shape is normalized to "redeploy the vertical", and only because it is the
|
|
343
|
+
* deployment's own proof that it cannot have acted: a script built before the route
|
|
344
|
+
* answers a **404** (the path does not exist — the route itself answers a module it holds
|
|
345
|
+
* nothing for with a 200 `held: false`, never a 404) or its **SPA shell** (a 200 that is
|
|
346
|
+
* not JSON). "Nothing was switched" is true of exactly those.
|
|
347
|
+
*
|
|
348
|
+
* Everything else surfaces as the failure it is, and never claims that: a transport
|
|
349
|
+
* failure (`reach` → 502 "unreachable"), a genuine 5xx from the far end, and a 200 JSON of
|
|
350
|
+
* the wrong shape. The request may have landed and the switch may have moved before the
|
|
351
|
+
* answer was lost, so the only honest instruction is to confirm the position first.
|
|
352
|
+
*/
|
|
353
|
+
systemSwitch(input: {
|
|
354
|
+
scopeId: ScopeId;
|
|
355
|
+
moduleId: ModuleId;
|
|
356
|
+
to: 'on' | 'off';
|
|
357
|
+
}): Promise<SystemSwitchOutcome>;
|
|
337
358
|
/** Journal a platform-request outcome back in the vertical after the platform ran it. */
|
|
338
359
|
settlePlatformRequest(tenantId: TenantId, scopeId: ScopeId, id: PlatformRequestId, outcome: {
|
|
339
360
|
status: PlatformRequestStatus;
|
|
@@ -398,8 +419,12 @@ export declare class VerticalClient {
|
|
|
398
419
|
* The Tier-2 drain's read (#1334): the oldest not-yet-drained events of a scope this
|
|
399
420
|
* deployment serves. Scope bytes cross, on their way to the lake; the control plane's
|
|
400
421
|
* `readUndrainedEvents` is the audited door this stands behind.
|
|
422
|
+
*
|
|
423
|
+
* Asks for what the read stepped over too (#1636, `withSkipped=1`), and takes either
|
|
424
|
+
* answer: a vertical deployed before that parameter ignores it and sends the bare array,
|
|
425
|
+
* which reads as a read that said nothing about skips — never as one that skipped none.
|
|
401
426
|
*/
|
|
402
|
-
undrainedEvents(scopeId: ScopeId, limit: number): Promise<
|
|
427
|
+
undrainedEvents(scopeId: ScopeId, limit: number): Promise<UndrainedEvents>;
|
|
403
428
|
/**
|
|
404
429
|
* The Tier-2 drain's stamp (#1334), after the sink confirmed the batch. Answers how
|
|
405
430
|
* many rows changed, which is what lets a retried pass record no egress it did not
|
|
@@ -409,8 +434,15 @@ export declare class VerticalClient {
|
|
|
409
434
|
/**
|
|
410
435
|
* Reopen rows stamped before `drainedBefore` so the drain ships them again (#1334).
|
|
411
436
|
* Answers how many changed, which is what the platform's receipt is written from.
|
|
437
|
+
*
|
|
438
|
+
* `countOnly` (#1545) asks the same window for a number and changes nothing — and it
|
|
439
|
+
* takes a DIFFERENT path rather than riding as a field on this one. The deployment on
|
|
440
|
+
* the far end is the tenant's, deployed on its own clock, so it is routinely older than
|
|
441
|
+
* this client: an unknown field is stripped by the Zod boundary there, and the reopen
|
|
442
|
+
* would run and answer with a number that looks exactly like the count. A path that
|
|
443
|
+
* deployment does not serve answers 404, which throws here, before anything moves.
|
|
412
444
|
*/
|
|
413
|
-
redrainEvents(scopeId: ScopeId, drainedBefore: string): Promise<number>;
|
|
445
|
+
redrainEvents(scopeId: ScopeId, drainedBefore: string, countOnly?: boolean): Promise<number>;
|
|
414
446
|
/**
|
|
415
447
|
* When one scope's migrations actually ran (#1236) — the schema-change
|
|
416
448
|
* annotation release health reads. Metadata only; no scope bytes cross.
|
|
@@ -420,6 +452,12 @@ export declare class VerticalClient {
|
|
|
420
452
|
version: string;
|
|
421
453
|
appliedAt: string | null;
|
|
422
454
|
}[]>;
|
|
455
|
+
/**
|
|
456
|
+
* One scope's database size in bytes (#1524), for the on-demand storage reading.
|
|
457
|
+
* Metadata only. A 200 without a number is a wire disagreement and throws, because a
|
|
458
|
+
* missing size read as 0 would sit inside a sum and look like a real, small scope.
|
|
459
|
+
*/
|
|
460
|
+
databaseSize(scopeId: ScopeId): Promise<number>;
|
|
423
461
|
/**
|
|
424
462
|
* The PITR bookmarks one scope recorded before its migration passes (#286) —
|
|
425
463
|
* the rewind points the deployments UI offers for a backout. Metadata only;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vertical-client.d.ts","sourceRoot":"","sources":["../src/vertical-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,
|
|
1
|
+
{"version":3,"file":"vertical-client.d.ts","sourceRoot":"","sources":["../src/vertical-client.ts"],"names":[],"mappings":"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"}
|