nylorun 0.7.0-beta → 0.9.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 +109 -0
- package/README.md +95 -25
- package/dist/cli.js +17 -2
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +10 -0
- package/dist/project/link.d.ts +13 -7
- package/dist/project/link.js +13 -2
- package/dist/sandbox/cluster-file.d.ts +47 -0
- package/dist/sandbox/cluster-file.js +55 -0
- package/dist/sandbox/commands.d.ts +41 -0
- package/dist/sandbox/commands.js +281 -0
- package/dist/sandbox/kubectl.d.ts +39 -0
- package/dist/sandbox/kubectl.js +70 -0
- package/dist/sandbox/manifests.d.ts +41 -0
- package/dist/sandbox/manifests.js +138 -0
- package/dist/sandbox/probe.d.ts +44 -0
- package/dist/sandbox/probe.js +137 -0
- package/dist/stack/commands.d.ts +71 -1
- package/dist/stack/commands.js +209 -38
- package/dist/stack/compose-file.d.ts +47 -7
- package/dist/stack/compose-file.js +223 -18
- package/dist/stack/env-file.d.ts +50 -14
- package/dist/stack/env-file.js +87 -40
- package/dist/stack/host-files.js +11 -1
- package/dist/stack/images.d.ts +8 -2
- package/dist/stack/images.js +8 -2
- package/dist/stack/index.d.ts +9 -0
- package/dist/stack/index.js +18 -1
- package/dist/stack/keys.d.ts +4 -0
- package/dist/stack/keys.js +72 -0
- package/dist/stack/mcp.d.ts +8 -0
- package/dist/stack/mcp.js +114 -0
- package/dist/stack/operator-keys.d.ts +39 -0
- package/dist/stack/operator-keys.js +146 -0
- package/dist/stack/paths.d.ts +32 -0
- package/dist/stack/paths.js +11 -0
- package/dist/stack/prepare.d.ts +12 -6
- package/dist/stack/prepare.js +24 -6
- package/dist/stack/sandbox.d.ts +4 -0
- package/dist/stack/sandbox.js +106 -0
- package/package.json +5 -5
- package/dist/project/derived-key.d.ts +0 -8
- package/dist/project/derived-key.js +0 -18
|
@@ -6,16 +6,15 @@ import { PINNED_IMAGES } from "./images.js";
|
|
|
6
6
|
* network and ports. Container, network and volume names are global on the Docker engine, so
|
|
7
7
|
* every one is `<project>-<role>` (`nylorun-shop-studio`, network `nylorun-shop`, volume
|
|
8
8
|
* `nylorun-shop-postgres`), labelled `dev.nylorun.tenant: <name>`; they are written here, from
|
|
9
|
-
* the project and the Tenant's name. Everything else that varies (ports, the Postgres password
|
|
10
|
-
* UID/GID, the Host root, the images) comes from `docker/.env`.
|
|
9
|
+
* the project and the Tenant's name. Everything else that varies (ports, the Postgres password and the other
|
|
10
|
+
* secrets, UID/GID, the Host root, the images) comes from `docker/.env`.
|
|
11
11
|
*
|
|
12
12
|
* The Tenant's state is its Postgres database, executed through Restate, with its
|
|
13
13
|
* history in s2-lite; the Runtime's /ready checks all three. Postgres initialises the
|
|
14
14
|
* database with C collation (`--locale=C`) and runs with `wal_level=logical`: the stream
|
|
15
15
|
* relay feeds s2-lite from the record over logical replication (Durable Streams), and
|
|
16
16
|
* `max_slot_wal_keep_size` caps the WAL a stuck relay can hold. The Runtime creates the
|
|
17
|
-
* Tenant on its first start, with its name (`NYLORUN_TENANT_NAME`)
|
|
18
|
-
* principals of `NYLORUN_DERIVED_PRINCIPALS` (`project` for the Project link).
|
|
17
|
+
* Tenant on its first start, with its name (`NYLORUN_TENANT_NAME`).
|
|
19
18
|
*
|
|
20
19
|
* Restate signs requests to the Worker endpoint with the private key in
|
|
21
20
|
* `docker/restate-identity.pem`, mounted read-only; the Runtime gets the public
|
|
@@ -26,8 +25,8 @@ import { PINNED_IMAGES } from "./images.js";
|
|
|
26
25
|
* which alone reads model credentials, and the Tool Gate (F4.1), which alone
|
|
27
26
|
* holds remote MCP connections and their credentials and POSTs every Action
|
|
28
27
|
* delivery. Every model call, remote MCP call and delivery of the loop crosses it
|
|
29
|
-
* (NYLORUN_GATES_URL, with NYLORUN_GATES_TOKEN from `.env
|
|
30
|
-
*
|
|
28
|
+
* (NYLORUN_GATES_URL, with NYLORUN_GATES_TOKEN from `.env`, or a run's token from the
|
|
29
|
+
* harness); MCP stdio servers run in the harness container. The gateway also runs keys (F4.2): the only
|
|
31
30
|
* holder of the vault key, it runs every vault write that touches a secret and signs
|
|
32
31
|
* every token (NYLORUN_KEYS_URL). The gateway mounts only the Host's Tenant directory
|
|
33
32
|
* (`tenant/`) and its keys directory (`keys/`, the vault key), read-only: never
|
|
@@ -36,8 +35,48 @@ import { PINNED_IMAGES } from "./images.js";
|
|
|
36
35
|
* Restate's private key and `.env`. The runtime does not wait for the gateway: while
|
|
37
36
|
* it is down, model and MCP calls fail, deliveries are retried, vault writes and
|
|
38
37
|
* token minting answer 503, and the session takes the next message.
|
|
38
|
+
*
|
|
39
|
+
* The Object store (blueprint D35) is RustFS, single node and single drive, pinned by digest,
|
|
40
|
+
* on the `rustfs` volume; it is not published. Its credential is the access key `nylorun` and
|
|
41
|
+
* the secret key NYLORUN_OBJECT_STORE_SECRET_KEY from `.env`, and only the runtime and the
|
|
42
|
+
* gateway receive it (NYLORUN_OBJECT_STORE_*). The runtime creates the bucket at boot and
|
|
43
|
+
* reaches the store through its `s3` BlobStore.
|
|
44
|
+
*
|
|
45
|
+
* With `sandboxes` (after `nylorun sandbox enable`, F7.2) the `sandboxes` service runs the
|
|
46
|
+
* agent-sandbox driver: the only container holding the cluster credentials
|
|
47
|
+
* (`<Host root>/sandboxes`, read-only), not published, reached by the runtime alone with
|
|
48
|
+
* NYLORUN_SANDBOXES_TOKEN. The runtime gets an empty read-only mount over that directory.
|
|
49
|
+
* Sandbox pods reach the stack only on the Docker host's address (NYLORUN_SANDBOX_HOST_ADDRESS),
|
|
50
|
+
* where three ports are published on NYLORUN_SANDBOX_BIND: the runtime's Harness API listener
|
|
51
|
+
* (NYLORUN_SANDBOX_HARNESS_PORT → 4200: pods join and connect with host tokens), the
|
|
52
|
+
* gateway's gates (NYLORUN_SANDBOX_GATES_PORT → 4100: model and MCP calls with run tokens) and
|
|
53
|
+
* egress-gate (NYLORUN_SANDBOX_EGRESS_PORT → 4200 in the gateway, `--service gates,keys,egress`:
|
|
54
|
+
* CONNECT with the egress token, to the hosts the sandbox spec allows). The two HTTP listeners
|
|
55
|
+
* accept that address as a Host.
|
|
56
|
+
*
|
|
57
|
+
* The harness (F6.2, `harness: "remote"`, the default) runs every agent turn, MCP stdio server
|
|
58
|
+
* and workspace in its own container: the runtime image with `--service harness`, connected to
|
|
59
|
+
* core's Harness API (`ws://runtime:4200/nylorun/harness/v1`) with NYLORUN_HARNESS_TOKEN, its
|
|
60
|
+
* only credential, and calling models and remote MCP servers through the gateway with each
|
|
61
|
+
* run's token. It mounts only the Tenant's `sandboxes/`, `plugin-data/`, `home/` and `tmp/`
|
|
62
|
+
* under `/harness`, and the Host root's `plugins/` read-only at its own path (plugin roots name
|
|
63
|
+
* paths on this machine; the runtime mounts it there too). NYLORUN_HARNESS=in-process in `.env`
|
|
64
|
+
* rolls back to turns in the runtime container, without the harness service.
|
|
65
|
+
*
|
|
66
|
+
* Three networks: `store` (internal: no egress) joins Postgres, s2-lite, Restate and RustFS to
|
|
67
|
+
* the runtime and the gateway; `harness` joins the harness to the runtime and the gateway only;
|
|
68
|
+
* `default` carries egress and the published ports (runtime, gateway, Studio, sandboxes).
|
|
69
|
+
* Restate's admin port (its UI, unauthenticated) is published, and Restate joins `default`,
|
|
70
|
+
* only with `restateUi` (`nylorun start --restate-ui`).
|
|
71
|
+
*
|
|
72
|
+
* With `identity` (when `<Host root>/identity.yaml` exists) the runtime reads its trusted issuers
|
|
73
|
+
* from it, through the Host root mount (NYLORUN_IDENTITY_FILE=/nylorun/identity.yaml).
|
|
39
74
|
*/
|
|
40
|
-
export function renderComposeFile(project, name) {
|
|
75
|
+
export function renderComposeFile(project, name, options = {}) {
|
|
76
|
+
const sandboxes = options.sandboxes === true;
|
|
77
|
+
const identity = options.identity === true;
|
|
78
|
+
const remote = (options.harness ?? "remote") === "remote";
|
|
79
|
+
const restateUi = options.restateUi === true;
|
|
41
80
|
return `# Written by \`nylorun start\`; rewritten on every start. Settings live in .env.
|
|
42
81
|
name: ${project}
|
|
43
82
|
|
|
@@ -58,6 +97,7 @@ services:
|
|
|
58
97
|
POSTGRES_INITDB_ARGS: "--locale=C" # C collation for the whole database
|
|
59
98
|
volumes:
|
|
60
99
|
- postgres:/var/lib/postgresql/data
|
|
100
|
+
networks: [store]
|
|
61
101
|
healthcheck:
|
|
62
102
|
test: ["CMD-SHELL", "pg_isready -U nylorun -d nylorun"]
|
|
63
103
|
interval: 2s
|
|
@@ -75,9 +115,7 @@ services:
|
|
|
75
115
|
volumes:
|
|
76
116
|
- restate:/restate-data
|
|
77
117
|
- \${NYLORUN_HOST_ROOT:?run nylorun start}/docker/restate-identity.pem:/run/nylorun/restate-identity.pem:ro
|
|
78
|
-
|
|
79
|
-
- "127.0.0.1:\${NYLORUN_RESTATE_PORT:?run nylorun start}:9070" # Restate UI and admin, for debugging
|
|
80
|
-
healthcheck:
|
|
118
|
+
${restateUi ? RESTATE_UI : RESTATE_CLOSED} healthcheck:
|
|
81
119
|
test: ["CMD", "curl", "-fsS", "http://localhost:9070/health"]
|
|
82
120
|
interval: 2s
|
|
83
121
|
timeout: 5s
|
|
@@ -94,15 +132,36 @@ services:
|
|
|
94
132
|
# (e.g. /data) is root-owned and s2-lite cannot write it.
|
|
95
133
|
volumes:
|
|
96
134
|
- s2-lite:/home/nonroot
|
|
135
|
+
networks: [store]
|
|
97
136
|
# The s2 image has no shell or HTTP client, so it has no health check;
|
|
98
137
|
# the Runtime's /ready covers it.
|
|
99
138
|
restart: unless-stopped
|
|
100
139
|
|
|
101
|
-
|
|
140
|
+
rustfs: # the Object store (D35); not published, only the runtime and the gateway hold its credential
|
|
141
|
+
image: ${PINNED_IMAGES.rustfs}
|
|
142
|
+
container_name: ${project}-rustfs
|
|
143
|
+
labels: *tenant
|
|
144
|
+
environment:
|
|
145
|
+
RUSTFS_ACCESS_KEY: nylorun
|
|
146
|
+
RUSTFS_SECRET_KEY: \${NYLORUN_OBJECT_STORE_SECRET_KEY:?run nylorun start}
|
|
147
|
+
RUSTFS_CONSOLE_ENABLE: "false" # no web console: clients go through the Runtime
|
|
148
|
+
RUSTFS_OBS_LOG_DIRECTORY: "" # log to stdout (nylorun logs rustfs)
|
|
149
|
+
# The image runs as uid 10001 and owns /data, which Docker copies into the new volume.
|
|
150
|
+
volumes:
|
|
151
|
+
- rustfs:/data
|
|
152
|
+
networks: [store]
|
|
153
|
+
healthcheck:
|
|
154
|
+
test: ["CMD", "curl", "-fsS", "http://localhost:9000/health"]
|
|
155
|
+
interval: 2s
|
|
156
|
+
timeout: 5s
|
|
157
|
+
retries: 30
|
|
158
|
+
restart: unless-stopped
|
|
159
|
+
|
|
160
|
+
gateway: # the Model and Tool Gates and keys: the vault key, credentials, signing, outbound calls; not published${sandboxes ? ", but for sandbox pods" : ""}
|
|
102
161
|
image: \${NYLORUN_RUNTIME_IMAGE:?run nylorun start}
|
|
103
162
|
container_name: ${project}-gateway
|
|
104
163
|
labels: *tenant
|
|
105
|
-
command: ["--service", "gates,keys"]
|
|
164
|
+
command: ["--service", "gates,keys${sandboxes ? ",egress" : ""}"]
|
|
106
165
|
user: "\${NYLORUN_UID:?run nylorun start}:\${NYLORUN_GID:?run nylorun start}"
|
|
107
166
|
depends_on:
|
|
108
167
|
postgres: { condition: service_healthy }
|
|
@@ -110,17 +169,27 @@ services:
|
|
|
110
169
|
NYLORUN_HOME: /nylorun
|
|
111
170
|
NYLORUN_PACKING: combined
|
|
112
171
|
NYLORUN_GATES_LISTEN_PORT: "4100"
|
|
113
|
-
NYLORUN_GATES_ALLOWED_HOSTS: gateway:4100
|
|
172
|
+
NYLORUN_GATES_ALLOWED_HOSTS: gateway:4100${sandboxes ? SANDBOXES_GATES_HOST : ""}
|
|
114
173
|
NYLORUN_GATES_TOKEN: \${NYLORUN_GATES_TOKEN:?run nylorun start}
|
|
115
174
|
NYLORUN_DATABASE_URL: postgres://nylorun:\${NYLORUN_POSTGRES_PASSWORD}@postgres:5432/nylorun
|
|
175
|
+
# The Object store, where model-gate will read file parts (F8.1).
|
|
176
|
+
NYLORUN_OBJECT_STORE_ENDPOINT: http://rustfs:9000
|
|
177
|
+
NYLORUN_OBJECT_STORE_ACCESS_KEY: nylorun
|
|
178
|
+
NYLORUN_OBJECT_STORE_SECRET_KEY: \${NYLORUN_OBJECT_STORE_SECRET_KEY:?run nylorun start}
|
|
116
179
|
# Action endpoints on this machine: \`localhost\` in a registered URL means the Docker host.
|
|
117
180
|
NYLORUN_ENDPOINT_LOOPBACK: docker-host
|
|
181
|
+
# Your credential resolver, for people's own MCP credentials (DEPLOYMENT.md, Credentials);
|
|
182
|
+
# unset by default. Set both in the shell that runs \`nylorun start\`.
|
|
183
|
+
NYLORUN_RESOLVER_URL: \${NYLORUN_RESOLVER_URL:-}
|
|
184
|
+
NYLORUN_RESOLVER_TOKEN: \${NYLORUN_RESOLVER_TOKEN:-}${sandboxes ? SANDBOXES_GATEWAY_ENV : ""}
|
|
118
185
|
extra_hosts:
|
|
119
186
|
host.docker.internal: host-gateway # model servers, MCP servers and Action endpoints on this machine
|
|
120
187
|
volumes:
|
|
121
188
|
# The Tenant's homes and its vault key only, read-only.
|
|
122
189
|
- \${NYLORUN_HOST_ROOT:?run nylorun start}/tenant:/nylorun/tenant:ro
|
|
123
190
|
- \${NYLORUN_HOST_ROOT:?run nylorun start}/keys:/nylorun/keys:ro
|
|
191
|
+
${sandboxes ? SANDBOXES_GATES_PORT : ""} # Egress and the stores; the harness reaches its gates on \`harness\`.
|
|
192
|
+
networks: [default, store, harness]
|
|
124
193
|
healthcheck:
|
|
125
194
|
test: ["CMD", "node", "-e", "fetch('http://localhost:4100/ready').then(r=>process.exit(r.ok?0:1),()=>process.exit(1))"]
|
|
126
195
|
interval: 2s
|
|
@@ -138,12 +207,12 @@ services:
|
|
|
138
207
|
postgres: { condition: service_healthy }
|
|
139
208
|
restate: { condition: service_healthy }
|
|
140
209
|
s2-lite: { condition: service_started }
|
|
210
|
+
rustfs: { condition: service_healthy }
|
|
141
211
|
environment:
|
|
142
212
|
NYLORUN_HOME: /nylorun
|
|
143
213
|
NYLORUN_PACKING: combined
|
|
144
214
|
# The Tenant the Runtime creates on its first start (later starts open it).
|
|
145
215
|
NYLORUN_TENANT_NAME: \${NYLORUN_TENANT_NAME:?run nylorun start}
|
|
146
|
-
NYLORUN_DERIVED_PRINCIPALS: \${NYLORUN_DERIVED_PRINCIPALS:-project}
|
|
147
216
|
# Model calls, remote MCP calls and deliveries go through the gateway, and vault writes
|
|
148
217
|
# and token signing through its keys service: this container never reads a credential
|
|
149
218
|
# or the vault key.
|
|
@@ -168,8 +237,18 @@ services:
|
|
|
168
237
|
NYLORUN_S2_ENDPOINT: http://s2-lite:80
|
|
169
238
|
NYLORUN_S2_TOKEN: ignored # s2-lite has no access tokens yet
|
|
170
239
|
NYLORUN_WORKSPACE_STORE_URL: file:///workspaces
|
|
240
|
+
# The Object store, through the BlobStore seam's s3 adapter; the runtime creates the bucket.
|
|
241
|
+
NYLORUN_OBJECT_STORE_ENDPOINT: http://rustfs:9000
|
|
242
|
+
NYLORUN_OBJECT_STORE_ACCESS_KEY: nylorun
|
|
243
|
+
NYLORUN_OBJECT_STORE_SECRET_KEY: \${NYLORUN_OBJECT_STORE_SECRET_KEY:?run nylorun start}
|
|
171
244
|
# Action endpoints on this machine: \`localhost\` in a registered URL means the Docker host.
|
|
172
245
|
NYLORUN_ENDPOINT_LOOPBACK: docker-host
|
|
246
|
+
# Agent turns, MCP servers and workspaces run in the harness container, which connects to
|
|
247
|
+
# this listener with NYLORUN_HARNESS_TOKEN. NYLORUN_HARNESS=in-process in .env rolls back.
|
|
248
|
+
NYLORUN_HARNESS: \${NYLORUN_HARNESS:-remote}
|
|
249
|
+
NYLORUN_HARNESS_LISTEN_PORT: "4200"
|
|
250
|
+
NYLORUN_HARNESS_ALLOWED_HOSTS: runtime:4200${sandboxes ? SANDBOXES_HARNESS_HOST : ""}
|
|
251
|
+
NYLORUN_HARNESS_TOKEN: \${NYLORUN_HARNESS_TOKEN:?run nylorun start}${sandboxes ? SANDBOXES_RUNTIME_ENV : ""}${identity ? IDENTITY_RUNTIME_ENV : ""}
|
|
173
252
|
extra_hosts:
|
|
174
253
|
host.docker.internal: host-gateway # the Docker host, also on Linux Docker Engine
|
|
175
254
|
volumes:
|
|
@@ -183,11 +262,14 @@ services:
|
|
|
183
262
|
- type: tmpfs
|
|
184
263
|
target: /nylorun/docker
|
|
185
264
|
read_only: true
|
|
186
|
-
tmpfs: { size: 4096, mode: 0755 } # empty, and listable by the runtime user
|
|
265
|
+
tmpfs: { size: 4096, mode: 0755 } # empty, and listable by the runtime user${sandboxes ? SANDBOXES_RUNTIME_MOUNT : ""}
|
|
187
266
|
- workspaces:/workspaces
|
|
267
|
+
# Plugin roots on this machine, at their own path (as for the harness), for NYLORUN_HARNESS=in-process.
|
|
268
|
+
- \${NYLORUN_HOST_ROOT:?run nylorun start}/plugins:\${NYLORUN_HOST_ROOT:?run nylorun start}/plugins:ro
|
|
269
|
+
networks: [default, store, harness]
|
|
188
270
|
ports:
|
|
189
271
|
- "127.0.0.1:\${NYLORUN_PORT:?run nylorun start}:4000" # Tenant API, SSE, browsers
|
|
190
|
-
- "127.0.0.1:\${NYLORUN_ADMIN_PORT:?run nylorun start}:4001" # Admin API (operators only)
|
|
272
|
+
- "127.0.0.1:\${NYLORUN_ADMIN_PORT:?run nylorun start}:4001" # Admin API (operators only)${sandboxes ? SANDBOXES_RUNTIME_PORT : ""}
|
|
191
273
|
healthcheck: # liveness: a Tenant that cannot open is reported by nylorun start from the Admin status, not by a 300 s wait
|
|
192
274
|
test: ["CMD", "node", "-e", "fetch('http://localhost:4000/health').then(r=>process.exit(r.ok?0:1),()=>process.exit(1))"]
|
|
193
275
|
interval: 2s
|
|
@@ -211,12 +293,16 @@ services:
|
|
|
211
293
|
# Browsers share cookies across ports of one host: a cookie per Tenant keeps the
|
|
212
294
|
# sessions of two Studios on localhost apart.
|
|
213
295
|
NYLORUN_STUDIO_SESSION_COOKIE: nylorun_studio_${name}
|
|
214
|
-
# Exact origins that may frame Studio (
|
|
296
|
+
# Exact origins that may frame Studio (none by default: embedding is opt-in); kept in .env.
|
|
215
297
|
NYLORUN_STUDIO_FRAME_ANCESTORS: \${NYLORUN_STUDIO_FRAME_ANCESTORS:-}
|
|
216
298
|
# Studio's anonymous usage analytics; empty when telemetry is off.
|
|
217
299
|
NYLORUN_STUDIO_ANALYTICS_ID: \${NYLORUN_STUDIO_ANALYTICS_ID:-}
|
|
300
|
+
# Extra Host values Studio serves behind a sign-in proxy (studio.acme.dev),
|
|
301
|
+
# comma-separated; from the environment of nylorun start.
|
|
302
|
+
NYLORUN_STUDIO_ALLOWED_HOSTS: \${NYLORUN_STUDIO_ALLOWED_HOSTS:-}
|
|
218
303
|
volumes:
|
|
219
304
|
- \${NYLORUN_HOST_ROOT}/host-credentials.json:/run/nylorun/host-credentials.json:ro
|
|
305
|
+
networks: [default]
|
|
220
306
|
ports:
|
|
221
307
|
- "127.0.0.1:\${NYLORUN_STUDIO_PORT:?run nylorun start}:3000" # opened as http://localhost:<port>
|
|
222
308
|
healthcheck: # /healthz needs no session; Host studio:3000 is accepted
|
|
@@ -225,16 +311,135 @@ services:
|
|
|
225
311
|
timeout: 5s
|
|
226
312
|
retries: 30
|
|
227
313
|
restart: unless-stopped
|
|
228
|
-
|
|
314
|
+
${remote ? harnessService(project) : ""}${sandboxes ? sandboxesService(project) : ""}
|
|
229
315
|
networks:
|
|
230
316
|
default:
|
|
231
317
|
name: ${project}
|
|
232
318
|
labels: *tenant
|
|
319
|
+
store: # the stores: no egress, no published port
|
|
320
|
+
name: ${project}-store
|
|
321
|
+
internal: true
|
|
322
|
+
labels: *tenant
|
|
323
|
+
harness: # the harness reaches the runtime's Harness API and the gateway only (egress for MCP servers and bash)
|
|
324
|
+
name: ${project}-harness
|
|
325
|
+
labels: *tenant
|
|
233
326
|
|
|
234
327
|
volumes:
|
|
235
328
|
postgres: { name: ${project}-postgres, labels: *tenant }
|
|
236
329
|
restate: { name: ${project}-restate, labels: *tenant }
|
|
237
330
|
s2-lite: { name: ${project}-s2-lite, labels: *tenant }
|
|
331
|
+
rustfs: { name: ${project}-rustfs, labels: *tenant }
|
|
238
332
|
workspaces: { name: ${project}-workspaces, labels: *tenant }
|
|
239
333
|
`;
|
|
240
334
|
}
|
|
335
|
+
/** Restate on the stores' network only: its admin port (the UI) is not published. */
|
|
336
|
+
const RESTATE_CLOSED = ` networks: [store] # admin and UI not published: nylorun start --restate-ui
|
|
337
|
+
`;
|
|
338
|
+
/** Restate's admin port and UI on loopback (`--restate-ui`): it must join `default` to be published. */
|
|
339
|
+
const RESTATE_UI = ` networks: [store, default]
|
|
340
|
+
ports:
|
|
341
|
+
- "127.0.0.1:\${NYLORUN_RESTATE_PORT:?run nylorun start}:9070" # Restate UI and admin (unauthenticated): nylorun start --restate-ui
|
|
342
|
+
`;
|
|
343
|
+
/**
|
|
344
|
+
* The harness service (F6.2): agent turns, MCP stdio servers and workspaces, apart from core.
|
|
345
|
+
* It holds the harness token only; models and remote MCP servers are reached through the
|
|
346
|
+
* gateway with each run's token. Healthy once connected to core's Harness API.
|
|
347
|
+
*/
|
|
348
|
+
function harnessService(project) {
|
|
349
|
+
return `
|
|
350
|
+
harness: # agent turns, MCP servers and workspaces; holds only the harness token; not published
|
|
351
|
+
image: \${NYLORUN_RUNTIME_IMAGE:?run nylorun start}
|
|
352
|
+
container_name: ${project}-harness
|
|
353
|
+
labels: *tenant
|
|
354
|
+
command: ["--service", "harness"]
|
|
355
|
+
user: "\${NYLORUN_UID:?run nylorun start}:\${NYLORUN_GID:?run nylorun start}"
|
|
356
|
+
depends_on:
|
|
357
|
+
runtime: { condition: service_healthy }
|
|
358
|
+
gateway: { condition: service_healthy }
|
|
359
|
+
environment:
|
|
360
|
+
NYLORUN_HARNESS_URL: ws://runtime:4200/nylorun/harness/v1
|
|
361
|
+
NYLORUN_HARNESS_TOKEN: \${NYLORUN_HARNESS_TOKEN:?run nylorun start}
|
|
362
|
+
NYLORUN_GATES_URL: http://gateway:4100
|
|
363
|
+
NYLORUN_HARNESS_ROOT: /harness
|
|
364
|
+
volumes:
|
|
365
|
+
# The Tenant's workspaces, plugin data and the homes of MCP stdio servers; nothing else.
|
|
366
|
+
- \${NYLORUN_HOST_ROOT:?run nylorun start}/tenant/sandboxes:/harness/sandboxes
|
|
367
|
+
- \${NYLORUN_HOST_ROOT:?run nylorun start}/tenant/plugin-data:/harness/plugin-data
|
|
368
|
+
- \${NYLORUN_HOST_ROOT:?run nylorun start}/tenant/home:/harness/home
|
|
369
|
+
- \${NYLORUN_HOST_ROOT:?run nylorun start}/tenant/tmp:/harness/tmp
|
|
370
|
+
# Plugin roots on this machine, read-only, at their own path: agents name them so.
|
|
371
|
+
- \${NYLORUN_HOST_ROOT:?run nylorun start}/plugins:\${NYLORUN_HOST_ROOT:?run nylorun start}/plugins:ro
|
|
372
|
+
networks: [harness]
|
|
373
|
+
healthcheck: # healthy once connected to core's Harness API
|
|
374
|
+
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:4300/health').then(r=>r.json()).then(b=>process.exit(b.connected?0:1),()=>process.exit(1))"]
|
|
375
|
+
interval: 2s
|
|
376
|
+
timeout: 5s
|
|
377
|
+
retries: 90
|
|
378
|
+
restart: unless-stopped
|
|
379
|
+
`;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* The runtime reaches the sandboxes service on the Compose network with its token, and serves
|
|
383
|
+
* the Harness API to sandbox pods (F7.2) on the Docker host's address.
|
|
384
|
+
*/
|
|
385
|
+
/** The identity file (`<Host root>/identity.yaml`): the trusted issuers, read at boot. */
|
|
386
|
+
const IDENTITY_RUNTIME_ENV = `
|
|
387
|
+
# Trusted issuers (<Host root>/identity.yaml): JWTs from the operator's identity provider.
|
|
388
|
+
NYLORUN_IDENTITY_FILE: /nylorun/identity.yaml`;
|
|
389
|
+
const SANDBOXES_RUNTIME_ENV = `
|
|
390
|
+
# The sandboxes service (nylorun sandbox enable): pods on the Tenant's cluster.
|
|
391
|
+
NYLORUN_SANDBOXES_URL: http://sandboxes:4300
|
|
392
|
+
NYLORUN_SANDBOXES_TOKEN: \${NYLORUN_SANDBOXES_TOKEN:?run nylorun sandbox enable}
|
|
393
|
+
NYLORUN_SANDBOX_HARNESS_IMAGE: \${NYLORUN_RUNTIME_IMAGE:?run nylorun start} # pods copy the engine from it`;
|
|
394
|
+
/** Pods join and connect to the Harness API at the Docker host's address. */
|
|
395
|
+
const SANDBOXES_HARNESS_HOST = ",\${NYLORUN_SANDBOX_HOST_ADDRESS:?run nylorun sandbox enable}:\${NYLORUN_SANDBOX_HARNESS_PORT:?run nylorun sandbox enable}";
|
|
396
|
+
/** The Harness API, published for sandbox pods. */
|
|
397
|
+
const SANDBOXES_RUNTIME_PORT = `
|
|
398
|
+
- "\${NYLORUN_SANDBOX_BIND:?run nylorun sandbox enable}:\${NYLORUN_SANDBOX_HARNESS_PORT:?run nylorun sandbox enable}:4200" # Harness API, for sandbox pods`;
|
|
399
|
+
/** The gates accept the pods' Host; their calls carry run tokens. */
|
|
400
|
+
const SANDBOXES_GATES_HOST = ",\${NYLORUN_SANDBOX_HOST_ADDRESS:?run nylorun sandbox enable}:\${NYLORUN_SANDBOX_GATES_PORT:?run nylorun sandbox enable}";
|
|
401
|
+
/** The gates and egress-gate, published for sandbox pods. */
|
|
402
|
+
const SANDBOXES_GATES_PORT = ` ports:
|
|
403
|
+
- "\${NYLORUN_SANDBOX_BIND:?run nylorun sandbox enable}:\${NYLORUN_SANDBOX_GATES_PORT:?run nylorun sandbox enable}:4100" # gates, for sandbox pods (run tokens)
|
|
404
|
+
- "\${NYLORUN_SANDBOX_BIND:?run nylorun sandbox enable}:\${NYLORUN_SANDBOX_EGRESS_PORT:?run nylorun sandbox enable}:4200" # egress-gate, for sandbox pods (egress tokens)
|
|
405
|
+
`;
|
|
406
|
+
/** egress-gate (F7.2): pods' CONNECT proxy in the gateway. */
|
|
407
|
+
const SANDBOXES_GATEWAY_ENV = `
|
|
408
|
+
# egress-gate: the only way out of a sandbox pod, to the hosts its spec allows.
|
|
409
|
+
NYLORUN_EGRESS_LISTEN_PORT: "4200"`;
|
|
410
|
+
/** Empty and read-only over the cluster credentials: only the sandboxes service reads them. */
|
|
411
|
+
const SANDBOXES_RUNTIME_MOUNT = `
|
|
412
|
+
- type: tmpfs
|
|
413
|
+
target: /nylorun/sandboxes
|
|
414
|
+
read_only: true
|
|
415
|
+
tmpfs: { size: 4096, mode: 0755 } # empty, and listable by the runtime user`;
|
|
416
|
+
/**
|
|
417
|
+
* The sandboxes service. The pod-facing ports `nylorun sandbox enable` records
|
|
418
|
+
* (NYLORUN_SANDBOX_*_PORT in .env), the Harness API's, the gates' and egress-gate's, are
|
|
419
|
+
* published by the runtime and the gateway (above).
|
|
420
|
+
*/
|
|
421
|
+
function sandboxesService(project) {
|
|
422
|
+
return `
|
|
423
|
+
sandboxes: # agent-sandbox driver; the only holder of the cluster credentials; not published
|
|
424
|
+
image: \${NYLORUN_SANDBOXES_IMAGE:?run nylorun sandbox enable}
|
|
425
|
+
container_name: ${project}-sandboxes
|
|
426
|
+
labels: *tenant
|
|
427
|
+
user: "\${NYLORUN_UID:?run nylorun start}:\${NYLORUN_GID:?run nylorun start}" # reads the 0600 token
|
|
428
|
+
environment:
|
|
429
|
+
NYLORUN_SANDBOXES_TOKEN: \${NYLORUN_SANDBOXES_TOKEN:?run nylorun sandbox enable}
|
|
430
|
+
NYLORUN_SANDBOXES_DIR: /run/nylorun/sandboxes
|
|
431
|
+
NYLORUN_SANDBOXES_LISTEN: ":4300"
|
|
432
|
+
extra_hosts:
|
|
433
|
+
host.docker.internal: host-gateway # the API server when the kubeconfig names this machine's loopback
|
|
434
|
+
volumes:
|
|
435
|
+
# cluster.json (API server, CA, namespace) and the ServiceAccount token, read-only.
|
|
436
|
+
- \${NYLORUN_HOST_ROOT:?run nylorun start}/sandboxes:/run/nylorun/sandboxes:ro
|
|
437
|
+
networks: [default] # the cluster's API server, through host.docker.internal
|
|
438
|
+
healthcheck: # /ready: informers synced and the API server answers
|
|
439
|
+
test: ["CMD", "/sandboxes", "healthcheck"]
|
|
440
|
+
interval: 5s
|
|
441
|
+
timeout: 5s
|
|
442
|
+
retries: 24
|
|
443
|
+
restart: unless-stopped
|
|
444
|
+
`;
|
|
445
|
+
}
|
package/dist/stack/env-file.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Origins that may embed Studio by default:
|
|
3
|
-
*
|
|
2
|
+
* Origins that may embed Studio by default: none. Embedding is opt-in: the operator lists
|
|
3
|
+
* the exact origins of the app that frames Studio (`--studio-embed-origin`, Studio §8.9).
|
|
4
4
|
*/
|
|
5
|
-
export declare const DEFAULT_STUDIO_FRAME_ANCESTORS: readonly [
|
|
5
|
+
export declare const DEFAULT_STUDIO_FRAME_ANCESTORS: readonly string[];
|
|
6
6
|
/**
|
|
7
7
|
* Settings in `docker/.env` (mode 0600). Ports, the password and the gates token
|
|
8
8
|
* persist; the Restate identity public key is derived from `restate-identity.pem`.
|
|
@@ -14,11 +14,32 @@ export interface StackEnv {
|
|
|
14
14
|
adminPort: number;
|
|
15
15
|
/** Published Studio port (loopback). */
|
|
16
16
|
studioPort: number;
|
|
17
|
-
/**
|
|
17
|
+
/** Restate UI and admin port (loopback), published only with `restateUi`. */
|
|
18
18
|
restatePort: number;
|
|
19
|
+
/**
|
|
20
|
+
* `NYLORUN_RESTATE_UI`: `1` publishes Restate's UI and admin port (unauthenticated) for this
|
|
21
|
+
* start (`nylorun start --restate-ui`); `0` keeps it closed. Decided again on every start.
|
|
22
|
+
*/
|
|
23
|
+
restateUi: boolean;
|
|
19
24
|
postgresPassword: string;
|
|
20
25
|
/** The token the runtime presents to the gateway (`NYLORUN_GATES_TOKEN`), 32 bytes as hex. */
|
|
21
26
|
gatesToken: string;
|
|
27
|
+
/**
|
|
28
|
+
* The harness's credential for core's Harness API (`NYLORUN_HARNESS_TOKEN`), 32 bytes as hex:
|
|
29
|
+
* given only to the runtime (which checks it) and the harness.
|
|
30
|
+
*/
|
|
31
|
+
harnessToken: string;
|
|
32
|
+
/**
|
|
33
|
+
* `NYLORUN_HARNESS`: `remote` (the harness container runs agent turns, MCP servers and
|
|
34
|
+
* workspaces) or `in-process` (the runtime does, without a harness container). Kept across
|
|
35
|
+
* starts; edit `.env` to roll back.
|
|
36
|
+
*/
|
|
37
|
+
harness: HarnessMode;
|
|
38
|
+
/**
|
|
39
|
+
* RustFS's secret key (`NYLORUN_OBJECT_STORE_SECRET_KEY`), 32 bytes as hex: the Object store's
|
|
40
|
+
* credential, given only to the runtime and the gateway.
|
|
41
|
+
*/
|
|
42
|
+
objectStoreSecretKey: string;
|
|
22
43
|
/** `publickeyv1_...` of `docker/restate-identity.pem`. */
|
|
23
44
|
restateIdentityKey: string;
|
|
24
45
|
uid: number;
|
|
@@ -36,18 +57,28 @@ export interface StackEnv {
|
|
|
36
57
|
studioAnalyticsId: string;
|
|
37
58
|
/** The Tenant's name, which the Runtime gives the Tenant it creates. */
|
|
38
59
|
tenantName: string;
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
* always includes `project`, the Project link's principal.
|
|
42
|
-
*/
|
|
43
|
-
derivedPrincipals: string;
|
|
60
|
+
/** Present once `nylorun sandbox enable` ran for the Tenant (F7.2). */
|
|
61
|
+
sandboxes?: SandboxStackEnv;
|
|
44
62
|
}
|
|
45
|
-
export declare function renderEnvFile(env: StackEnv): string;
|
|
46
63
|
/**
|
|
47
|
-
* `
|
|
48
|
-
*
|
|
64
|
+
* The sandboxes service's settings (`nylorun sandbox enable`): its bearer token (only the
|
|
65
|
+
* runtime presents it), its image, and the host ports pods reach (Harness API, gates,
|
|
66
|
+
* egress) on the host address, published on the bind address. The ports persist.
|
|
49
67
|
*/
|
|
50
|
-
export
|
|
68
|
+
export interface SandboxStackEnv {
|
|
69
|
+
/** NYLORUN_SANDBOXES_TOKEN, 32 bytes as hex. */
|
|
70
|
+
token: string;
|
|
71
|
+
image: string;
|
|
72
|
+
harnessPort: number;
|
|
73
|
+
gatesPort: number;
|
|
74
|
+
egressPort: number;
|
|
75
|
+
/** The Docker host as pods reach it (192.168.65.254 on Docker Desktop). */
|
|
76
|
+
hostAddress: string;
|
|
77
|
+
/** Where the pod-facing ports are published (127.0.0.1 on Docker Desktop). */
|
|
78
|
+
bind: string;
|
|
79
|
+
}
|
|
80
|
+
export type HarnessMode = "remote" | "in-process";
|
|
81
|
+
export declare function renderEnvFile(env: StackEnv): string;
|
|
51
82
|
/** Parse KEY=value lines; comments and blank lines are skipped. */
|
|
52
83
|
export declare function parseEnvLines(text: string): Map<string, string>;
|
|
53
84
|
/** The settings that persist across starts, from an existing .env. */
|
|
@@ -56,10 +87,15 @@ export interface PersistedStackEnv {
|
|
|
56
87
|
adminPort?: number;
|
|
57
88
|
studioPort?: number;
|
|
58
89
|
restatePort?: number;
|
|
90
|
+
/** Whether the last start published Restate's UI (`NYLORUN_RESTATE_UI=1`). */
|
|
91
|
+
restateUi?: boolean;
|
|
59
92
|
postgresPassword?: string;
|
|
60
93
|
gatesToken?: string;
|
|
94
|
+
harnessToken?: string;
|
|
95
|
+
harness?: HarnessMode;
|
|
96
|
+
objectStoreSecretKey?: string;
|
|
61
97
|
/** Validated origins; absent when the line is missing (an older .env). */
|
|
62
98
|
studioFrameAncestors?: string[];
|
|
63
|
-
|
|
99
|
+
sandboxes?: SandboxStackEnv;
|
|
64
100
|
}
|
|
65
101
|
export declare function parsePersisted(text: string): PersistedStackEnv;
|