@nylorun/runtime 0.13.0-beta → 0.15.0-beta
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/CHANGELOG.md +434 -0
- package/README.md +137 -41
- package/dist/adapters/execution/restate.d.ts +7 -2
- package/dist/adapters/execution/restate.js +40 -10
- package/dist/adapters/replication/pgoutput.d.ts +21 -0
- package/dist/adapters/replication/pgoutput.js +170 -0
- package/dist/adapters/streams/s2.js +37 -20
- package/dist/admin-openapi.json +570 -0
- package/dist/api/a2a/card.d.ts +15 -0
- package/dist/api/a2a/card.js +24 -0
- package/dist/api/a2a/endpoint.d.ts +8 -0
- package/dist/api/a2a/endpoint.js +81 -0
- package/dist/api/a2a/protocol.d.ts +144 -0
- package/dist/api/a2a/protocol.js +188 -0
- package/dist/api/a2a/routes.d.ts +19 -0
- package/dist/api/a2a/routes.js +289 -0
- package/dist/api/a2a/tasks.d.ts +50 -0
- package/dist/api/a2a/tasks.js +186 -0
- package/dist/api/ag-ui/endpoint.d.ts +8 -0
- package/dist/api/ag-ui/endpoint.js +126 -0
- package/dist/api/ag-ui/history.d.ts +7 -0
- package/dist/api/ag-ui/history.js +60 -0
- package/dist/api/ag-ui/routes.d.ts +32 -0
- package/dist/api/ag-ui/routes.js +305 -0
- package/dist/api/ag-ui/session-id.d.ts +1 -0
- package/dist/api/ag-ui/session-id.js +12 -0
- package/dist/api/ag-ui/sse.d.ts +9 -0
- package/dist/api/ag-ui/sse.js +7 -0
- package/dist/api/ag-ui/translate.d.ts +38 -0
- package/dist/api/ag-ui/translate.js +222 -0
- package/dist/api/components.d.ts +8065 -0
- package/dist/api/components.js +82 -0
- package/dist/api/gate/keys.d.ts +14 -0
- package/dist/api/gate/keys.js +59 -0
- package/dist/api/gate/routes.d.ts +34 -0
- package/dist/api/gate/routes.js +235 -0
- package/dist/api/http/app.d.ts +18 -0
- package/dist/api/http/app.js +58 -0
- package/dist/api/http/body.d.ts +4 -0
- package/dist/api/http/body.js +37 -0
- package/dist/api/http/define.d.ts +39 -0
- package/dist/api/http/define.js +122 -0
- package/dist/api/http/respond.d.ts +9 -0
- package/dist/api/http/respond.js +44 -0
- package/dist/api/http/routes/access.d.ts +24 -0
- package/dist/api/http/routes/access.js +266 -0
- package/dist/api/http/routes/actions.d.ts +8 -0
- package/dist/api/http/routes/actions.js +76 -0
- package/dist/api/http/routes/endpoints.d.ts +7 -0
- package/dist/api/http/routes/endpoints.js +100 -0
- package/dist/api/http/routes/sessions.d.ts +3 -0
- package/dist/api/http/routes/sessions.js +205 -0
- package/dist/api/http/routes/tenant.d.ts +11 -0
- package/dist/api/http/routes/tenant.js +244 -0
- package/dist/api/http/routes/vaults.d.ts +8 -0
- package/dist/api/http/routes/vaults.js +167 -0
- package/dist/api/openapi.d.ts +19 -0
- package/dist/api/openapi.js +137 -0
- package/dist/api/route.d.ts +12 -0
- package/dist/api/route.js +4 -0
- package/dist/core/bearer.d.ts +8 -0
- package/dist/core/bearer.js +11 -0
- package/dist/core/flow-host.d.ts +7 -15
- package/dist/core/flow-host.js +16 -41
- package/dist/core/sandbox-routes.d.ts +12 -7
- package/dist/core/sandbox-routes.js +24 -25
- package/dist/execution/memory.d.ts +8 -5
- package/dist/execution/memory.js +33 -13
- package/dist/execution/types.d.ts +38 -6
- package/dist/execution/types.js +12 -3
- package/dist/gates/contract.d.ts +51 -0
- package/dist/gates/contract.js +37 -0
- package/dist/gates/handler.d.ts +21 -0
- package/dist/gates/handler.js +55 -0
- package/dist/gates/http-client.d.ts +12 -0
- package/dist/gates/http-client.js +160 -0
- package/dist/gates/in-process.d.ts +32 -0
- package/dist/gates/in-process.js +42 -0
- package/dist/gates/inflight.d.ts +39 -0
- package/dist/gates/inflight.js +70 -0
- package/dist/gates/mcp-handler.d.ts +35 -0
- package/dist/gates/mcp-handler.js +142 -0
- package/dist/gates/meter.d.ts +24 -0
- package/dist/gates/meter.js +165 -0
- package/dist/gates/model-gate.d.ts +45 -0
- package/dist/gates/model-gate.js +1 -0
- package/dist/gates/tenant-vaults.d.ts +64 -0
- package/dist/gates/tenant-vaults.js +94 -0
- package/dist/gates/tool-calls.d.ts +51 -0
- package/dist/gates/tool-calls.js +64 -0
- package/dist/gates/tool-client.d.ts +12 -0
- package/dist/gates/tool-client.js +201 -0
- package/dist/gates/tool-contract.d.ts +116 -0
- package/dist/gates/tool-contract.js +64 -0
- package/dist/gates/tool-gate.d.ts +45 -0
- package/dist/gates/tool-gate.js +10 -0
- package/dist/host/admin-api.d.ts +23 -0
- package/dist/host/admin-api.js +74 -0
- package/dist/host/app.d.ts +34 -0
- package/dist/host/app.js +181 -0
- package/dist/host/config-for.d.ts +3 -1
- package/dist/host/config-for.js +3 -2
- package/dist/host/config.d.ts +11 -0
- package/dist/host/cors.d.ts +27 -0
- package/dist/host/cors.js +64 -0
- package/dist/host/create-host.d.ts +36 -8
- package/dist/host/create-host.js +161 -330
- package/dist/host/execution.d.ts +20 -18
- package/dist/host/execution.js +2 -2
- package/dist/host/gates.d.ts +58 -0
- package/dist/host/gates.js +127 -0
- package/dist/host/http.d.ts +20 -4
- package/dist/host/http.js +81 -23
- package/dist/host/index.d.ts +1 -1
- package/dist/host/main.js +189 -58
- package/dist/host/stack-config.d.ts +91 -19
- package/dist/host/stack-config.js +246 -17
- package/dist/infra/database.d.ts +2 -2
- package/dist/infra/execution.d.ts +5 -5
- package/dist/infra/execution.js +8 -6
- package/dist/infra/index.d.ts +1 -1
- package/dist/infra/workers.d.ts +17 -17
- package/dist/infra/workers.js +9 -9
- package/dist/keys/client.d.ts +9 -0
- package/dist/keys/client.js +70 -0
- package/dist/keys/contract.d.ts +24 -0
- package/dist/keys/contract.js +12 -0
- package/dist/keys/keys.d.ts +41 -0
- package/dist/keys/keys.js +50 -0
- package/dist/mcp/connect.d.ts +43 -3
- package/dist/mcp/connect.js +12 -5
- package/dist/mcp/pool.d.ts +39 -3
- package/dist/mcp/pool.js +74 -11
- package/dist/model/catalog.d.ts +2 -10
- package/dist/model/classify.d.ts +13 -0
- package/dist/model/classify.js +61 -0
- package/dist/model/models.d.ts +10 -1
- package/dist/model/models.js +14 -6
- package/dist/model/pi-model.d.ts +17 -2
- package/dist/model/pi-model.js +366 -222
- package/dist/openapi.json +25889 -0
- package/dist/record/append.d.ts +51 -0
- package/dist/record/append.js +20 -0
- package/dist/record/envelope.d.ts +19 -0
- package/dist/record/envelope.js +49 -0
- package/dist/record/index.d.ts +11 -0
- package/dist/record/index.js +11 -0
- package/dist/sandbox/manager.d.ts +13 -1
- package/dist/sandbox/manager.js +8 -11
- package/dist/sandbox/resolve.d.ts +33 -0
- package/dist/sandbox/resolve.js +81 -0
- package/dist/sandbox/session-sandbox.d.ts +11 -0
- package/dist/sandbox/session-sandbox.js +51 -0
- package/dist/sandbox/share.d.ts +14 -2
- package/dist/sandbox/share.js +33 -3
- package/dist/sandbox/tenant-config.d.ts +37 -0
- package/dist/sandbox/tenant-config.js +51 -0
- package/dist/sandbox/tools.d.ts +7 -9
- package/dist/sandbox/tools.js +11 -3
- package/dist/sandbox/types.d.ts +8 -2
- package/dist/store/postgres/connect.d.ts +25 -9
- package/dist/store/postgres/connect.js +27 -10
- package/dist/store/postgres/db.d.ts +16 -0
- package/dist/store/postgres/db.js +28 -0
- package/dist/store/postgres/drizzle/0000_foundation.sql +22 -0
- package/dist/store/postgres/drizzle/0001_baseline.sql +255 -0
- package/dist/store/postgres/drizzle/0002_stream_relay.sql +4 -0
- package/dist/store/postgres/drizzle/0003_tool_crossings.sql +9 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +34 -0
- package/dist/store/postgres/migrate.d.ts +68 -0
- package/dist/store/postgres/migrate.js +183 -0
- package/dist/store/postgres/record-writer.d.ts +4 -0
- package/dist/store/postgres/record-writer.js +34 -0
- package/dist/store/postgres/record.d.ts +5 -0
- package/dist/store/postgres/record.js +60 -0
- package/dist/store/postgres/schema.d.ts +3096 -0
- package/dist/store/postgres/schema.js +363 -0
- package/dist/store/postgres/store.d.ts +2 -4
- package/dist/store/postgres/store.js +590 -434
- package/dist/store/postgres/tenant.d.ts +56 -0
- package/dist/store/postgres/tenant.js +154 -0
- package/dist/store/types.d.ts +183 -132
- package/dist/store/types.js +0 -1
- package/dist/streams/basin.d.ts +18 -1
- package/dist/streams/basin.js +34 -2
- package/dist/streams/control.d.ts +18 -0
- package/dist/streams/control.js +33 -0
- package/dist/streams/memory.d.ts +3 -1
- package/dist/streams/memory.js +14 -8
- package/dist/streams/relay/core.d.ts +35 -0
- package/dist/streams/relay/core.js +366 -0
- package/dist/streams/relay/types.d.ts +78 -0
- package/dist/streams/relay/types.js +8 -0
- package/dist/streams/types.d.ts +71 -36
- package/dist/streams/types.js +18 -53
- package/dist/tenant/access-policy.d.ts +30 -0
- package/dist/tenant/access-policy.js +52 -0
- package/dist/tenant/actions.d.ts +10 -149
- package/dist/tenant/actions.js +5 -167
- package/dist/tenant/advance.d.ts +7 -2
- package/dist/tenant/advance.js +125 -14
- package/dist/tenant/auth.d.ts +19 -17
- package/dist/tenant/auth.js +90 -93
- package/dist/tenant/browser.d.ts +15 -0
- package/dist/tenant/browser.js +39 -0
- package/dist/tenant/cause.d.ts +25 -0
- package/dist/tenant/cause.js +45 -0
- package/dist/tenant/commands.d.ts +18 -2
- package/dist/tenant/commands.js +111 -115
- package/dist/tenant/context.d.ts +101 -21
- package/dist/tenant/context.js +14 -10
- package/dist/tenant/delivery-token.d.ts +41 -0
- package/dist/tenant/delivery-token.js +104 -0
- package/dist/tenant/delivery.d.ts +62 -0
- package/dist/tenant/delivery.js +481 -0
- package/dist/tenant/effects.d.ts +20 -1
- package/dist/tenant/effects.js +159 -42
- package/dist/tenant/endpoints.d.ts +67 -0
- package/dist/tenant/endpoints.js +90 -0
- package/dist/tenant/ephemeral.d.ts +22 -7
- package/dist/tenant/ephemeral.js +83 -125
- package/dist/tenant/history.d.ts +64 -0
- package/dist/tenant/history.js +120 -0
- package/dist/tenant/http.d.ts +8 -9
- package/dist/tenant/http.js +13 -27
- package/dist/tenant/jwt.d.ts +16 -0
- package/dist/tenant/jwt.js +50 -0
- package/dist/tenant/model-setting.d.ts +2 -2
- package/dist/tenant/module.d.ts +17 -28
- package/dist/tenant/module.js +133 -279
- package/dist/tenant/outbound.d.ts +44 -0
- package/dist/tenant/outbound.js +127 -0
- package/dist/tenant/paths.d.ts +11 -16
- package/dist/tenant/paths.js +24 -68
- package/dist/tenant/pool.d.ts +1 -3
- package/dist/tenant/pool.js +1 -19
- package/dist/tenant/principals.d.ts +23 -11
- package/dist/tenant/principals.js +61 -49
- package/dist/tenant/reset.d.ts +0 -4
- package/dist/tenant/reset.js +0 -5
- package/dist/tenant/runtime.d.ts +28 -20
- package/dist/tenant/runtime.js +95 -64
- package/dist/tenant/scheduler.d.ts +10 -3
- package/dist/tenant/scheduler.js +9 -5
- package/dist/tenant/session-streams.d.ts +127 -0
- package/dist/tenant/session-streams.js +404 -0
- package/dist/tenant/sessions.d.ts +28 -4
- package/dist/tenant/sessions.js +118 -24
- package/dist/tenant/signing-keys.d.ts +55 -0
- package/dist/tenant/signing-keys.js +172 -0
- package/dist/tenant/slim.d.ts +8 -0
- package/dist/tenant/slim.js +21 -0
- package/dist/tenant/status.d.ts +3 -3
- package/dist/tenant/status.js +20 -13
- package/dist/tenant/store-pg.d.ts +9 -6
- package/dist/tenant/store-pg.js +60 -125
- package/dist/tenant/streams.d.ts +62 -85
- package/dist/tenant/streams.js +143 -226
- package/dist/tenant/subject-limits.d.ts +16 -0
- package/dist/tenant/subject-limits.js +45 -0
- package/dist/tenant/sweep.d.ts +1 -10
- package/dist/tenant/sweep.js +29 -69
- package/dist/tenant/tokens.d.ts +14 -0
- package/dist/tenant/tokens.js +180 -0
- package/dist/tenant/transcript.d.ts +48 -5
- package/dist/tenant/transcript.js +58 -1
- package/dist/tenant/types.d.ts +69 -98
- package/dist/tenant/types.js +4 -15
- package/dist/tenant/worker.d.ts +3 -1
- package/dist/tenant/worker.js +13 -7
- package/dist/vault/host-model.d.ts +31 -0
- package/dist/vault/host-model.js +57 -0
- package/dist/vault/service.d.ts +44 -17
- package/dist/vault/service.js +126 -121
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +25 -9
- package/dist/core/executors.d.ts +0 -44
- package/dist/core/executors.js +0 -95
- package/dist/store/memory.d.ts +0 -57
- package/dist/store/memory.js +0 -645
- package/dist/store/postgres/migrations/001_initial.d.ts +0 -27
- package/dist/store/postgres/migrations/001_initial.js +0 -196
- package/dist/store/postgres/migrations/002_session_owner.d.ts +0 -6
- package/dist/store/postgres/migrations/002_session_owner.js +0 -13
- package/dist/store/postgres/migrations/index.d.ts +0 -49
- package/dist/store/postgres/migrations/index.js +0 -76
- package/dist/store/postgres/names.d.ts +0 -11
- package/dist/store/postgres/names.js +0 -42
- package/dist/store/postgres/tenants.d.ts +0 -90
- package/dist/store/postgres/tenants.js +0 -214
- package/dist/streams/relay.d.ts +0 -37
- package/dist/streams/relay.js +0 -254
- package/dist/tenant/envelope.d.ts +0 -7
- package/dist/tenant/envelope.js +0 -22
- package/dist/tenant/live.d.ts +0 -92
- package/dist/tenant/live.js +0 -290
- package/dist/tenant/quarantine-error.d.ts +0 -6
- package/dist/tenant/quarantine-error.js +0 -9
- package/dist/tenant/quarantine.d.ts +0 -27
- package/dist/tenant/quarantine.js +0 -72
- package/dist/tenant/routes-tenant.d.ts +0 -12
- package/dist/tenant/routes-tenant.js +0 -137
- package/dist/tenant/routes.d.ts +0 -9
- package/dist/tenant/routes.js +0 -151
- package/dist/tenant/store-memory.d.ts +0 -16
- package/dist/tenant/store-memory.js +0 -111
- /package/dist/{store → record}/cursor.d.ts +0 -0
- /package/dist/{store → record}/cursor.js +0 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tenant signing keys for subject tokens. Each key is an ES256 pair: the public JWK is stored
|
|
3
|
+
* as is, the PKCS#8 private key is sealed with the vault KEK (AAD bound to the Tenant and key
|
|
4
|
+
* id), like a credential.
|
|
5
|
+
*
|
|
6
|
+
* States: `standby` (published, not used yet), `current` (signs), `previous` (verifies only),
|
|
7
|
+
* `revoked`. The first use creates `current` and `standby`. Rotating moves standby → current →
|
|
8
|
+
* previous → revoked and makes a new standby; it is refused while the key it would revoke may
|
|
9
|
+
* still verify live tokens, so a rotation never signs anyone out (`force` skips that guard).
|
|
10
|
+
*
|
|
11
|
+
* Key states are read from the database on every use, so a revocation applies at once on
|
|
12
|
+
* every node. Only imported key objects are cached: public keys by id (immutable) and the
|
|
13
|
+
* current private key.
|
|
14
|
+
*/
|
|
15
|
+
import { importJWK } from "jose";
|
|
16
|
+
import type { PublicJwk, SigningKeyView } from "@nylorun/core/contracts";
|
|
17
|
+
import type { SigningKeyRow, Tx } from "../store/types.js";
|
|
18
|
+
type KeyLike = Awaited<ReturnType<typeof importJWK>>;
|
|
19
|
+
export interface SigningKeysOptions {
|
|
20
|
+
tenantId: string;
|
|
21
|
+
/** The vault KEK, created on first use. Resolve it before opening a transaction. */
|
|
22
|
+
kek(): Buffer;
|
|
23
|
+
}
|
|
24
|
+
export interface CurrentKey {
|
|
25
|
+
readonly id: string;
|
|
26
|
+
readonly key: KeyLike;
|
|
27
|
+
}
|
|
28
|
+
export declare class SigningKeys {
|
|
29
|
+
private readonly options;
|
|
30
|
+
private readonly publicKeys;
|
|
31
|
+
private current;
|
|
32
|
+
constructor(options: SigningKeysOptions);
|
|
33
|
+
kek(): Buffer;
|
|
34
|
+
private aad;
|
|
35
|
+
/** A new sealed key row in `state`. In-memory crypto only; safe inside a transaction. */
|
|
36
|
+
private create;
|
|
37
|
+
/** The current key row, creating `current` and `standby` if the Tenant has none. */
|
|
38
|
+
ensure(t: Tx, kek: Buffer): Promise<SigningKeyRow>;
|
|
39
|
+
/** The private key of `row` (the current key), imported once per key id. */
|
|
40
|
+
privateKey(row: SigningKeyRow, kek: Buffer): Promise<CurrentKey>;
|
|
41
|
+
/** The public key of `row`, imported once per key id. */
|
|
42
|
+
publicKey(row: SigningKeyRow): Promise<KeyLike>;
|
|
43
|
+
/**
|
|
44
|
+
* Rotates: previous → revoked, current → previous, standby → current, new standby. Refused
|
|
45
|
+
* with 409 while the previous key may still verify live tokens (retired less than the
|
|
46
|
+
* longest token lifetime, `maxTtlSeconds` or a delivery token's, plus a minute ago), unless
|
|
47
|
+
* `force`.
|
|
48
|
+
*/
|
|
49
|
+
rotate(t: Tx, kek: Buffer, maxTtlSeconds: number, force: boolean): Promise<SigningKeyRow[]>;
|
|
50
|
+
/** Revokes a `previous` or `standby` key. The current key must be rotated first. */
|
|
51
|
+
revoke(t: Tx, id: string): Promise<SigningKeyRow>;
|
|
52
|
+
}
|
|
53
|
+
export declare function publicJwk(row: SigningKeyRow): PublicJwk;
|
|
54
|
+
export declare function signingKeyView(row: SigningKeyRow): SigningKeyView;
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tenant signing keys for subject tokens. Each key is an ES256 pair: the public JWK is stored
|
|
3
|
+
* as is, the PKCS#8 private key is sealed with the vault KEK (AAD bound to the Tenant and key
|
|
4
|
+
* id), like a credential.
|
|
5
|
+
*
|
|
6
|
+
* States: `standby` (published, not used yet), `current` (signs), `previous` (verifies only),
|
|
7
|
+
* `revoked`. The first use creates `current` and `standby`. Rotating moves standby → current →
|
|
8
|
+
* previous → revoked and makes a new standby; it is refused while the key it would revoke may
|
|
9
|
+
* still verify live tokens, so a rotation never signs anyone out (`force` skips that guard).
|
|
10
|
+
*
|
|
11
|
+
* Key states are read from the database on every use, so a revocation applies at once on
|
|
12
|
+
* every node. Only imported key objects are cached: public keys by id (immutable) and the
|
|
13
|
+
* current private key.
|
|
14
|
+
*/
|
|
15
|
+
import { exportJWK, exportPKCS8, generateKeyPair, importJWK, importPKCS8, } from "jose";
|
|
16
|
+
import { newSigningKeyId, SIGNING_KEY_ID_PATTERN, } from "@nylorun/core/compatibility";
|
|
17
|
+
import { DELIVERY_TOKEN_MAX_TTL_SECONDS } from "@nylorun/core/contracts";
|
|
18
|
+
import { decryptSecret, encryptSecret } from "../vault/crypto.js";
|
|
19
|
+
import { fail } from "./http.js";
|
|
20
|
+
const ALG = "ES256";
|
|
21
|
+
/** Grace added to the longest token lifetime before a previous key may be revoked. */
|
|
22
|
+
const ROTATION_GRACE_MS = 60_000;
|
|
23
|
+
export class SigningKeys {
|
|
24
|
+
options;
|
|
25
|
+
publicKeys = new Map();
|
|
26
|
+
current;
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.options = options;
|
|
29
|
+
}
|
|
30
|
+
kek() {
|
|
31
|
+
return this.options.kek();
|
|
32
|
+
}
|
|
33
|
+
aad(id) {
|
|
34
|
+
return Buffer.from(`nylorun.signing-key:${this.options.tenantId}:${id}`);
|
|
35
|
+
}
|
|
36
|
+
/** A new sealed key row in `state`. In-memory crypto only; safe inside a transaction. */
|
|
37
|
+
async create(kek, state, now) {
|
|
38
|
+
const id = newSigningKeyId();
|
|
39
|
+
const pair = await generateKeyPair(ALG, { extractable: true });
|
|
40
|
+
const jwk = await exportJWK(pair.publicKey);
|
|
41
|
+
const pkcs8 = await exportPKCS8(pair.privateKey);
|
|
42
|
+
const sealed = encryptSecret(kek, this.aad(id), Buffer.from(pkcs8, "utf8"));
|
|
43
|
+
return {
|
|
44
|
+
id,
|
|
45
|
+
state,
|
|
46
|
+
alg: ALG,
|
|
47
|
+
publicJwk: JSON.stringify({
|
|
48
|
+
kty: jwk.kty,
|
|
49
|
+
crv: jwk.crv,
|
|
50
|
+
x: jwk.x,
|
|
51
|
+
y: jwk.y,
|
|
52
|
+
}),
|
|
53
|
+
createdAt: now,
|
|
54
|
+
activatedAt: state === "current" ? now : null,
|
|
55
|
+
retiredAt: null,
|
|
56
|
+
revokedAt: null,
|
|
57
|
+
kekId: sealed.kekId,
|
|
58
|
+
nonce: sealed.nonce,
|
|
59
|
+
ciphertext: sealed.ciphertext,
|
|
60
|
+
wrappedDek: sealed.wrappedDek,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** The current key row, creating `current` and `standby` if the Tenant has none. */
|
|
64
|
+
async ensure(t, kek) {
|
|
65
|
+
let live = await t.signingKeys(["standby", "current"]);
|
|
66
|
+
if (live.length < 2) {
|
|
67
|
+
// Concurrent first uses would both create keys: lock, then read what the winner made.
|
|
68
|
+
await t.lockSigningKeys();
|
|
69
|
+
live = await t.signingKeys(["standby", "current"]);
|
|
70
|
+
}
|
|
71
|
+
const now = new Date().toISOString();
|
|
72
|
+
let current = live.find((row) => row.state === "current");
|
|
73
|
+
if (!current) {
|
|
74
|
+
const standby = live.find((row) => row.state === "standby");
|
|
75
|
+
if (standby) {
|
|
76
|
+
await t.setSigningKeyState(standby.id, "standby", "current", now);
|
|
77
|
+
current = { ...standby, state: "current", activatedAt: now };
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
current = await this.create(kek, "current", now);
|
|
81
|
+
await t.insertSigningKey(current);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (!live.some((row) => row.state === "standby" && row.id !== current.id))
|
|
85
|
+
await t.insertSigningKey(await this.create(kek, "standby", now));
|
|
86
|
+
return current;
|
|
87
|
+
}
|
|
88
|
+
/** The private key of `row` (the current key), imported once per key id. */
|
|
89
|
+
async privateKey(row, kek) {
|
|
90
|
+
if (this.current?.id === row.id)
|
|
91
|
+
return this.current;
|
|
92
|
+
const pkcs8 = decryptSecret(kek, this.aad(row.id), Buffer.from(row.nonce), Buffer.from(row.ciphertext), Buffer.from(row.wrappedDek), row.kekId).toString("utf8");
|
|
93
|
+
const key = await importPKCS8(pkcs8, ALG);
|
|
94
|
+
this.current = { id: row.id, key };
|
|
95
|
+
return this.current;
|
|
96
|
+
}
|
|
97
|
+
/** The public key of `row`, imported once per key id. */
|
|
98
|
+
async publicKey(row) {
|
|
99
|
+
const cached = this.publicKeys.get(row.id);
|
|
100
|
+
if (cached)
|
|
101
|
+
return cached;
|
|
102
|
+
const key = await importJWK({ ...JSON.parse(row.publicJwk), alg: ALG }, ALG);
|
|
103
|
+
this.publicKeys.set(row.id, key);
|
|
104
|
+
return key;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Rotates: previous → revoked, current → previous, standby → current, new standby. Refused
|
|
108
|
+
* with 409 while the previous key may still verify live tokens (retired less than the
|
|
109
|
+
* longest token lifetime, `maxTtlSeconds` or a delivery token's, plus a minute ago), unless
|
|
110
|
+
* `force`.
|
|
111
|
+
*/
|
|
112
|
+
async rotate(t, kek, maxTtlSeconds, force) {
|
|
113
|
+
await t.lockSigningKeys();
|
|
114
|
+
await this.ensure(t, kek);
|
|
115
|
+
const now = new Date();
|
|
116
|
+
const at = now.toISOString();
|
|
117
|
+
const [previous] = await t.signingKeys(["previous"]);
|
|
118
|
+
if (previous && !force) {
|
|
119
|
+
const retired = Date.parse(previous.retiredAt ?? previous.createdAt);
|
|
120
|
+
// Delivery tokens may outlive the policy's subject tokens.
|
|
121
|
+
const longest = Math.max(maxTtlSeconds, DELIVERY_TOKEN_MAX_TTL_SECONDS);
|
|
122
|
+
const until = retired + longest * 1000 + ROTATION_GRACE_MS;
|
|
123
|
+
if (until > now.getTime())
|
|
124
|
+
fail(409, "The previous key may still verify live tokens", {
|
|
125
|
+
code: "request_rejected",
|
|
126
|
+
details: { retryAfterSeconds: Math.ceil((until - now.getTime()) / 1000) },
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (previous)
|
|
130
|
+
await t.setSigningKeyState(previous.id, "previous", "revoked", at);
|
|
131
|
+
const [current] = await t.signingKeys(["current"]);
|
|
132
|
+
const [standby] = await t.signingKeys(["standby"]);
|
|
133
|
+
if (current)
|
|
134
|
+
await t.setSigningKeyState(current.id, "current", "previous", at);
|
|
135
|
+
if (standby)
|
|
136
|
+
await t.setSigningKeyState(standby.id, "standby", "current", at);
|
|
137
|
+
else
|
|
138
|
+
await t.insertSigningKey(await this.create(kek, "current", at));
|
|
139
|
+
await t.insertSigningKey(await this.create(kek, "standby", at));
|
|
140
|
+
return t.signingKeys();
|
|
141
|
+
}
|
|
142
|
+
/** Revokes a `previous` or `standby` key. The current key must be rotated first. */
|
|
143
|
+
async revoke(t, id) {
|
|
144
|
+
const row = SIGNING_KEY_ID_PATTERN.test(id)
|
|
145
|
+
? await t.signingKey(id)
|
|
146
|
+
: undefined;
|
|
147
|
+
if (!row)
|
|
148
|
+
return fail(404, "Signing key not found");
|
|
149
|
+
if (row.state === "revoked")
|
|
150
|
+
return row;
|
|
151
|
+
if (row.state === "current")
|
|
152
|
+
return fail(409, "Rotate before revoking the current key");
|
|
153
|
+
const at = new Date().toISOString();
|
|
154
|
+
await t.setSigningKeyState(row.id, row.state, "revoked", at);
|
|
155
|
+
return { ...row, state: "revoked", revokedAt: at };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
export function publicJwk(row) {
|
|
159
|
+
return { ...JSON.parse(row.publicJwk), kid: row.id, alg: ALG, use: "sig" };
|
|
160
|
+
}
|
|
161
|
+
export function signingKeyView(row) {
|
|
162
|
+
return {
|
|
163
|
+
id: row.id,
|
|
164
|
+
state: row.state,
|
|
165
|
+
alg: row.alg,
|
|
166
|
+
publicKey: publicJwk(row),
|
|
167
|
+
createdAt: row.createdAt,
|
|
168
|
+
activatedAt: row.activatedAt,
|
|
169
|
+
retiredAt: row.retiredAt,
|
|
170
|
+
revokedAt: row.revokedAt,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Tx } from "../store/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Slim the completed model effects of a turn that ended (Model Calls §9): drop the prompt
|
|
4
|
+
* and the answer, which the transcript already holds, and keep the identity and status.
|
|
5
|
+
* Replay only ever reads the effects of a running segment. Tool, flow and uncertain rows
|
|
6
|
+
* are never touched: completed actions return their receipts, and status lists uncertain work.
|
|
7
|
+
*/
|
|
8
|
+
export declare function slimModelEffects(t: Tx, sessionId: string, turnId: string | null): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slim the completed model effects of a turn that ended (Model Calls §9): drop the prompt
|
|
3
|
+
* and the answer, which the transcript already holds, and keep the identity and status.
|
|
4
|
+
* Replay only ever reads the effects of a running segment. Tool, flow and uncertain rows
|
|
5
|
+
* are never touched: completed actions return their receipts, and status lists uncertain work.
|
|
6
|
+
*/
|
|
7
|
+
export async function slimModelEffects(t, sessionId, turnId) {
|
|
8
|
+
if (!turnId)
|
|
9
|
+
return;
|
|
10
|
+
for (const effect of await t.effectsForTurn(sessionId, turnId, ["completed"])) {
|
|
11
|
+
if (effect.request?.kind !== "model" || effect.slimmed)
|
|
12
|
+
continue;
|
|
13
|
+
const { input: _input, ...request } = effect.request;
|
|
14
|
+
await t.put("effects", request.effectId, {
|
|
15
|
+
...effect,
|
|
16
|
+
request,
|
|
17
|
+
outcome: {},
|
|
18
|
+
slimmed: true,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
package/dist/tenant/status.d.ts
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
import type { SeedTenantConfigRequest, SeedTenantConfigResponse, TenantStatus } from "@nylorun/core/contracts";
|
|
2
2
|
import type { SessionStore } from "../store/types.js";
|
|
3
|
-
import type { ExecutorRegistry } from "../core/executors.js";
|
|
4
3
|
import type { VaultService } from "../vault/service.js";
|
|
5
4
|
import type { SandboxManager } from "../sandbox/manager.js";
|
|
6
5
|
import type { TenantConfig } from "./types.js";
|
|
7
6
|
import type { TenantEnvelope } from "@nylorun/core/contracts";
|
|
8
7
|
import type { StuckInvocation } from "../execution/types.js";
|
|
9
8
|
import type { StreamsStatus } from "./streams.js";
|
|
9
|
+
import type { Keys } from "../keys/keys.js";
|
|
10
10
|
export interface TenantStatusContext {
|
|
11
11
|
envelope: TenantEnvelope;
|
|
12
12
|
config: TenantConfig;
|
|
13
13
|
store: SessionStore;
|
|
14
|
-
registry: ExecutorRegistry;
|
|
15
14
|
vault: VaultService;
|
|
16
15
|
sandbox: SandboxManager;
|
|
17
16
|
closing: boolean;
|
|
18
17
|
modelConfigured: boolean;
|
|
19
|
-
executorStreams: Map<string, Set<unknown>>;
|
|
20
18
|
/**
|
|
21
19
|
* This Tenant's execution invocations that need an operator (`TenantExecution`). Absent
|
|
22
20
|
* when the execution cannot report them.
|
|
@@ -34,4 +32,6 @@ export declare function buildTenantStatus(ctx: TenantStatusContext): Promise<Ten
|
|
|
34
32
|
export declare function seedTenantConfig(ctx: {
|
|
35
33
|
store: SessionStore;
|
|
36
34
|
vault: VaultService;
|
|
35
|
+
/** Seals the seeded model credential (F4.2). */
|
|
36
|
+
keys: Pick<Keys, "putHostModel">;
|
|
37
37
|
}, body: SeedTenantConfigRequest): Promise<SeedTenantConfigResponse>;
|
package/dist/tenant/status.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FIXTURE_MODEL_SETTING, seedFixtureModel } from "./model-setting.js";
|
|
2
|
+
import { SANDBOX_CONFIG_SETTING, writeSandboxConfig } from "../sandbox/tenant-config.js";
|
|
2
3
|
/** How long status waits for the execution to list stuck invocations. */
|
|
3
4
|
const STUCK_TIMEOUT_MS = 5000;
|
|
4
5
|
/** `TenantStatus.execution`: paused and backing-off invocations, or why they are unknown. */
|
|
@@ -54,17 +55,13 @@ export async function buildTenantStatus(ctx) {
|
|
|
54
55
|
const definitionIds = new Set(definitions
|
|
55
56
|
.map((d) => d.manifest?.id)
|
|
56
57
|
.filter((id) => typeof id === "string"));
|
|
57
|
-
const
|
|
58
|
-
const agentIds = new Set([...definitionIds, ...
|
|
59
|
-
const agents = [...agentIds].sort().map((agentId) => {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
connected: !!record &&
|
|
65
|
-
(ctx.executorStreams.get(record.tokenHash)?.size ?? 0) > 0,
|
|
66
|
-
};
|
|
67
|
-
});
|
|
58
|
+
const endpointIds = new Set((await ctx.store.tx((t) => t.listEndpoints())).map((e) => e.agentId));
|
|
59
|
+
const agentIds = new Set([...definitionIds, ...endpointIds]);
|
|
60
|
+
const agents = [...agentIds].sort().map((agentId) => ({
|
|
61
|
+
agentId,
|
|
62
|
+
registered: definitionIds.has(agentId),
|
|
63
|
+
endpoint: endpointIds.has(agentId),
|
|
64
|
+
}));
|
|
68
65
|
return {
|
|
69
66
|
tenant: ctx.envelope,
|
|
70
67
|
path: ctx.config.paths.root,
|
|
@@ -72,7 +69,7 @@ export async function buildTenantStatus(ctx) {
|
|
|
72
69
|
store: health.schemaVersion > 0,
|
|
73
70
|
scheduler: !ctx.closing,
|
|
74
71
|
model: ctx.modelConfigured || modelView.configured,
|
|
75
|
-
|
|
72
|
+
endpoints: true,
|
|
76
73
|
schema: health.ok,
|
|
77
74
|
},
|
|
78
75
|
model: modelView,
|
|
@@ -108,6 +105,16 @@ export async function seedTenantConfig(ctx, body) {
|
|
|
108
105
|
});
|
|
109
106
|
(inserted ? applied : kept).push("sandbox.backend");
|
|
110
107
|
}
|
|
108
|
+
const sandboxConfig = body.sandbox?.config;
|
|
109
|
+
if (sandboxConfig) {
|
|
110
|
+
const inserted = await ctx.store.tx(async (t) => {
|
|
111
|
+
if ((await t.getSetting(SANDBOX_CONFIG_SETTING)) !== undefined)
|
|
112
|
+
return false;
|
|
113
|
+
await writeSandboxConfig(t, sandboxConfig);
|
|
114
|
+
return true;
|
|
115
|
+
});
|
|
116
|
+
(inserted ? applied : kept).push(SANDBOX_CONFIG_SETTING);
|
|
117
|
+
}
|
|
111
118
|
if (body.fixtureModel) {
|
|
112
119
|
const inserted = await ctx.store.tx((t) => seedFixtureModel(t));
|
|
113
120
|
(inserted ? applied : kept).push(FIXTURE_MODEL_SETTING);
|
|
@@ -118,7 +125,7 @@ export async function seedTenantConfig(ctx, body) {
|
|
|
118
125
|
kept.push("model");
|
|
119
126
|
}
|
|
120
127
|
else {
|
|
121
|
-
await ctx.
|
|
128
|
+
await ctx.keys.putHostModel({
|
|
122
129
|
requestId: body.requestId,
|
|
123
130
|
idempotencyKey: `seed-model:${body.requestId}`,
|
|
124
131
|
...body.model,
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import type { PostgresClient } from "../store/postgres/connect.js";
|
|
2
|
-
import type { Migration } from "../store/postgres/
|
|
3
|
-
import { type
|
|
4
|
-
|
|
2
|
+
import type { Migration } from "../store/postgres/migrate.js";
|
|
3
|
+
import { type TenantCreation } from "../store/postgres/tenant.js";
|
|
4
|
+
import { type Logger, type OpenTenantRuntime, type TenantConfig, type TenantOpener } from "./types.js";
|
|
5
|
+
export interface PostgresTenantOptions {
|
|
5
6
|
hostRoot: string;
|
|
6
|
-
/** The
|
|
7
|
+
/** The pool on the Tenant's database. The Tenant never ends it. */
|
|
7
8
|
sql: PostgresClient;
|
|
8
|
-
/**
|
|
9
|
+
/** Who the Tenant is when the database holds none yet. */
|
|
10
|
+
create: TenantCreation;
|
|
11
|
+
/** Opens the Tenant Runtime on the opened store; the Runtime then owns it. */
|
|
9
12
|
openRuntime: OpenTenantRuntime;
|
|
10
13
|
configFor: (tenantId: string) => TenantConfig;
|
|
11
14
|
logger?: Logger;
|
|
12
15
|
/** Tests only: the migrations this Runtime knows. */
|
|
13
16
|
migrations?: readonly Migration[];
|
|
14
17
|
}
|
|
15
|
-
export declare function
|
|
18
|
+
export declare function createPostgresTenantOpener(options: PostgresTenantOptions): TenantOpener;
|
package/dist/tenant/store-pg.js
CHANGED
|
@@ -1,140 +1,75 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Opens the Host's Tenant on its Postgres database (tenancy.md §4–§5, plan P9).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* the Tenant
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* The database holds one Tenant (`store/postgres/tenant.ts`): opening migrates it, creates
|
|
5
|
+
* the Tenant on first start (from the Host's configuration: id, name, Studio and derived
|
|
6
|
+
* principals), and opens the Tenant Runtime on its Session Store. What stays on the Host root
|
|
7
|
+
* is the Tenant directory `tenant/`: the vault key (`vault-kek`), `plugin-data/`, `logs/`,
|
|
8
|
+
* and the private `home/`, `tmp/` and `sandboxes/`.
|
|
9
9
|
*
|
|
10
|
-
* -
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* throws that quarantine. A failure outside the Tenant (Postgres unreachable, its sweep
|
|
16
|
-
* not armed) is `TenantUnavailableError`, which the module does not cache.
|
|
17
|
-
* - `trash` drops the schema and removes the directory. There is no `trash/` copy: the key
|
|
18
|
-
* is useless without the schema.
|
|
10
|
+
* - A failure in the Tenant (an old layout, a database newer than this Runtime, a failed
|
|
11
|
+
* migration, an unreadable Tenant row, vault ciphertext without its key) rejects with that
|
|
12
|
+
* `TenantOpenError`; the Host reports it and fails readiness.
|
|
13
|
+
* - A failure outside the Tenant (Postgres unreachable, its sweep not armed in Restate) is
|
|
14
|
+
* `TenantUnavailableError`; the Tenant module retries.
|
|
19
15
|
*/
|
|
20
|
-
import { mkdirSync
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
16
|
+
import { mkdirSync } from "node:fs";
|
|
17
|
+
import { openTenantDatabase, } from "../store/postgres/tenant.js";
|
|
18
|
+
import { TenantOpenError } from "./cause.js";
|
|
23
19
|
import { tenantPaths } from "./paths.js";
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
/** Repair instructions for a Postgres Tenant, naming its schema. */
|
|
27
|
-
function repairFor(code, tenantId) {
|
|
28
|
-
const status = `nylo tenant status ${tenantId}`;
|
|
29
|
-
const schema = tenantSchemaName(tenantId);
|
|
30
|
-
switch (code) {
|
|
31
|
-
case "schema-too-new":
|
|
32
|
-
return `${status} — schema ${schema} was migrated by a newer Runtime; run that version or newer`;
|
|
33
|
-
case "migration-failed":
|
|
34
|
-
return `${status} — inspect the Runtime log for the failed migration of ${schema}, fix it, then restart the Runtime`;
|
|
35
|
-
case "envelope-invalid":
|
|
36
|
-
return `${status} — the tenant table of schema ${schema} is missing or invalid; restore the schema from a Postgres backup`;
|
|
37
|
-
case "kek-missing":
|
|
38
|
-
return `${status} — restore vault-kek under the Tenant directory (ciphertext cannot be opened without it)`;
|
|
39
|
-
default:
|
|
40
|
-
return `${status} — inspect the Runtime log and Postgres, then restart the Runtime`;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
function unavailable(tenantId, cause) {
|
|
20
|
+
import { TenantUnavailableError, } from "./types.js";
|
|
21
|
+
function unavailable(cause) {
|
|
44
22
|
return cause instanceof TenantUnavailableError
|
|
45
23
|
? cause
|
|
46
|
-
: new TenantUnavailableError(
|
|
47
|
-
}
|
|
48
|
-
function quarantined(reason, tenantId) {
|
|
49
|
-
return new QuarantineError({ ...reason, repair: repairFor(reason.code, tenantId) });
|
|
24
|
+
: new TenantUnavailableError({ cause });
|
|
50
25
|
}
|
|
51
|
-
export function
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
26
|
+
export function createPostgresTenantOpener(options) {
|
|
27
|
+
const { logger } = options;
|
|
28
|
+
return async () => {
|
|
29
|
+
const opened = await openTenantDatabase({
|
|
30
|
+
sql: options.sql,
|
|
31
|
+
create: options.create,
|
|
32
|
+
...(options.migrations ? { migrations: options.migrations } : {}),
|
|
33
|
+
onError: (error) => logger?.error("post-commit step failed", {
|
|
34
|
+
message: error instanceof Error ? error.message : String(error),
|
|
35
|
+
}),
|
|
36
|
+
}).catch((error) => {
|
|
37
|
+
if (error instanceof TenantOpenError)
|
|
38
|
+
throw error;
|
|
39
|
+
throw unavailable(error);
|
|
40
|
+
});
|
|
41
|
+
const { store, envelope } = opened;
|
|
42
|
+
const tenantId = envelope.id;
|
|
43
|
+
if (opened.created)
|
|
44
|
+
logger?.info("tenant created", { tenantId, name: envelope.name });
|
|
45
|
+
else if (options.create.tenantId !== undefined && options.create.tenantId !== tenantId)
|
|
46
|
+
logger?.warn("tenant id configured differs", {
|
|
47
|
+
tenantId,
|
|
48
|
+
configured: options.create.tenantId,
|
|
49
|
+
message: "The database already holds a Tenant; the configured id applies only to a new database",
|
|
50
|
+
});
|
|
51
|
+
if (opened.migrated.from !== opened.migrated.to)
|
|
52
|
+
logger?.info("tenant schema migrated", {
|
|
53
|
+
tenantId,
|
|
54
|
+
from: opened.migrated.from,
|
|
55
|
+
to: opened.migrated.to,
|
|
56
|
+
});
|
|
67
57
|
try {
|
|
68
|
-
|
|
58
|
+
const config = options.configFor(tenantId);
|
|
59
|
+
const paths = tenantPaths(options.hostRoot);
|
|
60
|
+
mkdirSync(paths.root, { recursive: true, mode: 0o700 });
|
|
61
|
+
return await options.openRuntime({ ...config, tenantId }, { store, envelope });
|
|
69
62
|
}
|
|
70
63
|
catch (error) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
throw new TenantNotFoundError(id);
|
|
76
|
-
throw quarantined(error.toQuarantine(), id);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return {
|
|
80
|
-
enumerate: () => catalog.listTenantIds(),
|
|
81
|
-
readEnvelope,
|
|
82
|
-
async create(envelope, bootstrap) {
|
|
83
|
-
makeDirectory(envelope.id);
|
|
84
|
-
try {
|
|
85
|
-
const result = await catalog.createTenant({ envelope, principals: bootstrap });
|
|
86
|
-
return result.status;
|
|
87
|
-
}
|
|
88
|
-
catch (error) {
|
|
89
|
-
// An existing Tenant with other bootstrap material; the module reports the conflict.
|
|
90
|
-
if (error instanceof TenantConflictError)
|
|
91
|
-
return "exists";
|
|
64
|
+
await store.close().catch(() => undefined);
|
|
65
|
+
if (error instanceof TenantOpenError) {
|
|
66
|
+
// The Tenant's own failure (`kek-missing`): report which Tenant it is.
|
|
67
|
+
error.envelope ??= envelope;
|
|
92
68
|
throw error;
|
|
93
69
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
.openTenant(id)
|
|
99
|
-
.catch((error) => {
|
|
100
|
-
throw unavailable(id, error);
|
|
101
|
-
});
|
|
102
|
-
if (result.status === "not-found")
|
|
103
|
-
throw new TenantNotFoundError(id);
|
|
104
|
-
if (result.status === "quarantined")
|
|
105
|
-
throw quarantined(result.reason, id);
|
|
106
|
-
if (result.migrated.from !== result.migrated.to)
|
|
107
|
-
options.logger?.info("tenant schema migrated", {
|
|
108
|
-
tenantId: id,
|
|
109
|
-
from: result.migrated.from,
|
|
110
|
-
to: result.migrated.to,
|
|
111
|
-
});
|
|
112
|
-
try {
|
|
113
|
-
makeDirectory(id);
|
|
114
|
-
const config = options.configFor(id);
|
|
115
|
-
return await options.openRuntime({ ...config, tenantId: id }, { store: result.store, envelope: result.envelope });
|
|
116
|
-
}
|
|
117
|
-
catch (error) {
|
|
118
|
-
await result.store.close().catch(() => undefined);
|
|
119
|
-
// The Runtime's own quarantines (`kek-missing`) get this store's repair wording.
|
|
120
|
-
const reason = error?.quarantine;
|
|
121
|
-
if (reason)
|
|
122
|
-
throw quarantined(reason, id);
|
|
123
|
-
if (error instanceof QuarantineError)
|
|
124
|
-
throw error;
|
|
125
|
-
// Anything else failed outside the Tenant (Postgres, arming its sweep in Restate):
|
|
126
|
-
// the next use tries again rather than quarantining it.
|
|
127
|
-
throw unavailable(id, error);
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
async trash(id) {
|
|
131
|
-
await catalog.deleteTenant(id);
|
|
132
|
-
rmSync(paths(id).root, { recursive: true, force: true });
|
|
133
|
-
},
|
|
134
|
-
async removePartial(id) {
|
|
135
|
-
if (await catalog.tenantExists(id))
|
|
136
|
-
return;
|
|
137
|
-
rmSync(paths(id).root, { recursive: true, force: true });
|
|
138
|
-
},
|
|
70
|
+
// Anything else failed outside the Tenant (Postgres, arming its sweep in Restate): the
|
|
71
|
+
// next use tries again.
|
|
72
|
+
throw unavailable(error);
|
|
73
|
+
}
|
|
139
74
|
};
|
|
140
75
|
}
|