@nylorun/runtime 0.15.1-beta → 0.16.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 +82 -0
- package/README.md +33 -6
- package/dist/adapters/execution/restate.d.ts +9 -1
- package/dist/adapters/execution/restate.js +64 -5
- package/dist/adapters/replication/pgoutput.js +3 -1
- package/dist/adapters/sandbox/local.d.ts +10 -0
- package/dist/adapters/sandbox/local.js +195 -0
- package/dist/adapters/sandbox/virtual.js +36 -1
- package/dist/admin-openapi.json +37 -3
- package/dist/api/a2a/tasks.js +6 -1
- package/dist/api/ag-ui/history.js +6 -1
- package/dist/api/ag-ui/routes.js +5 -1
- package/dist/api/components.d.ts +2673 -655
- package/dist/api/components.js +17 -1
- package/dist/api/gate/keys.d.ts +4 -3
- package/dist/api/gate/routes.d.ts +19 -3
- package/dist/api/gate/routes.js +162 -53
- package/dist/api/http/app.js +4 -0
- package/dist/api/http/define.d.ts +10 -0
- package/dist/api/http/define.js +2 -1
- package/dist/api/http/routes/artifacts.d.ts +11 -0
- package/dist/api/http/routes/artifacts.js +518 -0
- package/dist/api/http/routes/sandboxes.d.ts +9 -0
- package/dist/api/http/routes/sandboxes.js +152 -0
- package/dist/api/http/routes/sessions.js +3 -1
- package/dist/api/http/routes/tenant.js +36 -2
- package/dist/artifacts/config.d.ts +23 -0
- package/dist/artifacts/config.js +27 -0
- package/dist/artifacts/export.d.ts +8 -0
- package/dist/artifacts/export.js +205 -0
- package/dist/artifacts/files.d.ts +36 -0
- package/dist/artifacts/files.js +45 -0
- package/dist/artifacts/folders.d.ts +60 -0
- package/dist/artifacts/folders.js +209 -0
- package/dist/artifacts/links.d.ts +28 -0
- package/dist/artifacts/links.js +92 -0
- package/dist/artifacts/media-types.d.ts +13 -0
- package/dist/artifacts/media-types.js +80 -0
- package/dist/artifacts/parts.d.ts +34 -0
- package/dist/artifacts/parts.js +38 -0
- package/dist/artifacts/service.d.ts +74 -0
- package/dist/artifacts/service.js +301 -0
- package/dist/artifacts/workspace.d.ts +60 -0
- package/dist/artifacts/workspace.js +57 -0
- package/dist/artifacts/zip.d.ts +10 -0
- package/dist/artifacts/zip.js +126 -0
- package/dist/blob/fs.d.ts +13 -0
- package/dist/blob/fs.js +194 -0
- package/dist/blob/index.d.ts +9 -0
- package/dist/blob/index.js +9 -0
- package/dist/blob/s3.d.ts +42 -0
- package/dist/blob/s3.js +366 -0
- package/dist/blob/types.d.ts +108 -0
- package/dist/blob/types.js +85 -0
- package/dist/core/sandbox-routes.d.ts +2 -2
- package/dist/core/sandbox-routes.js +4 -3
- package/dist/execution/memory.d.ts +11 -1
- package/dist/execution/memory.js +77 -3
- package/dist/execution/types.d.ts +54 -1
- package/dist/execution/types.js +31 -0
- package/dist/gates/contract.d.ts +14 -13
- package/dist/gates/contract.js +13 -12
- package/dist/gates/egress.d.ts +72 -0
- package/dist/gates/egress.js +319 -0
- package/dist/gates/handler.d.ts +3 -0
- package/dist/gates/handler.js +3 -2
- package/dist/gates/http-client.d.ts +3 -2
- package/dist/gates/http-client.js +17 -6
- package/dist/gates/in-process.d.ts +13 -1
- package/dist/gates/in-process.js +11 -0
- package/dist/gates/inflight.d.ts +23 -4
- package/dist/gates/inflight.js +18 -4
- package/dist/gates/model-gate.d.ts +6 -2
- package/dist/gates/tool-calls.d.ts +9 -5
- package/dist/gates/tool-calls.js +6 -6
- package/dist/gates/tool-client.d.ts +9 -2
- package/dist/gates/tool-client.js +56 -22
- package/dist/gates/tool-contract.d.ts +12 -7
- package/dist/gates/tool-contract.js +10 -4
- package/dist/gates/tool-gate.d.ts +1 -0
- package/dist/harness/calls.d.ts +52 -0
- package/dist/harness/calls.js +113 -0
- package/dist/harness/executors.d.ts +22 -0
- package/dist/harness/executors.js +96 -0
- package/dist/harness/main.d.ts +6 -0
- package/dist/harness/main.js +103 -0
- package/dist/harness/pod.d.ts +21 -0
- package/dist/harness/pod.js +119 -0
- package/dist/harness/run-tokens.d.ts +18 -0
- package/dist/harness/run-tokens.js +15 -0
- package/dist/harness/service.d.ts +46 -0
- package/dist/harness/service.js +179 -0
- package/dist/harness/ws-client.d.ts +43 -0
- package/dist/harness/ws-client.js +122 -0
- package/dist/harness/ws-port.d.ts +22 -0
- package/dist/harness/ws-port.js +78 -0
- package/dist/harness-api/in-process.d.ts +24 -0
- package/dist/harness-api/in-process.js +119 -0
- package/dist/harness-api/record.d.ts +44 -0
- package/dist/harness-api/record.js +250 -0
- package/dist/harness-api/renew.d.ts +37 -0
- package/dist/harness-api/renew.js +77 -0
- package/dist/harness-api/server.d.ts +104 -0
- package/dist/harness-api/server.js +408 -0
- package/dist/harness-api/start.d.ts +36 -0
- package/dist/harness-api/start.js +118 -0
- package/dist/harness-api/workspace.d.ts +53 -0
- package/dist/harness-api/workspace.js +201 -0
- package/dist/harness-api/ws-server.d.ts +34 -0
- package/dist/harness-api/ws-server.js +227 -0
- package/dist/host/app.js +23 -2
- package/dist/host/gates.d.ts +27 -2
- package/dist/host/gates.js +46 -1
- package/dist/host/http.d.ts +4 -1
- package/dist/host/http.js +6 -1
- package/dist/host/main.js +135 -27
- package/dist/host/stack-config.d.ts +101 -1
- package/dist/host/stack-config.js +199 -8
- package/dist/mcp/snapshot.d.ts +8 -1
- package/dist/mcp/snapshot.js +28 -0
- package/dist/media.d.ts +5 -24
- package/dist/media.js +5 -0
- package/dist/model/pi-model.d.ts +6 -2
- package/dist/model/pi-model.js +39 -22
- package/dist/node/index.d.ts +1 -1
- package/dist/node/index.js +1 -1
- package/dist/openapi.json +15245 -8850
- package/dist/record/index.d.ts +1 -0
- package/dist/record/index.js +1 -0
- package/dist/record/sandbox.d.ts +25 -0
- package/dist/record/sandbox.js +43 -0
- package/dist/sandbox/capability.d.ts +3 -0
- package/dist/sandbox/capability.js +10 -0
- package/dist/sandbox/egress-token.d.ts +26 -0
- package/dist/sandbox/egress-token.js +8 -0
- package/dist/sandbox/join.d.ts +37 -0
- package/dist/sandbox/join.js +165 -0
- package/dist/sandbox/manager.d.ts +99 -34
- package/dist/sandbox/manager.js +216 -84
- package/dist/sandbox/placement.d.ts +27 -0
- package/dist/sandbox/placement.js +44 -0
- package/dist/sandbox/pods/client.d.ts +90 -0
- package/dist/sandbox/pods/client.js +79 -0
- package/dist/sandbox/pods/lifecycle.d.ts +91 -0
- package/dist/sandbox/pods/lifecycle.js +178 -0
- package/dist/sandbox/pods/name.d.ts +1 -0
- package/dist/sandbox/pods/name.js +28 -0
- package/dist/sandbox/pods/network-gate.d.ts +25 -0
- package/dist/sandbox/pods/network-gate.js +71 -0
- package/dist/sandbox/pods/reconcile.d.ts +11 -0
- package/dist/sandbox/pods/reconcile.js +152 -0
- package/dist/sandbox/pods/spec.d.ts +45 -0
- package/dist/sandbox/pods/spec.js +62 -0
- package/dist/sandbox/records.d.ts +29 -0
- package/dist/sandbox/records.js +79 -0
- package/dist/sandbox/resolve.d.ts +5 -0
- package/dist/sandbox/resolve.js +3 -2
- package/dist/sandbox/session-sandbox.js +7 -5
- package/dist/sandbox/share.d.ts +11 -0
- package/dist/sandbox/share.js +10 -0
- package/dist/sandbox/tenant-config.d.ts +16 -1
- package/dist/sandbox/tenant-config.js +9 -0
- package/dist/sandbox/types.d.ts +29 -1
- package/dist/sandbox/types.js +13 -1
- package/dist/store/postgres/drizzle/0004_sandbox_resources.sql +20 -0
- package/dist/store/postgres/drizzle/0005_artifacts.sql +27 -0
- package/dist/store/postgres/drizzle/0006_folder_artifacts.sql +12 -0
- package/dist/store/postgres/drizzle/0007_sandbox_pods.sql +14 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
- package/dist/store/postgres/record-writer.d.ts +6 -1
- package/dist/store/postgres/record-writer.js +20 -2
- package/dist/store/postgres/schema.d.ts +894 -0
- package/dist/store/postgres/schema.js +130 -2
- package/dist/store/postgres/store.js +289 -4
- package/dist/store/types.d.ts +143 -6
- package/dist/streams/control.d.ts +2 -0
- package/dist/streams/control.js +5 -0
- package/dist/streams/types.d.ts +10 -1
- package/dist/tenant/advance.js +218 -160
- package/dist/tenant/artifact-tool.d.ts +5 -0
- package/dist/tenant/artifact-tool.js +119 -0
- package/dist/tenant/commands.js +30 -5
- package/dist/tenant/context.d.ts +64 -5
- package/dist/tenant/effects.d.ts +19 -13
- package/dist/tenant/effects.js +5 -382
- package/dist/tenant/history.d.ts +13 -23
- package/dist/tenant/history.js +14 -62
- package/dist/tenant/host-token.d.ts +92 -0
- package/dist/tenant/host-token.js +179 -0
- package/dist/tenant/module.js +1 -0
- package/dist/tenant/paths.d.ts +2 -1
- package/dist/tenant/paths.js +3 -1
- package/dist/tenant/reset.d.ts +5 -2
- package/dist/tenant/reset.js +22 -2
- package/dist/tenant/run-grants.d.ts +49 -0
- package/dist/tenant/run-grants.js +62 -0
- package/dist/tenant/run-token.d.ts +70 -0
- package/dist/tenant/run-token.js +138 -0
- package/dist/tenant/runtime.d.ts +38 -0
- package/dist/tenant/runtime.js +120 -32
- package/dist/tenant/sandboxes.d.ts +119 -0
- package/dist/tenant/sandboxes.js +551 -0
- package/dist/tenant/session.d.ts +2 -13
- package/dist/tenant/session.js +4 -28
- package/dist/tenant/sessions.d.ts +5 -0
- package/dist/tenant/sessions.js +28 -1
- package/dist/tenant/status.d.ts +8 -3
- package/dist/tenant/status.js +5 -0
- package/dist/tenant/streams.d.ts +7 -0
- package/dist/tenant/streams.js +24 -1
- package/dist/tenant/sweep.js +37 -4
- package/dist/tenant/tokens.js +9 -2
- package/dist/tenant/types.d.ts +18 -1
- package/dist/tenant/worker.d.ts +11 -5
- package/dist/tenant/worker.js +12 -6
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +13 -10
- package/dist/adapters/media.d.ts +0 -24
- package/dist/adapters/media.js +0 -111
package/dist/host/app.js
CHANGED
|
@@ -45,7 +45,10 @@ export function createHostApp(options) {
|
|
|
45
45
|
});
|
|
46
46
|
app.use(async (c, next) => {
|
|
47
47
|
const { incoming } = c.env;
|
|
48
|
-
if (requestHasBody(incoming) &&
|
|
48
|
+
if (requestHasBody(incoming) &&
|
|
49
|
+
!isJsonContentType(headerValue(incoming, "content-type")) &&
|
|
50
|
+
// An artifact upload's body is the file itself, of any type.
|
|
51
|
+
tenantRouteOf(incoming)?.bytes !== true)
|
|
49
52
|
return rejectedResponse(415, "unsupported_media_type", "Request bodies must use application/json");
|
|
50
53
|
await next();
|
|
51
54
|
});
|
|
@@ -100,7 +103,10 @@ export function createHostApp(options) {
|
|
|
100
103
|
return rejectedResponse(400, "invalid_request", `${PUBLISHABLE_KEY_HEADER} header is malformed`);
|
|
101
104
|
const tenantHeader = headerValue(incoming, TENANT_HEADER);
|
|
102
105
|
const named = tenantHeader === undefined || tenantHeader.trim() === "" ? undefined : tenantHeader.trim();
|
|
103
|
-
|
|
106
|
+
const protocol = headerValue(incoming, PROTOCOL_HEADER);
|
|
107
|
+
// A capability link is opened without the header; when one is sent, it is checked.
|
|
108
|
+
if (!protocolAccepted(protocol) &&
|
|
109
|
+
!(protocol === undefined && tenantRouteOf(incoming)?.unversioned === true))
|
|
104
110
|
return protocolRejectedResponse();
|
|
105
111
|
const resolution = await module.resolve();
|
|
106
112
|
if (resolution.kind !== "open") {
|
|
@@ -152,6 +158,21 @@ export function createHostApp(options) {
|
|
|
152
158
|
function browserRoute(method, segments) {
|
|
153
159
|
return findTenantRoute(method, segments)?.browser === true;
|
|
154
160
|
}
|
|
161
|
+
/** The Tenant route a request names, if any; a malformed path names none. */
|
|
162
|
+
function tenantRouteOf(incoming) {
|
|
163
|
+
const segments = [];
|
|
164
|
+
for (const segment of pathnameOf(incoming).split("/").filter(Boolean)) {
|
|
165
|
+
try {
|
|
166
|
+
segments.push(decodeURIComponent(segment));
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (segments[0] !== "v1" || segments[1] === "admin")
|
|
173
|
+
return undefined;
|
|
174
|
+
return findTenantRoute(incoming.method ?? "GET", segments);
|
|
175
|
+
}
|
|
155
176
|
/** The answer's status: a Response's, or the Node response's when it was written there. */
|
|
156
177
|
function statusOf(c) {
|
|
157
178
|
const logged = c.get("status");
|
package/dist/host/gates.d.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* the Tool Gate's routes (`api/gate/routes.ts`), behind the same `Host` check as the Runtime's
|
|
4
4
|
* listeners.
|
|
5
5
|
* It opens no Tenant runtime, no Restate endpoint and no stream relay, and runs no migration;
|
|
6
|
-
* it needs only the Postgres pool and the Host's tenant directory (read-only).
|
|
6
|
+
* it needs only the Postgres pool and the Host's tenant directory (read-only). Run tokens (F5)
|
|
7
|
+
* verify against the Tenant's public keys and session rows in that database.
|
|
7
8
|
*
|
|
8
9
|
* Nothing is written while a model call runs, so the server's request timeout sits above the
|
|
9
10
|
* gate's longest call. Closing stops accepting calls, lets running ones finish for up to
|
|
@@ -13,11 +14,13 @@ import { type Server } from "node:http";
|
|
|
13
14
|
import { type InflightCallsOptions } from "../gates/inflight.js";
|
|
14
15
|
import type { openMcpServer } from "../mcp/connect.js";
|
|
15
16
|
import type { OutboundPolicy } from "../tenant/outbound.js";
|
|
17
|
+
import { type BlobStore } from "../blob/index.js";
|
|
16
18
|
import type { ModelCallSettings } from "../gates/model-gate.js";
|
|
17
19
|
import { type TenantVaults } from "../gates/tenant-vaults.js";
|
|
18
20
|
import type { PostgresClient } from "../store/postgres/connect.js";
|
|
19
21
|
import type { Logger } from "../tenant/types.js";
|
|
20
|
-
import type { GatesConfig } from "./stack-config.js";
|
|
22
|
+
import type { EgressConfig, GatesConfig } from "./stack-config.js";
|
|
23
|
+
import { type EgressGate, type StartEgressGateOptions } from "../gates/egress.js";
|
|
21
24
|
/** Above the gate's 600 s provider request timeout and the loop's 630 s client timeout. */
|
|
22
25
|
export declare const GATES_REQUEST_TIMEOUT_MS = 660000;
|
|
23
26
|
export interface StartGatesOptions {
|
|
@@ -48,6 +51,11 @@ export interface StartGatesOptions {
|
|
|
48
51
|
* with the Tenant's vault key. The gateway is then not ready until the key file is there.
|
|
49
52
|
*/
|
|
50
53
|
readonly keys?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* The Object store model-gate reads the files a prompt names from (protocol 6): the stack's
|
|
56
|
+
* `s3` store. Without one, the Tenant's `fs` store under the Host root, when there is one.
|
|
57
|
+
*/
|
|
58
|
+
readonly blobs?: BlobStore;
|
|
51
59
|
}
|
|
52
60
|
export interface GatesServer {
|
|
53
61
|
/** Where the gate listens, e.g. `http://0.0.0.0:4100`. */
|
|
@@ -56,3 +64,20 @@ export interface GatesServer {
|
|
|
56
64
|
close(): Promise<void>;
|
|
57
65
|
}
|
|
58
66
|
export declare function startGates(options: StartGatesOptions): Promise<GatesServer>;
|
|
67
|
+
export interface StartEgressOptions {
|
|
68
|
+
readonly egress: EgressConfig;
|
|
69
|
+
readonly logger: Logger;
|
|
70
|
+
/** The pool for the Tenant's keys and sandboxes. Required unless `vaults` is given. */
|
|
71
|
+
readonly database?: PostgresClient;
|
|
72
|
+
/** The Host root. Required unless `vaults` is given. */
|
|
73
|
+
readonly hostRoot?: string;
|
|
74
|
+
/** Replaces the Postgres-backed Tenant (tests). */
|
|
75
|
+
readonly vaults?: TenantVaults;
|
|
76
|
+
/** Replaces how sandboxes are read, and the gate's limits and hooks (tests). */
|
|
77
|
+
readonly gate?: Partial<Omit<StartEgressGateOptions, "listen" | "logger" | "verify">>;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* egress-gate (`--service egress`, F7.2): pods' CONNECT proxy, verifying egress tokens against the
|
|
81
|
+
* Tenant's public keys and reading its sandboxes from the same database as the gates.
|
|
82
|
+
*/
|
|
83
|
+
export declare function startEgress(options: StartEgressOptions): Promise<EgressGate>;
|
package/dist/host/gates.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* the Tool Gate's routes (`api/gate/routes.ts`), behind the same `Host` check as the Runtime's
|
|
4
4
|
* listeners.
|
|
5
5
|
* It opens no Tenant runtime, no Restate endpoint and no stream relay, and runs no migration;
|
|
6
|
-
* it needs only the Postgres pool and the Host's tenant directory (read-only).
|
|
6
|
+
* it needs only the Postgres pool and the Host's tenant directory (read-only). Run tokens (F5)
|
|
7
|
+
* verify against the Tenant's public keys and session rows in that database.
|
|
7
8
|
*
|
|
8
9
|
* Nothing is written while a model call runs, so the server's request timeout sits above the
|
|
9
10
|
* gate's longest call. Closing stops accepting calls, lets running ones finish for up to
|
|
@@ -18,9 +19,13 @@ import { createMcpHandler, GATE_MCP_IDLE_MS } from "../gates/mcp-handler.js";
|
|
|
18
19
|
import { createToolCalls } from "../gates/tool-calls.js";
|
|
19
20
|
import { existsSync } from "node:fs";
|
|
20
21
|
import { tenantPaths } from "../tenant/paths.js";
|
|
22
|
+
import { staleRun, verifyRunToken } from "../tenant/run-token.js";
|
|
23
|
+
import { createFsBlobStore } from "../blob/index.js";
|
|
21
24
|
import { createTenantVaults } from "../gates/tenant-vaults.js";
|
|
22
25
|
import { probeDatabase } from "../infra/database.js";
|
|
23
26
|
import { bindListener, headerValue, isAllowedRequestHost, sendRejected } from "./http.js";
|
|
27
|
+
import { startEgressGate, storeEgressSandboxes } from "../gates/egress.js";
|
|
28
|
+
import { verifyEgressToken } from "../sandbox/egress-token.js";
|
|
24
29
|
/** Above the gate's 600 s provider request timeout and the loop's 630 s client timeout. */
|
|
25
30
|
export const GATES_REQUEST_TIMEOUT_MS = 660_000;
|
|
26
31
|
export async function startGates(options) {
|
|
@@ -31,6 +36,10 @@ export async function startGates(options) {
|
|
|
31
36
|
throw new Error("startGates needs the Postgres pool and the Host root");
|
|
32
37
|
vaults = createTenantVaults({ sql: database, hostRoot: options.hostRoot });
|
|
33
38
|
}
|
|
39
|
+
const blobs = options.blobs ??
|
|
40
|
+
(options.hostRoot !== undefined
|
|
41
|
+
? createFsBlobStore({ root: tenantPaths(options.hostRoot).blobs })
|
|
42
|
+
: undefined);
|
|
34
43
|
const inflight = createInflightCalls(options.inflight);
|
|
35
44
|
const mcpIdleMs = options.mcpIdleMs ?? GATE_MCP_IDLE_MS;
|
|
36
45
|
const mcp = createMcpHandler({
|
|
@@ -49,13 +58,24 @@ export async function startGates(options) {
|
|
|
49
58
|
sweep.unref();
|
|
50
59
|
const prune = setInterval(() => void toolCalls.prune(), 60 * 60_000);
|
|
51
60
|
prune.unref();
|
|
61
|
+
// Run tokens verify against the Tenant's public keys and session rows (F5): the same
|
|
62
|
+
// database the ledger and `tool_crossings` use.
|
|
63
|
+
const publicKeys = new Map();
|
|
52
64
|
const app = createGatesApp({
|
|
53
65
|
token: gates.token,
|
|
66
|
+
runs: {
|
|
67
|
+
async verify(raw) {
|
|
68
|
+
const vault = await vaults.open();
|
|
69
|
+
return verifyRunToken(vault.store, vault.tenantId, raw, publicKeys);
|
|
70
|
+
},
|
|
71
|
+
stale: async (claims) => staleRun((await vaults.open()).store, claims),
|
|
72
|
+
},
|
|
54
73
|
inflight,
|
|
55
74
|
modelGate: createModelCallHandler({
|
|
56
75
|
vaults,
|
|
57
76
|
logger,
|
|
58
77
|
...(options.settings ? { settings: options.settings } : {}),
|
|
78
|
+
...(blobs ? { blobs } : {}),
|
|
59
79
|
}),
|
|
60
80
|
ready: async () => {
|
|
61
81
|
// The keys service needs the vault key, which `nylorun start` writes; it never creates one.
|
|
@@ -125,3 +145,28 @@ export async function startGates(options) {
|
|
|
125
145
|
},
|
|
126
146
|
};
|
|
127
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* egress-gate (`--service egress`, F7.2): pods' CONNECT proxy, verifying egress tokens against the
|
|
150
|
+
* Tenant's public keys and reading its sandboxes from the same database as the gates.
|
|
151
|
+
*/
|
|
152
|
+
export async function startEgress(options) {
|
|
153
|
+
const { database } = options;
|
|
154
|
+
let vaults = options.vaults;
|
|
155
|
+
if (!vaults) {
|
|
156
|
+
if (!database || options.hostRoot === undefined)
|
|
157
|
+
throw new Error("startEgress needs the Postgres pool and the Host root");
|
|
158
|
+
vaults = createTenantVaults({ sql: database, hostRoot: options.hostRoot });
|
|
159
|
+
}
|
|
160
|
+
const tenant = vaults;
|
|
161
|
+
const publicKeys = new Map();
|
|
162
|
+
return startEgressGate({
|
|
163
|
+
listen: options.egress.listen,
|
|
164
|
+
logger: options.logger,
|
|
165
|
+
async verify(raw) {
|
|
166
|
+
const vault = await tenant.open();
|
|
167
|
+
return verifyEgressToken(vault.store, vault.tenantId, raw, publicKeys);
|
|
168
|
+
},
|
|
169
|
+
sandboxes: { live: async (sandboxId) => storeEgressSandboxes((await tenant.open()).store).live(sandboxId) },
|
|
170
|
+
...options.gate,
|
|
171
|
+
});
|
|
172
|
+
}
|
package/dist/host/http.d.ts
CHANGED
|
@@ -43,7 +43,10 @@ export declare function isJsonContentType(value: string | undefined): boolean;
|
|
|
43
43
|
export declare function requestHasBody(request: IncomingMessage): boolean;
|
|
44
44
|
/** `/health` and `/ready` are polled; the request log leaves them out. */
|
|
45
45
|
export declare function pathnameIsLogged(rawUrl: string | undefined): boolean;
|
|
46
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Redact path parameters after `/v1/sessions/` for Host logs, and a capability link's token
|
|
48
|
+
* (a credential) after `/v1/artifact-links/`.
|
|
49
|
+
*/
|
|
47
50
|
export declare function redactRoutePath(pathname: string): string;
|
|
48
51
|
/** Constant-time comparison of SHA-256 digests of the presented and stored admin keys. */
|
|
49
52
|
export declare function adminKeyMatches(presented: string | undefined, adminKey: string): boolean;
|
package/dist/host/http.js
CHANGED
|
@@ -135,13 +135,18 @@ export function pathnameIsLogged(rawUrl) {
|
|
|
135
135
|
const pathname = new URL(rawUrl, "http://runtime.local").pathname;
|
|
136
136
|
return pathname !== "/health" && pathname !== "/ready";
|
|
137
137
|
}
|
|
138
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* Redact path parameters after `/v1/sessions/` for Host logs, and a capability link's token
|
|
140
|
+
* (a credential) after `/v1/artifact-links/`.
|
|
141
|
+
*/
|
|
139
142
|
export function redactRoutePath(pathname) {
|
|
140
143
|
const parts = pathname.split("/");
|
|
141
144
|
// ["", "v1", "sessions", "<id>", ...]
|
|
142
145
|
if (parts[1] === "v1" && parts[2] === "sessions" && parts.length > 3) {
|
|
143
146
|
return `/v1/sessions/:id${parts.length > 4 ? "/…" : ""}`;
|
|
144
147
|
}
|
|
148
|
+
if (parts[1] === "v1" && parts[2] === "artifact-links" && parts.length > 3)
|
|
149
|
+
return "/v1/artifact-links/:token";
|
|
145
150
|
return pathname;
|
|
146
151
|
}
|
|
147
152
|
/** Constant-time comparison of SHA-256 digests of the presented and stored admin keys. */
|
package/dist/host/main.js
CHANGED
|
@@ -13,10 +13,15 @@
|
|
|
13
13
|
* `NYLORUN_DERIVED_PRINCIPALS`; tenancy.md §4); with S2, a process running core
|
|
14
14
|
* runs the stream relay, which feeds the Tenant's streams from the record over
|
|
15
15
|
* logical replication (one process at a time holds the slot); `/ready` reports
|
|
16
|
-
* the Tenant and the infrastructure checks.
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* the Tenant and the infrastructure checks. With an Object store
|
|
17
|
+
* (`NYLORUN_OBJECT_STORE_*`) the Tenant's blobs go to it through the `s3` BlobStore, and a
|
|
18
|
+
* process running core creates its bucket; without one they stay on disk (`fs`). A process
|
|
19
|
+
* running the gates service (the local stack's `gateway` container) starts only the gate, and egress-gate with `egress` (`runGates`): it needs
|
|
20
|
+
* neither host.json nor host-credentials.json, and reads the files a prompt names from the Object store. A process running the harness service (`--service harness`, F6.2)
|
|
21
|
+
* starts only the harness (`harness/main.ts`), which connects to core's Harness API. With `NYLORUN_HARNESS=remote`
|
|
22
|
+
* the Tenant runs no harness of its own: core starts the Harness API listener (`NYLORUN_HARNESS_LISTEN_*`) and
|
|
23
|
+
* harnesses that present `NYLORUN_HARNESS_TOKEN` run its segments and hold its workspaces. See the startup order
|
|
24
|
+
* in `main()`. Tests compose a Host without this entry, with `createHost` and an injected Tenant module.
|
|
20
25
|
*/
|
|
21
26
|
import { readFileSync, mkdirSync, existsSync } from "node:fs";
|
|
22
27
|
import { createRequire } from "node:module";
|
|
@@ -40,10 +45,15 @@ import { createHostLogger } from "./logger.js";
|
|
|
40
45
|
import { describeEndpoints, describeServices, parseStackConfig, } from "./stack-config.js";
|
|
41
46
|
import { createExecution, createInfra } from "../infra/index.js";
|
|
42
47
|
import { createDatabase } from "../infra/database.js";
|
|
43
|
-
import { startGates } from "./gates.js";
|
|
48
|
+
import { startEgress, startGates } from "./gates.js";
|
|
44
49
|
import { httpToolGate } from "../gates/tool-client.js";
|
|
45
50
|
import { httpKeys } from "../keys/client.js";
|
|
46
51
|
import { httpModelGate } from "../gates/http-client.js";
|
|
52
|
+
import { createS3BlobStore } from "../blob/index.js";
|
|
53
|
+
import { createRunGrants } from "../tenant/run-grants.js";
|
|
54
|
+
import { runHarness } from "../harness/main.js";
|
|
55
|
+
import { startHarnessListener } from "../harness-api/ws-server.js";
|
|
56
|
+
import { httpSandboxesClient } from "../sandbox/pods/client.js";
|
|
47
57
|
const nodeRequire = createRequire(import.meta.url);
|
|
48
58
|
function coreVersion() {
|
|
49
59
|
try {
|
|
@@ -61,38 +71,85 @@ function resolveHostRoot() {
|
|
|
61
71
|
const root = fromEnv && fromEnv.length > 0 ? fromEnv : resolve(homedir(), ".nylorun");
|
|
62
72
|
return resolve(root);
|
|
63
73
|
}
|
|
74
|
+
/** The Object store's `s3` BlobStore (D35), from `StackConfig.objectStore`. */
|
|
75
|
+
function objectStore(config) {
|
|
76
|
+
return createS3BlobStore({
|
|
77
|
+
endpoint: config.endpoint,
|
|
78
|
+
bucket: config.bucket,
|
|
79
|
+
region: config.region,
|
|
80
|
+
accessKeyId: config.accessKeyId,
|
|
81
|
+
secretAccessKey: config.secretAccessKey,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Creates the Object store's bucket unless it exists, retrying while the store starts. In the
|
|
86
|
+
* background: only artifacts use the store, so a store that stays down is logged (artifact
|
|
87
|
+
* uploads and downloads fail meanwhile) and does not hold up the Host.
|
|
88
|
+
*/
|
|
89
|
+
async function ensureBucket(store, bucket, logger) {
|
|
90
|
+
for (let attempt = 1;; attempt += 1) {
|
|
91
|
+
try {
|
|
92
|
+
await store.ensureBucket();
|
|
93
|
+
logger.info("object_store_ready", { bucket });
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
if (attempt >= 10) {
|
|
98
|
+
logger.error("object_store_unavailable", {
|
|
99
|
+
bucket,
|
|
100
|
+
message: error instanceof Error ? error.message : String(error),
|
|
101
|
+
});
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(1000 * attempt, 5000)));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
64
108
|
/** The slot's lag in WAL bytes, when the source can tell. */
|
|
65
109
|
async function lagOf(source) {
|
|
66
110
|
const lagBytes = await source.lag?.().catch(() => undefined);
|
|
67
111
|
return lagBytes === undefined ? {} : { lagBytes };
|
|
68
112
|
}
|
|
69
113
|
/**
|
|
70
|
-
* The
|
|
71
|
-
* directory. Writes nothing to the Host root (the local
|
|
114
|
+
* The gateway's services: the Model Gate's listener (gates, keys) and egress-gate's (egress), over
|
|
115
|
+
* the Postgres pool and the Host's tenant directory. Writes nothing to the Host root (the local
|
|
116
|
+
* stack mounts it read-only).
|
|
72
117
|
*/
|
|
73
118
|
async function runGates(stack) {
|
|
74
|
-
const gates = stack.gates;
|
|
75
119
|
if (!stack.endpoints.databaseUrl)
|
|
76
|
-
throw new Error(
|
|
120
|
+
throw new Error(`NYLORUN_DATABASE_URL is required for --service ${describeServices(stack.services)}: the gateway reads the Tenant from Postgres (\`nylorun start\` sets it)`);
|
|
77
121
|
const logger = createHostLogger();
|
|
78
122
|
logger.info("host_stack_config", {
|
|
79
123
|
services: [...stack.services],
|
|
80
124
|
...(stack.packing ? { packing: stack.packing } : {}),
|
|
81
125
|
endpoints: describeEndpoints(stack.endpoints),
|
|
126
|
+
objectStore: stack.objectStore ? "s3" : "none",
|
|
82
127
|
});
|
|
83
128
|
const database = createDatabase(stack);
|
|
84
|
-
|
|
129
|
+
const servers = [];
|
|
85
130
|
try {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
131
|
+
if (stack.gates) {
|
|
132
|
+
const server = await startGates({
|
|
133
|
+
gates: stack.gates,
|
|
134
|
+
database,
|
|
135
|
+
hostRoot: resolveHostRoot(),
|
|
136
|
+
logger,
|
|
137
|
+
...(stack.delivery ? { delivery: stack.delivery } : {}),
|
|
138
|
+
...(stack.services.has("keys") ? { keys: true } : {}),
|
|
139
|
+
// Model-gate reads the files a prompt names from the Object store (protocol 6).
|
|
140
|
+
...(stack.objectStore ? { blobs: objectStore(stack.objectStore) } : {}),
|
|
141
|
+
});
|
|
142
|
+
servers.push(server);
|
|
143
|
+
logger.info("gates_ready", { url: server.url });
|
|
144
|
+
}
|
|
145
|
+
if (stack.egress) {
|
|
146
|
+
const egress = await startEgress({ egress: stack.egress, database, hostRoot: resolveHostRoot(), logger });
|
|
147
|
+
servers.push(egress);
|
|
148
|
+
logger.info("egress_ready", { url: egress.url });
|
|
149
|
+
}
|
|
94
150
|
}
|
|
95
151
|
catch (error) {
|
|
152
|
+
await Promise.all(servers.map((server) => server.close()));
|
|
96
153
|
await database.end({ timeout: 5 });
|
|
97
154
|
if (error instanceof HostListenError) {
|
|
98
155
|
logger.error("listen_failed", { message: error.message, exitCode: error.exitCode });
|
|
@@ -101,23 +158,23 @@ async function runGates(stack) {
|
|
|
101
158
|
}
|
|
102
159
|
throw error;
|
|
103
160
|
}
|
|
104
|
-
logger.info("gates_ready", { url: server.url });
|
|
105
161
|
let stopping = false;
|
|
106
162
|
for (const signal of ["SIGINT", "SIGTERM"])
|
|
107
163
|
process.on(signal, () => {
|
|
108
164
|
if (stopping)
|
|
109
165
|
return;
|
|
110
166
|
stopping = true;
|
|
111
|
-
void server
|
|
112
|
-
.close()
|
|
167
|
+
void Promise.all(servers.map((server) => server.close()))
|
|
113
168
|
.then(() => database.end({ timeout: 5 }))
|
|
114
169
|
.finally(() => process.exit(0));
|
|
115
170
|
});
|
|
116
171
|
}
|
|
117
172
|
export async function main() {
|
|
118
173
|
const stack = parseStackConfig(process.env, process.argv.slice(2));
|
|
119
|
-
if (stack.services.has("gates") || stack.services.has("keys"))
|
|
174
|
+
if (stack.services.has("gates") || stack.services.has("keys") || stack.services.has("egress"))
|
|
120
175
|
return runGates(stack);
|
|
176
|
+
if (stack.services.has("harness"))
|
|
177
|
+
return runHarness(stack, baselineEnvironment(process.env));
|
|
121
178
|
const hostRoot = resolveHostRoot();
|
|
122
179
|
const paths = hostPaths(hostRoot);
|
|
123
180
|
mkdirSync(paths.home, { recursive: true });
|
|
@@ -150,8 +207,10 @@ export async function main() {
|
|
|
150
207
|
...(stack.services.has("loop")
|
|
151
208
|
? { modelGate: stack.modelGate ? stack.modelGate.url : "in-process" }
|
|
152
209
|
: {}),
|
|
210
|
+
harness: stack.harnessMode ?? "in-process",
|
|
153
211
|
mode: stack.listen ? "container" : "local",
|
|
154
212
|
endpoints: describeEndpoints(stack.endpoints),
|
|
213
|
+
objectStore: stack.objectStore ? "s3" : "fs",
|
|
155
214
|
});
|
|
156
215
|
const infra = createInfra(stack, { logger });
|
|
157
216
|
const database = infra.database;
|
|
@@ -206,15 +265,31 @@ export async function main() {
|
|
|
206
265
|
};
|
|
207
266
|
// With the gates service the Tenant's vault-backed model calls, remote MCP calls and Action
|
|
208
267
|
// deliveries cross it, and this process never reads a model or MCP credential.
|
|
209
|
-
|
|
210
|
-
|
|
268
|
+
// Their credentials (F5): each advance's run token for its session's model and MCP calls,
|
|
269
|
+
// core's credential (NYLORUN_GATES_TOKEN) for deliveries and MCP requests outside a run.
|
|
270
|
+
const runGrants = stack.modelGate ? createRunGrants() : undefined;
|
|
271
|
+
const modelGate = stack.modelGate && runGrants
|
|
272
|
+
? httpModelGate({ url: stack.modelGate.url, runTokens: runGrants })
|
|
211
273
|
: undefined;
|
|
212
|
-
const toolGate = stack.modelGate
|
|
213
|
-
? httpToolGate({ url: stack.modelGate.url, token: stack.modelGate.token })
|
|
274
|
+
const toolGate = stack.modelGate && runGrants
|
|
275
|
+
? httpToolGate({ url: stack.modelGate.url, token: stack.modelGate.token, runTokens: runGrants })
|
|
214
276
|
: undefined;
|
|
215
277
|
// With the keys service, vault writes and token signing cross it, and this process never
|
|
216
278
|
// reads the vault key (F4.2).
|
|
217
279
|
const keys = stack.keys ? httpKeys({ url: stack.keys.url, token: stack.keys.token }) : undefined;
|
|
280
|
+
// The Object store (D35): the stack's S3 server through the BlobStore seam, handed to the
|
|
281
|
+
// Tenant (`TenantContext.blobs`). Without one the Tenant keeps blobs on disk. core creates
|
|
282
|
+
// the bucket.
|
|
283
|
+
const blobs = stack.objectStore ? objectStore(stack.objectStore) : undefined;
|
|
284
|
+
if (blobs && stack.services.has("core"))
|
|
285
|
+
void ensureBucket(blobs, stack.objectStore.bucket, logger);
|
|
286
|
+
// Sandbox pods (F7.2): the sandboxes service, which alone holds the cluster's credentials.
|
|
287
|
+
const pods = stack.sandboxes
|
|
288
|
+
? {
|
|
289
|
+
client: httpSandboxesClient({ url: stack.sandboxes.url, token: stack.sandboxes.token }),
|
|
290
|
+
harnessImage: stack.sandboxes.harnessImage,
|
|
291
|
+
}
|
|
292
|
+
: undefined;
|
|
218
293
|
const tenantSettings = stack.tenant ?? { name: "default", derivedPrincipals: ["project"] };
|
|
219
294
|
const module = createTenantModule({
|
|
220
295
|
open: createPostgresTenantOpener({
|
|
@@ -234,8 +309,12 @@ export async function main() {
|
|
|
234
309
|
execution: hostExecution.tenantExecution,
|
|
235
310
|
...(modelGate ? { modelGate } : {}),
|
|
236
311
|
...(toolGate ? { toolGate } : {}),
|
|
312
|
+
...(runGrants ? { runGrants } : {}),
|
|
237
313
|
...(keys ? { keys } : {}),
|
|
238
314
|
...(streams ? { streams, hostRelay: true } : {}),
|
|
315
|
+
...(blobs ? { blobs } : {}),
|
|
316
|
+
...(stack.harnessMode === "remote" ? { harness: "remote" } : {}),
|
|
317
|
+
...(pods ? { pods } : {}),
|
|
239
318
|
...opened,
|
|
240
319
|
}),
|
|
241
320
|
}),
|
|
@@ -273,8 +352,12 @@ export async function main() {
|
|
|
273
352
|
beforeTenants: async () => {
|
|
274
353
|
await hostExecution.stop();
|
|
275
354
|
await relay?.stop();
|
|
355
|
+
// The harnesses' runs stop with the Tenant's advances; their connections close after.
|
|
356
|
+
},
|
|
357
|
+
afterTenants: async () => {
|
|
358
|
+
await harnessListener?.close();
|
|
359
|
+
await infra.close();
|
|
276
360
|
},
|
|
277
|
-
afterTenants: () => infra.close(),
|
|
278
361
|
},
|
|
279
362
|
};
|
|
280
363
|
const host = createHost(options);
|
|
@@ -289,11 +372,35 @@ export async function main() {
|
|
|
289
372
|
// Restate's state (§14.8), and starts the stream relay. A Tenant that cannot
|
|
290
373
|
// be opened leaves the Host listening but not ready, with the cause in
|
|
291
374
|
// `/v1/admin/status`.
|
|
375
|
+
// The Harness API listener (NYLORUN_HARNESS=remote): only harnesses connect here, with the
|
|
376
|
+
// harness credential, and only while the Tenant is open.
|
|
377
|
+
let harnessListener;
|
|
292
378
|
try {
|
|
293
379
|
await hostExecution.start();
|
|
380
|
+
if (stack.harnessListener)
|
|
381
|
+
harnessListener = await startHarnessListener({
|
|
382
|
+
...stack.harnessListener.listen,
|
|
383
|
+
...(stack.harnessListener.token ? { token: stack.harnessListener.token } : {}),
|
|
384
|
+
// Sandbox pods join here and connect with host tokens (F7.2).
|
|
385
|
+
...(pods
|
|
386
|
+
? {
|
|
387
|
+
hosts: async () => {
|
|
388
|
+
const resolved = await module.resolve();
|
|
389
|
+
return resolved.kind === "open" ? resolved.handle.hostAuthority?.() : undefined;
|
|
390
|
+
},
|
|
391
|
+
}
|
|
392
|
+
: {}),
|
|
393
|
+
attach: async () => {
|
|
394
|
+
const resolved = await module.resolve();
|
|
395
|
+
const handle = resolved.kind === "open" ? resolved.handle : undefined;
|
|
396
|
+
return handle?.attachHarness ? (channel, peer) => handle.attachHarness(channel, peer) : undefined;
|
|
397
|
+
},
|
|
398
|
+
logger,
|
|
399
|
+
});
|
|
294
400
|
await host.listen();
|
|
295
401
|
}
|
|
296
402
|
catch (error) {
|
|
403
|
+
await harnessListener?.close().catch(() => undefined);
|
|
297
404
|
await hostExecution.stop().catch(() => undefined);
|
|
298
405
|
await module.close().catch(() => undefined);
|
|
299
406
|
await relay?.stop().catch(() => undefined);
|
|
@@ -311,6 +418,7 @@ export async function main() {
|
|
|
311
418
|
const tenant = module.tenant();
|
|
312
419
|
logger.info("host_ready", {
|
|
313
420
|
url: host.url,
|
|
421
|
+
...(harnessListener ? { harness: harnessListener.url } : {}),
|
|
314
422
|
hostId: config.hostId,
|
|
315
423
|
tenantId: tenant.id,
|
|
316
424
|
tenant: tenant.state,
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
import { type BlockedAddress } from "../sandbox/pods/network-gate.js";
|
|
1
2
|
/** A Runtime service this release has. */
|
|
2
|
-
export type RuntimeService = "core" | "loop" | "gates" | "keys";
|
|
3
|
+
export type RuntimeService = "core" | "loop" | "gates" | "keys" | "egress" | "harness";
|
|
3
4
|
export type RuntimeServices = ReadonlySet<RuntimeService>;
|
|
4
5
|
export declare const RUNTIME_SERVICES: readonly RuntimeService[];
|
|
5
6
|
/** Where the gates service listens by default. */
|
|
6
7
|
export declare const DEFAULT_GATES_LISTEN_PORT = 4100;
|
|
8
|
+
/** Where egress-gate listens by default. */
|
|
9
|
+
export declare const DEFAULT_EGRESS_LISTEN_PORT = 4200;
|
|
10
|
+
/** Where core's Harness API listener listens by default. */
|
|
11
|
+
export declare const DEFAULT_HARNESS_LISTEN_PORT = 4200;
|
|
12
|
+
/** Where a harness process answers `/health` (loopback only). */
|
|
13
|
+
export declare const DEFAULT_HARNESS_HEALTH_PORT = 4300;
|
|
7
14
|
/** What a process runs without `--service`: core and loop, as `--role all` did. */
|
|
8
15
|
export declare const DEFAULT_SERVICES: RuntimeServices;
|
|
9
16
|
/** The deprecated `--role` values, and the services each stands for. */
|
|
@@ -48,16 +55,90 @@ export interface GatesConfig {
|
|
|
48
55
|
/** `NYLORUN_GATES_TOKEN`: the bearer the loop presents; at least 32 bytes as hex. */
|
|
49
56
|
token: string;
|
|
50
57
|
}
|
|
58
|
+
/** egress-gate's listener (`NYLORUN_EGRESS_LISTEN_HOST`, `NYLORUN_EGRESS_LISTEN_PORT`). */
|
|
59
|
+
export interface EgressConfig {
|
|
60
|
+
listen: {
|
|
61
|
+
host: string;
|
|
62
|
+
port: number;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
51
65
|
/** Where the loop reaches the gates service (`NYLORUN_GATES_URL`, `NYLORUN_GATES_TOKEN`). */
|
|
52
66
|
export interface ModelGateEndpoint {
|
|
53
67
|
url: string;
|
|
54
68
|
token: string;
|
|
55
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Core's Harness API listener (`NYLORUN_HARNESS=remote`, or sandbox pods): where harnesses
|
|
72
|
+
* connect, and the credential they present (`NYLORUN_HARNESS_TOKEN`), which nothing else
|
|
73
|
+
* accepts. With sandbox pods and no harness container there is no token: only pods' host tokens
|
|
74
|
+
* are accepted (F7.2).
|
|
75
|
+
*/
|
|
76
|
+
export interface HarnessListenConfig {
|
|
77
|
+
listen: ContainerListen;
|
|
78
|
+
token?: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Sandbox pods (`NYLORUN_SANDBOXES_URL`, F7.2): the sandboxes service core drives them with, its
|
|
82
|
+
* token, and the Runtime image pods copy the engine from.
|
|
83
|
+
*/
|
|
84
|
+
export interface SandboxesConfig {
|
|
85
|
+
url: string;
|
|
86
|
+
token: string;
|
|
87
|
+
/** `NYLORUN_SANDBOX_HARNESS_IMAGE` (the stack sets it to the Runtime image). */
|
|
88
|
+
harnessImage: string;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The engine in a pod sandbox (`--service harness` with `NYLORUN_SANDBOX_KIND=pod`, F7.2): no
|
|
92
|
+
* harness token; it joins with the join token mounted in the pod.
|
|
93
|
+
*/
|
|
94
|
+
export interface PodHostConfig {
|
|
95
|
+
/** `NYLORUN_SANDBOX_ID`. */
|
|
96
|
+
sandboxId: string;
|
|
97
|
+
/** `NYLORUN_POD_UID` (the downward API's `metadata.uid`). */
|
|
98
|
+
podUid: string;
|
|
99
|
+
/** `NYLORUN_SANDBOX_JOIN_FILE`. */
|
|
100
|
+
joinFile: string;
|
|
101
|
+
/** The Harness API listener's HTTP base, for `host/join` and `host/renew`. */
|
|
102
|
+
httpUrl: string;
|
|
103
|
+
/** `NYLORUN_EGRESS_PROXY`: egress-gate's address, or undefined while it is not published. */
|
|
104
|
+
egressProxy?: string;
|
|
105
|
+
/** Addresses the pod's NetworkPolicy must block, probed before the engine starts. */
|
|
106
|
+
blocked: BlockedAddress[];
|
|
107
|
+
}
|
|
108
|
+
/** A harness process (`--service harness`). */
|
|
109
|
+
export interface HarnessServiceConfig {
|
|
110
|
+
/** `NYLORUN_HARNESS_URL`: core's Harness API, e.g. `ws://runtime:4200/nylorun/harness/v1`. */
|
|
111
|
+
url: string;
|
|
112
|
+
/** `NYLORUN_HARNESS_TOKEN`; absent for a pod's engine, which joins (`pod`). */
|
|
113
|
+
token?: string;
|
|
114
|
+
/** Present for the engine in a pod sandbox. */
|
|
115
|
+
pod?: PodHostConfig;
|
|
116
|
+
/** `NYLORUN_GATES_URL`: the gates service its model and MCP calls cross, with run tokens. */
|
|
117
|
+
gatesUrl: string;
|
|
118
|
+
/** `NYLORUN_HARNESS_ROOT`: its sandboxes, plugin data, home and tmp. Default `/harness`. */
|
|
119
|
+
root: string;
|
|
120
|
+
/** `/health` on `127.0.0.1:<NYLORUN_HARNESS_HEALTH_PORT>` (default 4300). */
|
|
121
|
+
healthPort: number;
|
|
122
|
+
}
|
|
56
123
|
export interface StackConfig {
|
|
57
124
|
/** The Runtime services this process runs. */
|
|
58
125
|
services: RuntimeServices;
|
|
59
126
|
/** Present when the process runs gates. */
|
|
60
127
|
gates?: GatesConfig;
|
|
128
|
+
/** Present when the process runs egress. */
|
|
129
|
+
egress?: EgressConfig;
|
|
130
|
+
/** Present when the process runs the harness service (`--service harness`). */
|
|
131
|
+
harness?: HarnessServiceConfig;
|
|
132
|
+
/**
|
|
133
|
+
* `NYLORUN_HARNESS` (`in-process` or `remote`; absent means `in-process`), for a process running
|
|
134
|
+
* core or loop: whether the Tenant runs its own harness, or harnesses connect to
|
|
135
|
+
* `harnessListener`.
|
|
136
|
+
*/
|
|
137
|
+
harnessMode?: "in-process" | "remote";
|
|
138
|
+
/** Present when `harnessMode` is `remote`, or with `sandboxes`: core's Harness API listener. */
|
|
139
|
+
harnessListener?: HarnessListenConfig;
|
|
140
|
+
/** Present when the process runs core or loop and `NYLORUN_SANDBOXES_URL` is set (F7.2). */
|
|
141
|
+
sandboxes?: SandboxesConfig;
|
|
61
142
|
/**
|
|
62
143
|
* Present when the process runs core or loop and `NYLORUN_GATES_URL` is set: its model calls,
|
|
63
144
|
* remote MCP calls and Action deliveries cross the gates service. Required for loop in
|
|
@@ -110,6 +191,25 @@ export interface StackConfig {
|
|
|
110
191
|
};
|
|
111
192
|
/** Present when the process runs core or loop: who its Tenant is on first start. */
|
|
112
193
|
tenant?: TenantSettings;
|
|
194
|
+
/**
|
|
195
|
+
* The Object store (blueprint D35) behind the `BlobStore` seam's `s3` adapter, from
|
|
196
|
+
* `NYLORUN_OBJECT_STORE_*`; the local stack's RustFS. Absent: the Tenant keeps blobs on disk
|
|
197
|
+
* (the `fs` adapter, `TenantPaths.blobs`).
|
|
198
|
+
*/
|
|
199
|
+
objectStore?: ObjectStoreConfig;
|
|
200
|
+
}
|
|
201
|
+
/** Where the Object store is and the credential for it (`NYLORUN_OBJECT_STORE_*`). */
|
|
202
|
+
export interface ObjectStoreConfig {
|
|
203
|
+
/** `NYLORUN_OBJECT_STORE_ENDPOINT`: the S3 endpoint, e.g. `http://rustfs:9000`. */
|
|
204
|
+
endpoint: string;
|
|
205
|
+
/** `NYLORUN_OBJECT_STORE_BUCKET`. Default `nylorun`. */
|
|
206
|
+
bucket: string;
|
|
207
|
+
/** `NYLORUN_OBJECT_STORE_REGION`. Default `us-east-1`. */
|
|
208
|
+
region: string;
|
|
209
|
+
/** `NYLORUN_OBJECT_STORE_ACCESS_KEY`. */
|
|
210
|
+
accessKeyId: string;
|
|
211
|
+
/** `NYLORUN_OBJECT_STORE_SECRET_KEY`. */
|
|
212
|
+
secretAccessKey: string;
|
|
113
213
|
}
|
|
114
214
|
/**
|
|
115
215
|
* The Tenant a Host creates when its database holds none (tenancy.md §4). Later starts open the
|