nylorun 0.9.0-beta → 0.11.1-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 CHANGED
@@ -1,5 +1,92 @@
1
1
  # nylorun
2
2
 
3
+ ## 0.11.1-beta
4
+
5
+ ### Patch Changes
6
+
7
+ - Pin runtime to the tested release.
8
+ - Pin studio to the tested release.
9
+
10
+ ## 0.11.0-beta
11
+
12
+ ### Minor Changes
13
+
14
+ - 713e676: **Remote MCP servers only (blueprint D47).** Nylorun accepts `streamable-http` and `sse` MCP servers, declared by URL and reached through the gates; stdio servers and plugin roots are gone. See MIGRATION.md.
15
+
16
+ - **Breaking (`@nylorun/core`):** `McpServerManifest` and the manifest schema keep only `streamable-http` and `sse`. A `stdio` server is refused by `.mcp({...})` (`McpError`, code `mcp.stdio`), by `Agent.from` and by the wire schema, all with one message (`stdioMcpRefusal`): "MCP server 'x' uses stdio; Nylorun accepts remote MCP servers only (streamable-http or sse). Run the server behind an HTTP transport and declare its URL." `PutAgentRequest` loses `pluginRoots`, `CapabilityDeclaration` loses `pluginRoot`, and the Harness API's `RunRouting` loses `pluginRoots`.
17
+ - **Breaking (`@nylorun/agents`):** `.plugin()` and `plugin()` throw `PluginError` (code `plugin.mcp-stdio`) for a stdio server in a plugin's `mcp.json`; its remote servers and skills load as before. `saveAgent` no longer sends plugin roots. `prepareStdioLaunch`, `expandPluginPlaceholders` and `StdioLaunch` are removed.
18
+ - **Breaking (`@nylorun/runtime`):** no stdio MCP launcher: `PUT /v1/agents/:id` refuses a stdio server (`400`) and `pluginRoots`. `TenantConfig.childEnv`, `TenantPaths.pluginData` and `tmp`, `tenantChildEnvironment`, `startEphemeralRuntime({ baseline })` and `configForFactory`'s `baseline` and `hostConfig` are removed; the harness service no longer takes `childEnv` or `paths.pluginData`.
19
+ - `nylorun`: the local stack no longer mounts the Host root's `plugins/` into the runtime and harness containers, nor the Tenant's `plugin-data/`, `home/` and `tmp/` into the harness, which now mounts only `sandboxes/`.
20
+
21
+ - c135267: **Action endpoints are removed (manifest-only agents, M6).** The Runtime runs no code of yours during a session: an agent's tools are HTTP tools, remote MCP servers, agents used as tools and the Runtime's built-ins. Protocol stays 8; the `action-endpoints` feature is gone, so a client that requires it is refused. See MIGRATION.md, "Action endpoints are removed".
22
+
23
+ - **Breaking (`@nylorun/core`):** the `Action`, endpoint (`PutEndpointsRequest`, `Endpoint`, `EndpointHealth`, …) and delivery schemas, `SIGNATURE_HEADER`, `OUTCOME_HEADER`, `DELIVERY_TOKEN_TYPE` and the `action.*` events (`action.pending`, `.delivered`, `.delivery_failed`, `.completed`, `.uncertain`) are removed, and `ToolDefinition.background` with them. `ActionOutcome` is renamed `EffectOutcome`. Tenant status loses `checks.endpoints`, `agents[].registered` and `agents[].endpoint`, and `counts.pendingActions`; the session view loses `actions`. A `turn.paused` interaction carries the tool call's `callId`. New `codeToolsOf` and `codeToolRefusal` name a definition's tools that would run your code. The Harness API is v2 (`HARNESS_API_VERSION = 2`): `TurnStart.options.holdMs` and `effect.resolved` are removed.
24
+ - **Breaking (`@nylorun/harness`):** held runs are gone: `createHarness` loses `holdMs`, and `apiHost` its `hold` option.
25
+ - **Breaking (`@nylorun/agents`):** `createActionHandler`, `executeAction`, `createActionSandbox`, `definitionDeclaresSandbox`, `isActionSandboxTool` and the `Action`, `ActionOutcome`, `ActionHandler`, `ActionHandlerOptions`, `RegisterOptions`, `ExecuteActionOptions` and `ExecutableDefinition` types are removed. `saveAgent` refuses a code tool (`tool({ run })`) or a flow tool stage before sending; its `implementationVersion` is optional (`NYLORUN_IMPLEMENTATION_VERSION`, else `dev`).
26
+ - **Breaking (`@nylorun/runtime`):** `/v1/endpoints` and `/v1/actions/*` answer `404`; delivery tokens, the deliverer, background tools, held runs (`TenantConfig.actionHoldMs`), `DurableExecution.deliver`, the Restate `NylorunAction` object, the `action_result` wake and the gates service's `/nylorun/v1/deliveries` are removed, and a migration drops the `actions` and `endpoints` tables. `PUT /v1/agents/:id` refuses a definition with a code tool or a flow tool stage (`400`); a tool the Runtime cannot run fails with `tool.unavailable`. The fixture model answers in text when the agent offers no `lookup_order` tool.
27
+ - **Breaking (`@nylorun/cli`):** `nylo endpoints` is removed (a usage error that says why); `nylo status` shows uncertain effects instead of pending Actions.
28
+ - **Breaking (`@nylorun/create-agent`):** the starter saves its agent with `saveAgent` and runs no server: no Action endpoint, `PORT` or `NYLORUN_ACTIONS_URL`. Its assistant has no tools, with a commented `http()` tool to start from.
29
+ - `nylorun`: the local stack's comments speak of MCP servers and HTTP tools on this machine, not Action endpoints.
30
+ - `@nylorun/studio`: the `action.*` event views and delivery status are removed; the chat shows `tool.completed`, and the Agent Manifest tab lists tools without a target as code tools.
31
+
32
+ ### Patch Changes
33
+
34
+ - Pin core to the tested release.
35
+ - Pin runtime to the tested release.
36
+ - Pin studio to the tested release.
37
+ - Updated dependencies [4bd2a0b]
38
+ - Updated dependencies [a64aaca]
39
+ - Updated dependencies [40b7648]
40
+ - Updated dependencies [713e676]
41
+ - Updated dependencies [c135267]
42
+ - Updated dependencies [d36f0d9]
43
+ - Updated dependencies [107b07d]
44
+ - @nylorun/core@0.15.0-beta
45
+
46
+ ## 0.10.0-beta
47
+
48
+ ### Major Changes
49
+
50
+ - 7f763c3: **The Admin API and the operator listener are gone (Runtime and Management APIs, step A5).** Host work moves to the machine: `nylorun` runs `nylorun-operate` inside the runtime container, and every remote client uses the Runtime API or the Management API.
51
+
52
+ - **Breaking (`@nylorun/runtime`):** `/v1/admin/*` (status, host, shutdown, keys, openapi.json) is removed; it answers like any unknown route. The operator listener, `NYLORUN_ADMIN_LISTEN_PORT`, `NYLORUN_ADMIN_LISTEN_HOST`, `NYLORUN_ADMIN_ALLOWED_HOSTS` and `host.json`'s `adminPort` are gone, and so is the `admin-openapi.json` package file. Stop the Host with SIGTERM. `nylorun-operate status [--json]` reports the version, protocol and the Tenant's id, name, state and cause, exiting 2 when the Tenant is not open. `/ready` adds `harness: { mode, connected }` while the Tenant is open. `startEphemeralRuntime` loses `operatorListener` and `adminUrl`. The admin key stays: it derives Studio's key.
53
+ - **Breaking (`@nylorun/admin`):** `createAdmin()` is the Management API client (`tenant`, `keys`, `models`, `vaults`, `signingKeys`, `settings`) with a management key: explicit `{ url, key }`, else `NYLORUN_RUNTIME_URL` + `NYLORUN_MANAGEMENT_KEY`, else the Project link's or the local Host root's management key. `status()`, `adminUrl`, the Admin API keys, `NYLORUN_ADMIN_URL`/`NYLORUN_ADMIN_KEY` and `OPERATOR_KEYS_FEATURE` are removed; `deriveStudioToken` and `mintStudioLoginToken` stay.
54
+ - **Breaking (`@nylorun/core`):** `admin-status` leaves `PROTOCOL_FEATURES` (the Host still advertises it for protocol 5–7 clients) and `operator-keys` is removed; `AdminStatusSchema`, `AdminHostStatusSchema`, `HostAggregateSchema` and `HostShutdownResponseSchema` are removed.
55
+ - **Breaking (`nylorun`):** no admin port: `NYLORUN_ADMIN_PORT` is no longer written or published (an existing one is ignored). `nylorun start` waits for `/ready`, and `nylorun status` reads readiness from `/ready` and the Tenant from `nylorun-operate status`.
56
+ - `@nylorun/cli`: `nylo status` no longer falls back to the Admin API; when the Tenant does not answer it points to `npx nylorun status`.
57
+
58
+ ### Minor Changes
59
+
60
+ - 3b3bdc6: **The clients use management keys (Runtime and Management APIs, step A3).** The protocol stays at 7; every client keeps working against a protocol 7 Runtime's routes.
61
+
62
+ - `nylorun`: `nylorun start` keeps an application key (`project`) and a management key (`project-management`) for a Project, in `<Host root>/project-credentials.json` and the Project's `.nylorun/credentials.json` (still format 1, with new `managementKey` and `managementPrincipalId` fields). A credentials file holding only an application key gains a management key at the next start. Commands outside a project keep `cli` and `cli-management`. Keys are issued through `nylorun-operate` in the runtime container instead of the Admin API, and `nylorun key put <id> --management` puts a management key. Seeding the Tenant and `nylorun mcp connect` use the management key (`/v1/tenant/vaults`). Studio reaches the Runtime's public listener.
63
+ - `@nylorun/cli`: `status`, `reset`, `configure`, `doctor` and `access signing-keys` use the Management API through `@nylorun/admin` with the Project's management key, or `NYLORUN_MANAGEMENT_KEY`.
64
+ - `@nylorun/studio`: local Studio needs no login. A request on the published loopback address (`localhost` or `127.0.0.1` at Studio's port) acts as signed in; hosts behind a sign-in proxy and embedding keep their login, and state-changing requests still need Studio's own `Origin`. Studio learns its Tenant from `GET /v1/tenant` with its key instead of the Admin API, and its Connections page manages vaults through `@nylorun/admin/client` at `/v1/tenant/vaults`.
65
+ - **Breaking (`@nylorun/admin`, `@nylorun/runtime`): Studio's key is derived from the admin key alone.** `deriveStudioToken(adminKey)` takes no Tenant id (HMAC-SHA256 over `nylorun/studio/v2`). The Host registers the new key's hash at its next start, replacing the old one; an app that embeds Studio and derives its key must update.
66
+ - `@nylorun/core`: `ProjectCredentialsFileSchema` gains optional `managementKey` and `managementPrincipalId`.
67
+
68
+ ### Patch Changes
69
+
70
+ - b28bdd7: **Local MCP servers work on a local Tenant, and a server that does not connect shows.** Additive; the protocol stays at 7.
71
+
72
+ - `@nylorun/runtime`: remote MCP servers (`streamable-http`, `sse`) are reached under the Host's address policy, as Action endpoints are (`NYLORUN_ENDPOINT_*`, `tenant/outbound.ts`). In the local Docker stack `localhost`, `127.0.0.1` and `[::1]` now mean the machine that runs Docker (`host.docker.internal`), so `.mcp({ x: { type: "streamable-http", url: "http://localhost:3002/x" } })` connects where it used to fail with `fetch failed`. With `NYLORUN_ENDPOINT_PRIVATE=refuse` a server on a private address is refused; with `NYLORUN_ENDPOINT_HTTP=refuse` an `http` server is refused. Redirects are still not followed. A connection failure now names its cause (`connect ECONNREFUSED …`) instead of `fetch failed`. This applies in the gateway, a harness process and an in-process Tenant. `guardedFetch` takes `stream: true`: the answer streams, unbounded, with no timeout but the caller's signal.
73
+ - `@nylorun/core`: new session event `mcp.discovered`, recorded once on the session's first turn with the MCP snapshot: one entry per declared server with `outcome` (`connected`, `refused`, `failed`), `message` and the number of `tools` it added (`McpDiscoveredPayloadSchema`, `McpServerOutcomeSchema`). A server that does not connect adds no tools for the session's life; this is where that shows in the event log, beside `mcpDiagnostics`.
74
+ - `@nylorun/agents`: `.plugin()` and `plugin()` emit a process warning (`NylorunPluginWarning`, the diagnostic's code) for each part of the package they skip, so building or registering the agent says when a plugin's MCP server was dropped. The `plugin.mcp-server-skipped` message now says why: for example, plain `http` is accepted only for `localhost`, `127.0.0.1` or `[::1]`.
75
+ - `@nylorun/studio`: the event list labels `mcp.discovered` and summarizes each server's outcome.
76
+
77
+ - 7a22f7f: **`nylorun start` opens no browser.** `start` (and `up`) prints the Runtime and Studio URLs, says to run `nylorun studio` to sign a browser in to Studio, and ends with a pointer to `nylorun --help`. It no longer mints a Studio login token or opens Studio, also in a terminal. `--no-open` is still accepted and ignored. `nylorun studio` signs a browser in as before.
78
+ - Pin core to the tested release.
79
+ - Pin runtime to the tested release.
80
+ - Pin studio to the tested release.
81
+ - Updated dependencies [3b3bdc6]
82
+ - Updated dependencies [6576e12]
83
+ - Updated dependencies [b28bdd7]
84
+ - Updated dependencies [cc107b1]
85
+ - Updated dependencies [7f763c3]
86
+ - Updated dependencies [98b0d37]
87
+ - Updated dependencies [c66d8ed]
88
+ - @nylorun/core@0.14.0-beta
89
+
3
90
  ## 0.9.0-beta
4
91
 
5
92
  ### Major Changes
package/README.md CHANGED
@@ -34,8 +34,8 @@ npx nylorun doctor # Node 24+, Docker, Compose v2, and the Tenan
34
34
  ## Commands
35
35
 
36
36
  ```sh
37
- nylorun up|start [--tenant <name>] [--no-link] [--no-studio] [--no-open] [--allow-downgrade] [--studio-embed-origin <origin>]... [--studio-embed-origin-reset] [--restate-ui]
38
- # create (first run) and start the Tenant; link the project; print the URLs; open Studio signed in
37
+ nylorun up|start [--tenant <name>] [--no-link] [--no-studio] [--allow-downgrade] [--studio-embed-origin <origin>]... [--studio-embed-origin-reset] [--restate-ui]
38
+ # create (first run) and start the Tenant; link the project; print the URLs
39
39
  # --restate-ui (or NYLORUN_RESTATE_UI=1): publish Restate's UI on loopback for this start
40
40
  nylorun down|stop [--tenant <name> | --all] # stop the containers (--all: every Tenant's); keep volumes
41
41
  nylorun status [--tenant <name>] [--json] # the Tenant, its Host root and id, services, endpoints, Runtime health
@@ -46,7 +46,7 @@ nylorun ls [--json] # the Tenants on this machine, with their sta
46
46
  nylorun delete <tenant> --yes # remove a Tenant: containers, volumes, Host root and vault key
47
47
  nylorun sandbox ls [--tenant <name>] [--label <key=value>]... [--json] # the running Tenant's sandboxes
48
48
  nylorun sandbox rm <id> [--tenant <name>] # delete a sandbox and its files
49
- nylorun key put <id> [--tenant <name>] # create or rotate the Tenant's operator key <id>; prints it once
49
+ nylorun key put <id> [--management] [--tenant <name>] # create or rotate the Tenant's application key <id> (--management: a management key); prints it once
50
50
  nylorun key list [--tenant <name>] [--json] # the Tenant's keys: id, role, when issued
51
51
  nylorun key rm <id> [--tenant <name>] # delete a key: it stops working at once
52
52
  nylorun mcp connect <url> --server <name> [--vault <id>] [--client-id <id>] [--tenant <name>] [--no-open] # sign the Tenant in to a remote MCP server with OAuth
@@ -135,18 +135,22 @@ recorded version.
135
135
  ## The Project link
136
136
 
137
137
  `nylorun start` in a project waits until the Tenant is open
138
- (`/v1/admin/status`), then writes, in a `.nylorun/` directory (mode 0700, with
138
+ (`/ready`), then writes, in a `.nylorun/` directory (mode 0700, with
139
139
  its own `.gitignore` of `*`):
140
140
 
141
141
  - `link.json`: `{ "format": 3, "tenant", "tenantId", "hostUrl", "hostId" }`.
142
142
  `tenant` is the local Tenant's name; the Tenant id is information only:
143
143
  nothing in a request selects a Tenant.
144
144
  - `credentials.json` (mode 0600): `{ "format": 1, "applicationKey",
145
- "principalId" }`, the operator key `project`. A later `start` keeps the file
146
- while its key still reaches the Tenant (one authenticated read); otherwise it
147
- writes the Tenant's `project` key, which the Host root keeps in
148
- `project-credentials.json` (mode 0600) so every checkout linked to the Tenant
149
- shares one key, or puts a new one through the Admin API.
145
+ "principalId", "managementKey", "managementPrincipalId" }`, the application
146
+ key `project` (for the Runtime API) and the management key
147
+ `project-management` (for the Management API). A later `start` keeps the file
148
+ while both keys still reach the Tenant (one authenticated read each);
149
+ otherwise it writes the Tenant's `project` and `project-management` keys,
150
+ which the Host root keeps in `project-credentials.json` (mode 0600) so every
151
+ checkout linked to the Tenant shares them, or puts new ones through
152
+ `nylorun-operate keys`. A file from an earlier release, with only an
153
+ application key, keeps it and gains the management key.
150
154
 
151
155
  Later starts reuse the Tenant and rewrite the link only when the Tenant's name,
152
156
  URL, Host or id changed (after `nylorun reset`, for example). When it writes a
@@ -162,19 +166,28 @@ both share it. `--no-link` starts a Tenant without linking the working
162
166
  directory (scripts). A link of an older nylorun (format 0 to 2) counts as no
163
167
  link: `start` replaces it.
164
168
 
165
- No key is derived from the admin key but Studio's. A project holds the operator
166
- key `project`; a key an older nylorun derived for it keeps working while it
167
- authenticates and is replaced by the operator key otherwise.
168
-
169
- ## Operator keys
170
-
171
- `nylorun key put <id>` creates the running Tenant's key `<id>` (or rotates it:
172
- the previous key stops working at once) and prints it once on stdout.
173
- `nylorun key list [--json]` shows each key's id, role and when it was issued,
174
- never the keys; `nylorun key rm <id>` deletes one. Ids match
175
- `^[a-z][a-z0-9-]{0,31}$`; `studio` belongs to Studio and is refused. Give each
176
- app server its own key. `nylorun sandbox` and `nylorun mcp` use the linked
177
- project's key, or the key `cli` they put once and keep in
169
+ No key is derived from the admin key but Studio's. A project holds the
170
+ application key `project` and the management key `project-management`; a key
171
+ an older nylorun derived for it keeps working while it authenticates and is
172
+ replaced by the application key otherwise.
173
+
174
+ ## Keys
175
+
176
+ Every Tenant serves the Runtime API, which takes **application keys**, and the
177
+ Management API (`/v1/tenant/*`), which takes **management keys**
178
+ ([SELF_HOSTING.md](../SELF_HOSTING.md#keys)). `nylorun key` manages both on the
179
+ running Tenant through `nylorun-operate` in its runtime container, so only this
180
+ machine can issue a management key.
181
+
182
+ `nylorun key put <id>` creates the application key `<id>` (or rotates it: the
183
+ previous key stops working at once) and prints it once on stdout; with
184
+ `--management` it puts a management key. `nylorun key list [--json]` shows each
185
+ key's id, role and when it was issued, never the keys; `nylorun key rm <id>`
186
+ deletes one. Ids match `^[a-z][a-z0-9-]{0,31}$`; `studio` belongs to Studio and
187
+ `bootstrap` to `NYLORUN_MANAGEMENT_KEY_FILE`, and both are refused. Putting an
188
+ id that holds the other role is refused. Give each app server its own
189
+ application key. `nylorun sandbox` and `nylorun mcp` use the linked project's
190
+ keys, or the keys `cli` and `cli-management` they put once and keep in
178
191
  `<Host root>/cli-credentials.json` (mode 0600).
179
192
 
180
193
  `nylorun mcp connect <url> --server <name>` signs the running Tenant in to a
@@ -201,12 +214,9 @@ project, and each has the label `dev.nylorun.tenant: <name>`. For Tenant `shop`:
201
214
  | Volumes | `nylorun-shop-postgres`, `nylorun-shop-restate`, `nylorun-shop-s2-lite`, `nylorun-shop-rustfs`, `nylorun-shop-workspaces` |
202
215
 
203
216
  The `harness` container (the Runtime image as `--service harness`) runs agent
204
- turns, stdio MCP servers and workspaces, apart from the `runtime` container. It
205
- holds only the harness token (`NYLORUN_HARNESS_TOKEN` in `.env`), mounts only
206
- the Tenant directory's `sandboxes/`, `plugin-data/`, `home/` and `tmp/`, and the
207
- Host root's `plugins/` read-only at its own path: put a plugin whose stdio MCP
208
- server the agent runs under `~/.nylorun/tenants/<name>/plugins/` and load it from
209
- there. `NYLORUN_HARNESS=in-process` in `.env` rolls back to running them in the
217
+ turns and workspaces, apart from the `runtime` container. It holds only the
218
+ harness token (`NYLORUN_HARNESS_TOKEN` in `.env`) and mounts only the Tenant
219
+ directory's `sandboxes/`. `NYLORUN_HARNESS=in-process` in `.env` rolls back to running them in the
210
220
  `runtime` container (no `harness` container); `nylorun status` shows which.
211
221
 
212
222
  Postgres initialises the Tenant's database with C collation. Restate runs with
@@ -214,8 +224,8 @@ its own defaults. A running Tenant uses about 1.2 GB, most of it Restate; stop
214
224
  the Tenants you are not using (`nylorun stop`, or `nylorun stop --all`). The Runtime and Studio images are pinned by
215
225
  this release (`package.json` `nylorun.runtime` and `nylorun.studio`);
216
226
  `NYLORUN_RUNTIME_IMAGE` and `NYLORUN_STUDIO_IMAGE` override them (local builds,
217
- CI). Ports publish on loopback only: the Runtime on `8787`, its operator
218
- listener (Admin API) on `8788` and Studio on `4161`, or free ports chosen on the
227
+ CI). Ports publish on loopback only: the Runtime on `8787` and Studio on
228
+ `4161`, or free ports chosen on the
219
229
  Tenant's first start, avoiding the ports other Tenants keep, and kept in its
220
230
  `.env`. Restate's UI and admin API (unauthenticated) are not published; `nylorun
221
231
  start --restate-ui` (or `NYLORUN_RESTATE_UI=1`) publishes them for that start on
@@ -223,14 +233,18 @@ start --restate-ui` (or `NYLORUN_RESTATE_UI=1`) publishes them for that start on
223
233
 
224
234
  `start`, `status`, `ls` and `nylorun studio` give Studio as
225
235
  `http://localhost:<port>` (`studio.url` in `status --json`). Studio has no
226
- password: in a terminal (not in CI, and not with `--no-open`), `start` asks the
227
- Studio container for a single-use login token with the admin key (`POST
228
- /_studio/login-tokens`) and opens `http://localhost:<port>/login?token=…` on the
229
- Tenant's page in the browser. That sets a session cookie for 30 days, which
230
- survives Studio restarts, so the printed URL keeps working in that browser. The
231
- token itself is never printed unless no browser starts. Otherwise `start` says
232
- to run `nylorun studio`, which signs a browser in the same way; `nylorun studio
233
- --no-open` prints the login URL (it works once, for two minutes) instead.
236
+ password, and `start` opens no browser: it prints the URLs and says to run
237
+ `nylorun studio` to sign a browser in (`--no-open` is accepted and ignored).
238
+ `nylorun studio` asks the Studio container for a single-use login token with the
239
+ admin key (`POST /_studio/login-tokens`) and opens
240
+ `http://localhost:<port>/login?token=…` on the Tenant's page in the browser.
241
+ That sets a session cookie for 30 days, which survives Studio restarts, so the
242
+ printed URL keeps working in that browser. The token itself is never printed
243
+ unless no browser starts; `nylorun studio --no-open` prints the login URL (it
244
+ works once, for two minutes) instead.
245
+ Studio needs no login on its published loopback address (`localhost` or
246
+ `127.0.0.1` at its port): a browser on this machine is served as signed in. A
247
+ host behind a sign-in proxy and an embedding app keep their sign-in.
234
248
 
235
249
  Browsers share cookies across the ports of one host, so each Tenant's Studio
236
250
  sets its own session cookie, `nylorun_studio_<name>`
@@ -309,8 +323,8 @@ bind-mounted into the Runtime and Studio containers, and holds `tenant.json`
309
323
  (`{ "format": 1, "name", "project"? }`: the Tenant's name and the project it
310
324
  was created for), `host.json` (the client-facing host and port),
311
325
  `host-credentials.json` (the admin key, mode 0600), `project-credentials.json`
312
- and `cli-credentials.json` (the operator keys `project` and `cli`, mode 0600, when
313
- they were put), the Docker Compose files in `docker/`, the Tenant directory `tenant/` (homes, logs) and `keys/vault-kek`,
326
+ and `cli-credentials.json` (the keys `project` and `project-management`, `cli`
327
+ and `cli-management`, mode 0600, when they were put), the Docker Compose files in `docker/`, the Tenant directory `tenant/` (homes, logs) and `keys/vault-kek`,
314
328
  the Tenant's vault key, which only the gateway container mounts. The Tenant's data lives in its Postgres, Restate and S2 volumes.
315
329
 
316
330
  `nylorun reset` deletes the selected Tenant's volumes, `tenant/` and `keys/`, and keeps
package/dist/doctor.js CHANGED
@@ -42,7 +42,7 @@ export async function doctorStack(options) {
42
42
  (!report.tenant.runtime.healthy ||
43
43
  !report.tenant.gateway.healthy ||
44
44
  (report.tenant.harness.mode === "remote" && !report.tenant.harness.healthy) ||
45
- report.tenant.tenant?.cause !== undefined);
45
+ (report.tenant.tenant !== undefined && report.tenant.tenant.state !== "open"));
46
46
  const failed = !nodeOk || !checks.docker.ok || checks.compose?.ok === false || broken;
47
47
  if (options.json) {
48
48
  log(JSON.stringify(report, null, 2));
@@ -103,7 +103,7 @@ export async function doctorStack(options) {
103
103
  "tenant id",
104
104
  local.tenant.state === "open"
105
105
  ? `✓ ${local.tenant.id ?? "?"} open`
106
- : `✗ ${local.tenant.id ?? "?"} unavailable${local.tenant.cause ? `: ${local.tenant.cause.code}: ${local.tenant.cause.repair}` : " (opening)"}`,
106
+ : `✗ ${local.tenant.id ?? "?"} unavailable${local.tenant.cause || local.tenant.message ? `: ${[local.tenant.cause, local.tenant.message].filter(Boolean).join(": ")}` : " (opening)"}`,
107
107
  ]);
108
108
  rows.push(["studio", `${local.studio.url ?? "?"} · ${local.studio.state}`]);
109
109
  }
@@ -22,16 +22,24 @@ export declare function writeProjectLink(projectRoot: string, link: {
22
22
  hostId: string;
23
23
  tenantId: string;
24
24
  }): Promise<void>;
25
- /** An application key and its principal: `credentials.json`'s content (format 1). */
25
+ /**
26
+ * An application key and its principal, and the management key and its principal when the file
27
+ * has one: `credentials.json`'s content (format 1).
28
+ */
26
29
  export interface KeyCredentials {
27
30
  applicationKey: string;
28
31
  principalId: string;
32
+ management?: {
33
+ key: string;
34
+ principalId: string;
35
+ };
29
36
  }
30
- /** The Project's application key and principal, or undefined when absent or unreadable. */
37
+ /** The Project's keys, or undefined when absent or unreadable. */
31
38
  export declare function readProjectCredentials(projectRoot: string): Promise<KeyCredentials | undefined>;
32
39
  /**
33
- * A credentials file (`{ format: 1, applicationKey, principalId }`): the Project's, or one the
34
- * nylorun commands keep in the Host root. Undefined when absent or unreadable.
40
+ * A credentials file (`{ format: 1, applicationKey, principalId, managementKey?,
41
+ * managementPrincipalId? }`): the Project's, or one the nylorun commands keep in the Host root.
42
+ * Undefined when absent or unreadable.
35
43
  */
36
44
  export declare function readCredentialsFile(path: string): Promise<KeyCredentials | undefined>;
37
45
  /** `.nylorun/credentials.json` (format 1, mode 0600). */
@@ -67,20 +67,27 @@ export async function writeProjectLink(projectRoot, link) {
67
67
  tenantId: link.tenantId,
68
68
  });
69
69
  }
70
- /** The Project's application key and principal, or undefined when absent or unreadable. */
70
+ /** The Project's keys, or undefined when absent or unreadable. */
71
71
  export async function readProjectCredentials(projectRoot) {
72
72
  return await readCredentialsFile(credentialsPath(projectRoot));
73
73
  }
74
74
  /**
75
- * A credentials file (`{ format: 1, applicationKey, principalId }`): the Project's, or one the
76
- * nylorun commands keep in the Host root. Undefined when absent or unreadable.
75
+ * A credentials file (`{ format: 1, applicationKey, principalId, managementKey?,
76
+ * managementPrincipalId? }`): the Project's, or one the nylorun commands keep in the Host root.
77
+ * Undefined when absent or unreadable.
77
78
  */
78
79
  export async function readCredentialsFile(path) {
79
80
  try {
80
81
  const value = JSON.parse(await readFile(path, "utf8"));
81
- return typeof value.applicationKey === "string" && typeof value.principalId === "string"
82
- ? { applicationKey: value.applicationKey, principalId: value.principalId }
83
- : undefined;
82
+ if (typeof value.applicationKey !== "string" || typeof value.principalId !== "string")
83
+ return undefined;
84
+ return {
85
+ applicationKey: value.applicationKey,
86
+ principalId: value.principalId,
87
+ ...(typeof value.managementKey === "string" && typeof value.managementPrincipalId === "string"
88
+ ? { management: { key: value.managementKey, principalId: value.managementPrincipalId } }
89
+ : {}),
90
+ };
84
91
  }
85
92
  catch {
86
93
  return undefined;
@@ -97,5 +104,11 @@ export async function writeCredentialsFile(path, credentials) {
97
104
  format: 1,
98
105
  applicationKey: credentials.applicationKey,
99
106
  principalId: credentials.principalId,
107
+ ...(credentials.management
108
+ ? {
109
+ managementKey: credentials.management.key,
110
+ managementPrincipalId: credentials.management.principalId,
111
+ }
112
+ : {}),
100
113
  });
101
114
  }
@@ -21,7 +21,8 @@ export declare function readProjectEnv(projectRoot: string): Record<string, stri
21
21
  export declare function seedTenant(input: {
22
22
  fetch: FetchLike;
23
23
  runtimeUrl: string;
24
- applicationKey: string;
24
+ /** The Project's management key: seeding is the Management API's. */
25
+ managementKey: string;
25
26
  projectRoot: string;
26
27
  }): Promise<SeedResult>;
27
28
  export {};
@@ -45,11 +45,11 @@ function modelFromEnv(env, projectRoot) {
45
45
  }
46
46
  return undefined;
47
47
  }
48
- async function call(fetch, url, applicationKey, init = {}) {
48
+ async function call(fetch, url, managementKey, init = {}) {
49
49
  const response = await fetch(url, {
50
50
  ...(init.method ? { method: init.method } : {}),
51
51
  headers: {
52
- authorization: `Bearer ${applicationKey}`,
52
+ authorization: `Bearer ${managementKey}`,
53
53
  [PROTOCOL_HEADER]: String(PROTOCOL_VERSION),
54
54
  accept: "application/json",
55
55
  ...(init.body === undefined ? {} : { "content-type": "application/json" }),
@@ -76,7 +76,7 @@ export async function seedTenant(input) {
76
76
  const result = { applied: [], kept: [] };
77
77
  const sandbox = env.NYLORUN_SANDBOX?.trim();
78
78
  if (sandbox === "auto" || sandbox === "virtual") {
79
- const body = (await call(input.fetch, `${base}/v1/tenant/config/seed`, input.applicationKey, {
79
+ const body = (await call(input.fetch, `${base}/v1/tenant/config/seed`, input.managementKey, {
80
80
  method: "PUT",
81
81
  body: { requestId: randomUUID(), sandbox: { backend: sandbox } },
82
82
  }));
@@ -90,12 +90,12 @@ export async function seedTenant(input) {
90
90
  const seeded = modelFromEnv(env, input.projectRoot);
91
91
  if (!seeded)
92
92
  return result;
93
- const current = (await call(input.fetch, `${base}/v1/tenant/model`, input.applicationKey));
93
+ const current = (await call(input.fetch, `${base}/v1/tenant/model`, input.managementKey));
94
94
  if (current.configured === true) {
95
95
  result.kept.push("model");
96
96
  return result;
97
97
  }
98
- await call(input.fetch, `${base}/v1/tenant/model`, input.applicationKey, {
98
+ await call(input.fetch, `${base}/v1/tenant/model`, input.managementKey, {
99
99
  method: "PUT",
100
100
  body: {
101
101
  requestId: randomUUID(),
@@ -1,10 +1,9 @@
1
- import type { HostTenant } from "@nylorun/core/contracts";
2
1
  import { type ComposeService, type DockerRunner } from "./docker.js";
3
2
  import type { HarnessMode, SandboxStackEnv } from "./env-file.js";
4
3
  import { type StackPaths } from "./paths.js";
5
4
  import type { PortProbe } from "./ports.js";
6
5
  import { type FetchLike } from "./studio-login.js";
7
- import { type AdminEndpoint } from "./operator-keys.js";
6
+ import { type OperateEndpoint } from "./operator-keys.js";
8
7
  export declare const STACK_SERVICES: readonly ["postgres", "restate", "s2-lite", "rustfs", "gateway", "runtime", "harness", "studio"];
9
8
  export declare const stackUsage: string;
10
9
  export interface StackDeps {
@@ -21,7 +20,7 @@ export interface StackDeps {
21
20
  err(line: string): void;
22
21
  /** Ask a yes/no question on the terminal; undefined when not interactive. */
23
22
  confirm?(question: string): Promise<boolean>;
24
- /** A developer is at a terminal: `start` may open a browser. */
23
+ /** A developer is at a terminal. */
25
24
  interactive?: boolean;
26
25
  /** Open `url` in a browser; false when no browser could be started. */
27
26
  openBrowser(url: string): Promise<boolean>;
@@ -53,10 +52,20 @@ export declare function parseStackFlags(args: readonly string[], allowed: {
53
52
  lists?: readonly string[];
54
53
  aliases?: Record<string, string>;
55
54
  }, usage: string): Flags;
56
- /** The Host's one Tenant as `/v1/admin/status` reports it (no envelope). */
57
- export type StackTenant = Pick<HostTenant, "id" | "name" | "state" | "cause">;
58
- /** Printed by `start` when it does not open Studio itself. */
55
+ /** The Host's one Tenant as `nylorun-operate status --json` reports it. */
56
+ export interface StackTenant {
57
+ /** Null until the Tenant row has been read. */
58
+ id: string | null;
59
+ name: string | null;
60
+ state: "open" | "unavailable";
61
+ /** Why it cannot open (`schema-too-new`, `database-layout-old`, …); `message` says more. */
62
+ cause?: string;
63
+ message?: string;
64
+ }
65
+ /** Printed by `start`, which prints Studio's URL and opens no browser. */
59
66
  export declare const STUDIO_SIGN_IN_HINT = "To sign a browser in to Studio, run \"npx nylorun studio\".";
67
+ /** Printed last by `start`. */
68
+ export declare const HELP_HINT = "Run \"npx nylorun --help\" to see what else nylorun can do.";
60
69
  export interface StackStatus {
61
70
  /** The Tenant's name. */
62
71
  name: string;
@@ -67,13 +76,13 @@ export interface StackStatus {
67
76
  state: "running" | "stopped" | "absent";
68
77
  runtime: {
69
78
  url?: string;
70
- /** The Admin API (operator listener), when the Tenant publishes one. */
71
- adminUrl?: string;
72
79
  healthy: boolean;
80
+ /** `/ready`: the Tenant is open and the Runtime's stores answer. */
81
+ ready: boolean;
73
82
  version?: string;
74
83
  hostId?: string;
75
84
  };
76
- /** The Tenant as the Runtime reports it, while it answers. */
85
+ /** The Tenant as `nylorun-operate status` reports it, while the runtime container runs. */
77
86
  tenant?: StackTenant;
78
87
  studio: {
79
88
  /** `http://localhost:<port>`: also what an embedding app frames (same site as its own localhost). */
@@ -117,9 +126,8 @@ export interface StackEndpoints {
117
126
  home: string;
118
127
  /** `http://localhost:<port>` */
119
128
  runtimeUrl: string;
120
- /** The operator listener (Admin API). */
121
- adminUrl: string;
122
129
  hostId: string;
130
+ /** The admin key (host-credentials.json): it mints Studio logins. */
123
131
  adminKey: string;
124
132
  studioPort: number;
125
133
  /** `http://localhost:<studio port>` */
@@ -129,31 +137,32 @@ export interface StackEndpoints {
129
137
  /** This call started the Runtime (it was not running before). */
130
138
  started: boolean;
131
139
  }
132
- /** The running Tenant's API, reached with an operator key (`nylorun sandbox`). */
140
+ /** The running Tenant's APIs and their keys (`nylorun sandbox`, `nylorun mcp`). */
133
141
  export interface TenantApi {
134
142
  /** The Tenant's name. */
135
143
  name: string;
136
144
  runtimeUrl: string;
137
145
  /**
138
- * The linked project's key (`.nylorun/credentials.json`) when it reaches the Tenant, else
139
- * the operator key `cli` the Host root keeps (`cli-credentials.json`).
146
+ * The linked project's keys (`.nylorun/credentials.json`) when both reach the Tenant, else
147
+ * the keys `cli` and `cli-management` the Host root keeps (`cli-credentials.json`).
140
148
  */
141
149
  applicationKey: string;
150
+ managementKey: string;
142
151
  }
143
- /** The selected Tenant's Admin API while it runs: `nylorun key` and `runningTenantApi`. */
144
- export interface RunningAdmin extends AdminEndpoint {
152
+ /** The selected Tenant's `nylorun-operate` while it runs: `nylorun key` and `runningTenantApi`. */
153
+ export interface RunningOperate extends OperateEndpoint {
145
154
  /** The Tenant's name. */
146
155
  name: string;
147
156
  runtimeUrl: string;
148
157
  paths: StackPaths;
149
158
  }
150
159
  /**
151
- * The selected Tenant's Admin API while it runs; exit 3 when it is not running, 7 when it is
152
- * not open.
160
+ * The selected Tenant's `nylorun-operate` while it runs; exit 3 when it is not running, 7 when
161
+ * it is not open.
153
162
  */
154
- export declare function runningAdmin(deps: StackDeps, options?: {
163
+ export declare function runningOperate(deps: StackDeps, options?: {
155
164
  name?: string;
156
- }): Promise<RunningAdmin>;
165
+ }): Promise<RunningOperate>;
157
166
  /**
158
167
  * The selected Tenant's API while it runs: commands that read or change the Tenant's data never
159
168
  * start it. Exit 3 when it is not running, 7 when it is not open.