@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
package/dist/host/main.js
CHANGED
|
@@ -2,33 +2,48 @@
|
|
|
2
2
|
* Runtime Host process entry (`@nylorun/runtime/server`).
|
|
3
3
|
*
|
|
4
4
|
* Reads `NYLORUN_HOME` (falls back to `~/.nylorun`), and hands an environment
|
|
5
|
-
* snapshot and argv to `parseStackConfig` (container listen mode, `--
|
|
5
|
+
* snapshot and argv to `parseStackConfig` (container listen mode, `--service`,
|
|
6
6
|
* stack endpoints). Absolute Host root is resolved once and passed down.
|
|
7
7
|
*
|
|
8
8
|
* Composition: `createInfra` builds the Postgres pool, Durable Session
|
|
9
9
|
* Execution and Durable Streams from the endpoints; `createHostExecution`
|
|
10
|
-
*
|
|
11
|
-
* (`NYLORUN_DATABASE_URL`, required)
|
|
12
|
-
*
|
|
10
|
+
* builds the process's execution; the Host serves the one Tenant its Postgres
|
|
11
|
+
* database holds (`NYLORUN_DATABASE_URL`, required), and creates it there on
|
|
12
|
+
* first start (`NYLORUN_TENANT_ID`, `NYLORUN_TENANT_NAME`,
|
|
13
|
+
* `NYLORUN_DERIVED_PRINCIPALS`; tenancy.md §4); with S2, a process running core
|
|
14
|
+
* runs the stream relay, which feeds the Tenant's streams from the record over
|
|
15
|
+
* logical replication (one process at a time holds the slot); `/ready` reports
|
|
16
|
+
* the Tenant and the infrastructure checks. A process running the gates service (the local
|
|
17
|
+
* stack's `gateway` container) starts only the gate (`runGates`): it needs
|
|
18
|
+
* neither host.json nor host-credentials.json. See the startup order in `main()`. Tests compose a Host without this
|
|
13
19
|
* entry, with `createHost` and an injected Tenant module.
|
|
14
20
|
*/
|
|
15
21
|
import { readFileSync, mkdirSync, existsSync } from "node:fs";
|
|
16
22
|
import { createRequire } from "node:module";
|
|
17
23
|
import { homedir } from "node:os";
|
|
18
24
|
import { resolve } from "node:path";
|
|
19
|
-
import { hostPaths
|
|
25
|
+
import { hostPaths } from "../tenant/paths.js";
|
|
20
26
|
import { createTenantModule } from "../tenant/module.js";
|
|
21
|
-
import {
|
|
27
|
+
import { hostPrincipals } from "../tenant/principals.js";
|
|
28
|
+
import { createPostgresTenantOpener } from "../tenant/store-pg.js";
|
|
22
29
|
import { openTenantRuntime } from "../tenant/runtime.js";
|
|
23
|
-
import {
|
|
30
|
+
import { createPgoutputSource } from "../adapters/replication/pgoutput.js";
|
|
31
|
+
import { assertLogicalReplication } from "../store/postgres/connect.js";
|
|
32
|
+
import { createPostgresRecordReader } from "../store/postgres/record.js";
|
|
33
|
+
import { createStreamRelay } from "../streams/relay/core.js";
|
|
24
34
|
import { configForFactory } from "./config-for.js";
|
|
25
35
|
import { createHost } from "./create-host.js";
|
|
26
36
|
import { createHostExecution } from "./execution.js";
|
|
27
37
|
import { baselineEnvironment } from "./environment.js";
|
|
28
38
|
import { HostListenError, EXIT_PORT_IN_USE, EXIT_NON_LOOPBACK, } from "./http.js";
|
|
29
39
|
import { createHostLogger } from "./logger.js";
|
|
30
|
-
import { describeEndpoints, parseStackConfig } from "./stack-config.js";
|
|
40
|
+
import { describeEndpoints, describeServices, parseStackConfig, } from "./stack-config.js";
|
|
31
41
|
import { createExecution, createInfra } from "../infra/index.js";
|
|
42
|
+
import { createDatabase } from "../infra/database.js";
|
|
43
|
+
import { startGates } from "./gates.js";
|
|
44
|
+
import { httpToolGate } from "../gates/tool-client.js";
|
|
45
|
+
import { httpKeys } from "../keys/client.js";
|
|
46
|
+
import { httpModelGate } from "../gates/http-client.js";
|
|
32
47
|
const nodeRequire = createRequire(import.meta.url);
|
|
33
48
|
function coreVersion() {
|
|
34
49
|
try {
|
|
@@ -46,20 +61,74 @@ function resolveHostRoot() {
|
|
|
46
61
|
const root = fromEnv && fromEnv.length > 0 ? fromEnv : resolve(homedir(), ".nylorun");
|
|
47
62
|
return resolve(root);
|
|
48
63
|
}
|
|
64
|
+
/** The slot's lag in WAL bytes, when the source can tell. */
|
|
65
|
+
async function lagOf(source) {
|
|
66
|
+
const lagBytes = await source.lag?.().catch(() => undefined);
|
|
67
|
+
return lagBytes === undefined ? {} : { lagBytes };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The gates service: the Model Gate's listener over the Postgres pool and the Host's tenant
|
|
71
|
+
* directory. Writes nothing to the Host root (the local stack mounts it read-only).
|
|
72
|
+
*/
|
|
73
|
+
async function runGates(stack) {
|
|
74
|
+
const gates = stack.gates;
|
|
75
|
+
if (!stack.endpoints.databaseUrl)
|
|
76
|
+
throw new Error("NYLORUN_DATABASE_URL is required for --service gates: the gate reads Tenant vaults from Postgres (`nylorun start` sets it)");
|
|
77
|
+
const logger = createHostLogger();
|
|
78
|
+
logger.info("host_stack_config", {
|
|
79
|
+
services: [...stack.services],
|
|
80
|
+
...(stack.packing ? { packing: stack.packing } : {}),
|
|
81
|
+
endpoints: describeEndpoints(stack.endpoints),
|
|
82
|
+
});
|
|
83
|
+
const database = createDatabase(stack);
|
|
84
|
+
let server;
|
|
85
|
+
try {
|
|
86
|
+
server = await startGates({
|
|
87
|
+
gates,
|
|
88
|
+
database,
|
|
89
|
+
hostRoot: resolveHostRoot(),
|
|
90
|
+
logger,
|
|
91
|
+
...(stack.delivery ? { delivery: stack.delivery } : {}),
|
|
92
|
+
...(stack.services.has("keys") ? { keys: true } : {}),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
await database.end({ timeout: 5 });
|
|
97
|
+
if (error instanceof HostListenError) {
|
|
98
|
+
logger.error("listen_failed", { message: error.message, exitCode: error.exitCode });
|
|
99
|
+
process.exitCode = error.exitCode;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
logger.info("gates_ready", { url: server.url });
|
|
105
|
+
let stopping = false;
|
|
106
|
+
for (const signal of ["SIGINT", "SIGTERM"])
|
|
107
|
+
process.on(signal, () => {
|
|
108
|
+
if (stopping)
|
|
109
|
+
return;
|
|
110
|
+
stopping = true;
|
|
111
|
+
void server
|
|
112
|
+
.close()
|
|
113
|
+
.then(() => database.end({ timeout: 5 }))
|
|
114
|
+
.finally(() => process.exit(0));
|
|
115
|
+
});
|
|
116
|
+
}
|
|
49
117
|
export async function main() {
|
|
50
118
|
const stack = parseStackConfig(process.env, process.argv.slice(2));
|
|
119
|
+
if (stack.services.has("gates") || stack.services.has("keys"))
|
|
120
|
+
return runGates(stack);
|
|
51
121
|
const hostRoot = resolveHostRoot();
|
|
52
122
|
const paths = hostPaths(hostRoot);
|
|
53
123
|
mkdirSync(paths.home, { recursive: true });
|
|
54
124
|
mkdirSync(paths.tmp, { recursive: true });
|
|
55
|
-
mkdirSync(paths.tenants, { recursive: true });
|
|
56
125
|
if (!existsSync(paths.config)) {
|
|
57
126
|
throw new Error(`Missing host.json at ${paths.config}; run \`nylorun start\` first`);
|
|
58
127
|
}
|
|
59
128
|
if (!existsSync(paths.credentials)) {
|
|
60
129
|
throw new Error(`Missing host-credentials.json at ${paths.credentials}; run \`nylorun start\` first`);
|
|
61
130
|
}
|
|
62
|
-
//
|
|
131
|
+
// The Tenant is a Postgres database. Only a Host outside a container (a local
|
|
63
132
|
// development Host) may run without S2, on in-process streams.
|
|
64
133
|
if (!stack.endpoints.databaseUrl) {
|
|
65
134
|
throw new Error("NYLORUN_DATABASE_URL is required: the Postgres URL of the Session Store (`nylorun start` sets it)");
|
|
@@ -70,13 +139,20 @@ export async function main() {
|
|
|
70
139
|
const config = loadJson(paths.config);
|
|
71
140
|
const credentials = loadJson(paths.credentials);
|
|
72
141
|
const logger = createHostLogger();
|
|
142
|
+
if (stack.deprecatedRole)
|
|
143
|
+
logger.warn("deprecated_flag", {
|
|
144
|
+
flag: "--role",
|
|
145
|
+
use: `--service ${describeServices(stack.services)}`,
|
|
146
|
+
});
|
|
73
147
|
logger.info("host_stack_config", {
|
|
74
|
-
|
|
148
|
+
services: [...stack.services],
|
|
149
|
+
...(stack.packing ? { packing: stack.packing } : {}),
|
|
150
|
+
...(stack.services.has("loop")
|
|
151
|
+
? { modelGate: stack.modelGate ? stack.modelGate.url : "in-process" }
|
|
152
|
+
: {}),
|
|
75
153
|
mode: stack.listen ? "container" : "local",
|
|
76
154
|
endpoints: describeEndpoints(stack.endpoints),
|
|
77
155
|
});
|
|
78
|
-
// Tenants from the SQLite Runtime are not migrated: move them out of the way.
|
|
79
|
-
trashSqliteTenants(hostRoot, logger);
|
|
80
156
|
const infra = createInfra(stack, { logger });
|
|
81
157
|
const database = infra.database;
|
|
82
158
|
if (!database)
|
|
@@ -89,46 +165,86 @@ export async function main() {
|
|
|
89
165
|
hostConfig: config,
|
|
90
166
|
logger,
|
|
91
167
|
baseline,
|
|
168
|
+
...(stack.delivery ? { delivery: stack.delivery } : {}),
|
|
92
169
|
});
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
// invocation for a Tenant that is not open here opens it on demand.
|
|
170
|
+
// The process's Durable Session Execution: Restate when its endpoints are set, else the
|
|
171
|
+
// in-process memory execution. An invocation that arrives while the Tenant opens waits for it.
|
|
96
172
|
const hostExecution = createHostExecution({
|
|
97
173
|
execution: infra.execution ?? createExecution(stack),
|
|
98
|
-
|
|
174
|
+
services: stack.services,
|
|
99
175
|
resolve: (tenantId) => module.worker(tenantId),
|
|
100
176
|
logger,
|
|
101
177
|
});
|
|
102
178
|
// Durable Streams: S2 when configured (always in container mode). Without
|
|
103
|
-
// them (a local Host only)
|
|
179
|
+
// them (a local Host only) the Tenant keeps in-process streams, whose history
|
|
104
180
|
// does not survive a restart.
|
|
105
181
|
const streams = infra.streams;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
182
|
+
// With S2, the stream relay feeds the Tenant's streams from the record. Every process
|
|
183
|
+
// running core runs one, once the Tenant is open (the relay reads its id); the replication
|
|
184
|
+
// slot lets exactly one be active. Without S2, the Tenant relays its own commits.
|
|
185
|
+
let relay;
|
|
186
|
+
let relayLag;
|
|
187
|
+
if (streams && stack.services.has("core"))
|
|
188
|
+
await assertLogicalReplication(database);
|
|
189
|
+
const startRelay = (tenantId) => {
|
|
190
|
+
if (!streams || !stack.services.has("core") || relay)
|
|
191
|
+
return;
|
|
192
|
+
const source = createPgoutputSource({
|
|
193
|
+
connectionString: stack.endpoints.databaseUrl,
|
|
194
|
+
tenantId,
|
|
195
|
+
log: (message, fields) => logger.info(message, fields),
|
|
196
|
+
});
|
|
197
|
+
relay = createStreamRelay({
|
|
198
|
+
source,
|
|
199
|
+
record: createPostgresRecordReader(database, { tenantId }),
|
|
200
|
+
streams,
|
|
201
|
+
log: (message, fields) => logger.info(message, fields),
|
|
202
|
+
});
|
|
203
|
+
const status = relay.status;
|
|
204
|
+
relayLag = async () => ({ ...status(), ...(await lagOf(source)) });
|
|
205
|
+
relay.start();
|
|
206
|
+
};
|
|
207
|
+
// With the gates service the Tenant's vault-backed model calls, remote MCP calls and Action
|
|
208
|
+
// deliveries cross it, and this process never reads a model or MCP credential.
|
|
209
|
+
const modelGate = stack.modelGate
|
|
210
|
+
? httpModelGate({ url: stack.modelGate.url, token: stack.modelGate.token })
|
|
211
|
+
: undefined;
|
|
212
|
+
const toolGate = stack.modelGate
|
|
213
|
+
? httpToolGate({ url: stack.modelGate.url, token: stack.modelGate.token })
|
|
214
|
+
: undefined;
|
|
215
|
+
// With the keys service, vault writes and token signing cross it, and this process never
|
|
216
|
+
// reads the vault key (F4.2).
|
|
217
|
+
const keys = stack.keys ? httpKeys({ url: stack.keys.url, token: stack.keys.token }) : undefined;
|
|
218
|
+
const tenantSettings = stack.tenant ?? { name: "default", derivedPrincipals: ["project"] };
|
|
117
219
|
const module = createTenantModule({
|
|
118
|
-
|
|
220
|
+
open: createPostgresTenantOpener({
|
|
221
|
+
hostRoot,
|
|
222
|
+
sql: database,
|
|
223
|
+
create: {
|
|
224
|
+
...(tenantSettings.id ? { tenantId: tenantSettings.id } : {}),
|
|
225
|
+
name: tenantSettings.name,
|
|
226
|
+
principals: hostPrincipals({
|
|
227
|
+
adminKey: credentials.adminKey,
|
|
228
|
+
derived: tenantSettings.derivedPrincipals,
|
|
229
|
+
}),
|
|
230
|
+
},
|
|
231
|
+
configFor,
|
|
232
|
+
logger,
|
|
233
|
+
openRuntime: (tenantConfig, opened) => openTenantRuntime(tenantConfig, {
|
|
234
|
+
execution: hostExecution.tenantExecution,
|
|
235
|
+
...(modelGate ? { modelGate } : {}),
|
|
236
|
+
...(toolGate ? { toolGate } : {}),
|
|
237
|
+
...(keys ? { keys } : {}),
|
|
238
|
+
...(streams ? { streams, hostRelay: true } : {}),
|
|
239
|
+
...opened,
|
|
240
|
+
}),
|
|
241
|
+
}),
|
|
119
242
|
logger,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
onDeleted: async (tenantId) => {
|
|
126
|
-
const failed = (await Promise.allSettled([
|
|
127
|
-
hostExecution.disarm(tenantId),
|
|
128
|
-
...(streams ? [deleteTenantStreams(streams, tenantId)] : []),
|
|
129
|
-
])).flatMap((result) => (result.status === "rejected" ? [result.reason] : []));
|
|
130
|
-
if (failed.length > 0)
|
|
131
|
-
throw new AggregateError(failed, "Deleted Tenant cleanup failed");
|
|
243
|
+
onOpen: (handle) => startRelay(handle.envelope.id),
|
|
244
|
+
relayStatus: async () => {
|
|
245
|
+
if (!relayLag)
|
|
246
|
+
throw new Error("no relay");
|
|
247
|
+
return relayLag();
|
|
132
248
|
},
|
|
133
249
|
});
|
|
134
250
|
const options = {
|
|
@@ -140,29 +256,47 @@ export async function main() {
|
|
|
140
256
|
coreVersion: coreVersion(),
|
|
141
257
|
...(stack.listen ? { listen: stack.listen } : {}),
|
|
142
258
|
...(stack.publicUrl ? { publicUrl: stack.publicUrl } : {}),
|
|
259
|
+
// Container mode (the local stack) allows browsers unless told not to; a Host started
|
|
260
|
+
// from host.json only when it says so. Without publishable keys nothing is reachable.
|
|
261
|
+
browserAccess: stack.browserAccess ?? (stack.listen ? true : config.browserAccess === true),
|
|
262
|
+
// The Admin API on its own listener: from the container environment, or from host.json
|
|
263
|
+
// (loopback, on the same host) when the Host runs outside a container.
|
|
264
|
+
...(stack.operator
|
|
265
|
+
? { operator: stack.operator }
|
|
266
|
+
: !stack.listen && typeof config.adminPort === "number"
|
|
267
|
+
? { operator: { host: config.host, port: config.adminPort } }
|
|
268
|
+
: {}),
|
|
143
269
|
...(infra.readiness ? { readiness: infra.readiness } : {}),
|
|
144
270
|
// SIGTERM and POST /v1/admin/host/shutdown both close the Host this way:
|
|
145
|
-
// stop the Worker, close the
|
|
271
|
+
// stop the Worker and the relay, close the Tenant, then end the infrastructure clients.
|
|
146
272
|
shutdown: {
|
|
147
|
-
beforeTenants: () =>
|
|
273
|
+
beforeTenants: async () => {
|
|
274
|
+
await hostExecution.stop();
|
|
275
|
+
await relay?.stop();
|
|
276
|
+
},
|
|
148
277
|
afterTenants: () => infra.close(),
|
|
149
278
|
},
|
|
150
279
|
};
|
|
151
280
|
const host = createHost(options);
|
|
152
|
-
// Startup order. The Worker starts first: opening
|
|
281
|
+
// Startup order. The Worker starts first: opening the Tenant arms its sweep
|
|
153
282
|
// through Restate's ingress, which answers 404 until a Worker has registered
|
|
154
|
-
// the services, so
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
// mode runs one `--
|
|
158
|
-
// listener starts
|
|
159
|
-
//
|
|
283
|
+
// the services, so the Tenant may not open before `start`. A core-only
|
|
284
|
+
// process serves no Worker endpoint, so it can open the Tenant only once some
|
|
285
|
+
// Worker process has registered (until then the open is retried). Container
|
|
286
|
+
// mode runs one `--service core,loop` process, which registers here. Then the
|
|
287
|
+
// listener starts and opens the Tenant (migrating its database, creating it on
|
|
288
|
+
// first start); opening arms its sweep, which recovers wakes lost with
|
|
289
|
+
// Restate's state (§14.8), and starts the stream relay. A Tenant that cannot
|
|
290
|
+
// be opened leaves the Host listening but not ready, with the cause in
|
|
291
|
+
// `/v1/admin/status`.
|
|
160
292
|
try {
|
|
161
293
|
await hostExecution.start();
|
|
162
294
|
await host.listen();
|
|
163
295
|
}
|
|
164
296
|
catch (error) {
|
|
165
297
|
await hostExecution.stop().catch(() => undefined);
|
|
298
|
+
await module.close().catch(() => undefined);
|
|
299
|
+
await relay?.stop().catch(() => undefined);
|
|
166
300
|
await infra.close();
|
|
167
301
|
if (error instanceof HostListenError) {
|
|
168
302
|
logger.error("listen_failed", {
|
|
@@ -174,16 +308,13 @@ export async function main() {
|
|
|
174
308
|
}
|
|
175
309
|
throw error;
|
|
176
310
|
}
|
|
177
|
-
const
|
|
178
|
-
await hostExecution
|
|
179
|
-
.armAll(tenants.filter((t) => t.state === "open").map((t) => t.id))
|
|
180
|
-
.catch((error) => logger.warn("tenant_sweeps_not_armed", {
|
|
181
|
-
message: error instanceof Error ? error.message : String(error),
|
|
182
|
-
}));
|
|
311
|
+
const tenant = module.tenant();
|
|
183
312
|
logger.info("host_ready", {
|
|
184
313
|
url: host.url,
|
|
185
314
|
hostId: config.hostId,
|
|
186
|
-
|
|
315
|
+
tenantId: tenant.id,
|
|
316
|
+
tenant: tenant.state,
|
|
317
|
+
...(tenant.cause ? { cause: tenant.cause.code } : {}),
|
|
187
318
|
});
|
|
188
319
|
void host.closed.then(() => process.exit(0));
|
|
189
320
|
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
@@ -1,21 +1,20 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* (default `0.0.0.0:4000`) and accepts only the `Host` headers in
|
|
11
|
-
* `NYLORUN_ALLOWED_HOSTS`, plus the loopback forms of the listen port for
|
|
12
|
-
* probes from inside the container.
|
|
13
|
-
*
|
|
14
|
-
* The Postgres, Restate and S2 endpoints are parsed and validated here;
|
|
15
|
-
* `infra/*` builds the clients from them.
|
|
16
|
-
*/
|
|
1
|
+
/** A Runtime service this release has. */
|
|
2
|
+
export type RuntimeService = "core" | "loop" | "gates" | "keys";
|
|
3
|
+
export type RuntimeServices = ReadonlySet<RuntimeService>;
|
|
4
|
+
export declare const RUNTIME_SERVICES: readonly RuntimeService[];
|
|
5
|
+
/** Where the gates service listens by default. */
|
|
6
|
+
export declare const DEFAULT_GATES_LISTEN_PORT = 4100;
|
|
7
|
+
/** What a process runs without `--service`: core and loop, as `--role all` did. */
|
|
8
|
+
export declare const DEFAULT_SERVICES: RuntimeServices;
|
|
9
|
+
/** The deprecated `--role` values, and the services each stands for. */
|
|
17
10
|
export type RuntimeRole = "api" | "worker" | "all";
|
|
18
|
-
|
|
11
|
+
/** `services` as the `--service` value that selects them, e.g. `core,loop`. */
|
|
12
|
+
export declare function describeServices(services: RuntimeServices): string;
|
|
13
|
+
export interface ServiceSelection {
|
|
14
|
+
services: RuntimeServices;
|
|
15
|
+
/** Set when the process was started with the deprecated `--role`. */
|
|
16
|
+
deprecatedRole?: RuntimeRole;
|
|
17
|
+
}
|
|
19
18
|
export declare const DEFAULT_CONTAINER_LISTEN_HOST = "0.0.0.0";
|
|
20
19
|
export declare const DEFAULT_CONTAINER_LISTEN_PORT = 4000;
|
|
21
20
|
export interface ContainerListen {
|
|
@@ -43,8 +42,43 @@ export interface StackEndpoints {
|
|
|
43
42
|
*/
|
|
44
43
|
restateIdentityKeys?: string[];
|
|
45
44
|
}
|
|
45
|
+
/** The gates service's listener and the token callers must present (`NYLORUN_GATES_*`). */
|
|
46
|
+
export interface GatesConfig {
|
|
47
|
+
listen: ContainerListen;
|
|
48
|
+
/** `NYLORUN_GATES_TOKEN`: the bearer the loop presents; at least 32 bytes as hex. */
|
|
49
|
+
token: string;
|
|
50
|
+
}
|
|
51
|
+
/** Where the loop reaches the gates service (`NYLORUN_GATES_URL`, `NYLORUN_GATES_TOKEN`). */
|
|
52
|
+
export interface ModelGateEndpoint {
|
|
53
|
+
url: string;
|
|
54
|
+
token: string;
|
|
55
|
+
}
|
|
46
56
|
export interface StackConfig {
|
|
47
|
-
|
|
57
|
+
/** The Runtime services this process runs. */
|
|
58
|
+
services: RuntimeServices;
|
|
59
|
+
/** Present when the process runs gates. */
|
|
60
|
+
gates?: GatesConfig;
|
|
61
|
+
/**
|
|
62
|
+
* Present when the process runs core or loop and `NYLORUN_GATES_URL` is set: its model calls,
|
|
63
|
+
* remote MCP calls and Action deliveries cross the gates service. Required for loop in
|
|
64
|
+
* container mode; outside a container (a development Host, tests) the loop may make them in
|
|
65
|
+
* its own process.
|
|
66
|
+
*/
|
|
67
|
+
modelGate?: ModelGateEndpoint;
|
|
68
|
+
/**
|
|
69
|
+
* Present when the process runs core or loop and `NYLORUN_KEYS_URL`, or else
|
|
70
|
+
* `NYLORUN_GATES_URL`, is set (F4.2): its vault writes and token signing cross the gateway's
|
|
71
|
+
* keys service, with `NYLORUN_GATES_TOKEN`, and it never reads the vault key. A loop in a
|
|
72
|
+
* container always has it, since it requires `NYLORUN_GATES_URL`.
|
|
73
|
+
*/
|
|
74
|
+
keys?: ModelGateEndpoint;
|
|
75
|
+
/**
|
|
76
|
+
* How the stack packs services into containers (`NYLORUN_PACKING`), for the startup log:
|
|
77
|
+
* `combined` (the local stack: runtime and gateway) or `split` (one container per service).
|
|
78
|
+
*/
|
|
79
|
+
packing?: "combined" | "split";
|
|
80
|
+
/** Set when the process was started with the deprecated `--role` (logged at startup). */
|
|
81
|
+
deprecatedRole?: RuntimeRole;
|
|
48
82
|
/** Present in container mode; absent means bind what host.json names. */
|
|
49
83
|
listen?: ContainerListen;
|
|
50
84
|
endpoints: StackEndpoints;
|
|
@@ -54,12 +88,50 @@ export interface StackConfig {
|
|
|
54
88
|
* means nothing outside the container.
|
|
55
89
|
*/
|
|
56
90
|
publicUrl?: string;
|
|
91
|
+
/**
|
|
92
|
+
* `NYLORUN_BROWSER_ACCESS` (`on` or `off`): whether browser requests may reach Tenant
|
|
93
|
+
* routes. Absent means the Host's default (on in container mode).
|
|
94
|
+
*/
|
|
95
|
+
browserAccess?: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* The operator listener in container mode (`NYLORUN_ADMIN_LISTEN_PORT`, `…_HOST`,
|
|
98
|
+
* `…_ALLOWED_HOSTS`). Absent: one listener serves the Admin API and the Tenant API.
|
|
99
|
+
*/
|
|
100
|
+
operator?: ContainerListen;
|
|
101
|
+
/**
|
|
102
|
+
* How the Runtime may call Action endpoints: `NYLORUN_ENDPOINT_LOOPBACK=docker-host` (the local
|
|
103
|
+
* stack: `localhost` means the machine that runs Docker), `NYLORUN_ENDPOINT_PRIVATE`
|
|
104
|
+
* (`allow` or `refuse`) and `NYLORUN_ENDPOINT_HTTP` (`allow` or `refuse`).
|
|
105
|
+
*/
|
|
106
|
+
delivery?: {
|
|
107
|
+
loopback?: "docker-host";
|
|
108
|
+
privateAddresses?: "allow" | "refuse";
|
|
109
|
+
allowHttp?: boolean;
|
|
110
|
+
};
|
|
111
|
+
/** Present when the process runs core or loop: who its Tenant is on first start. */
|
|
112
|
+
tenant?: TenantSettings;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The Tenant a Host creates when its database holds none (tenancy.md §4). Later starts open the
|
|
116
|
+
* Tenant the database holds; only principals missing from it are added.
|
|
117
|
+
*/
|
|
118
|
+
export interface TenantSettings {
|
|
119
|
+
/** `NYLORUN_TENANT_ID`: the new Tenant's id. Default: a new id. */
|
|
120
|
+
id?: string;
|
|
121
|
+
/** `NYLORUN_TENANT_NAME`. Default `default`. */
|
|
122
|
+
name: string;
|
|
123
|
+
/**
|
|
124
|
+
* `NYLORUN_DERIVED_PRINCIPALS`: comma-separated application principals whose keys the admin
|
|
125
|
+
* key derives (`deriveTenantKey`). Default `project`.
|
|
126
|
+
*/
|
|
127
|
+
derivedPrincipals: readonly string[];
|
|
57
128
|
}
|
|
58
129
|
export declare class StackConfigError extends Error {
|
|
59
130
|
constructor(message: string);
|
|
60
131
|
}
|
|
61
132
|
type EnvSnapshot = Readonly<Record<string, string | undefined>>;
|
|
62
|
-
|
|
133
|
+
/** Parses `--service a,b` (or the deprecated `--role`); throws `StackConfigError`. */
|
|
134
|
+
export declare function parseServices(argv: readonly string[]): ServiceSelection;
|
|
63
135
|
/**
|
|
64
136
|
* Normalize one `Host` header allowlist entry to lowercase `name:port`.
|
|
65
137
|
* IPv6 literals must be bracketed (`[::1]:4000`).
|