@nylorun/runtime 0.17.0-beta → 0.19.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.
Files changed (223) hide show
  1. package/CHANGELOG.md +155 -0
  2. package/README.md +89 -58
  3. package/dist/adapters/execution/restate.d.ts +1 -3
  4. package/dist/adapters/execution/restate.js +4 -31
  5. package/dist/adapters/replication/pgoutput.js +1 -1
  6. package/dist/adapters/sandbox/local.js +1 -1
  7. package/dist/adapters/sandbox/virtual.js +1 -1
  8. package/dist/api/ag-ui/routes.js +1 -1
  9. package/dist/api/ag-ui/translate.d.ts +1 -3
  10. package/dist/api/ag-ui/translate.js +2 -17
  11. package/dist/api/components.d.ts +4871 -2614
  12. package/dist/api/components.js +12 -11
  13. package/dist/api/gate/routes.d.ts +7 -8
  14. package/dist/api/gate/routes.js +58 -52
  15. package/dist/api/http/app.d.ts +6 -0
  16. package/dist/api/http/app.js +18 -14
  17. package/dist/api/http/define.d.ts +14 -2
  18. package/dist/api/http/define.js +34 -15
  19. package/dist/api/http/routes/access.d.ts +3 -3
  20. package/dist/api/http/routes/access.js +18 -17
  21. package/dist/api/http/routes/artifacts.js +2 -4
  22. package/dist/api/http/routes/files.d.ts +8 -0
  23. package/dist/api/http/routes/files.js +77 -0
  24. package/dist/api/http/routes/keys.d.ts +10 -0
  25. package/dist/api/http/routes/keys.js +64 -0
  26. package/dist/api/http/routes/me.js +5 -2
  27. package/dist/api/http/routes/reads.d.ts +66 -0
  28. package/dist/api/http/routes/reads.js +127 -0
  29. package/dist/api/http/routes/sandboxes.js +7 -4
  30. package/dist/api/http/routes/sessions.js +25 -8
  31. package/dist/api/http/routes/tenant.js +15 -18
  32. package/dist/api/http/routes/vaults.d.ts +10 -9
  33. package/dist/api/http/routes/vaults.js +27 -23
  34. package/dist/api/openapi.d.ts +10 -11
  35. package/dist/api/openapi.js +288 -63
  36. package/dist/artifacts/links.js +1 -1
  37. package/dist/core/flow-host.d.ts +44 -23
  38. package/dist/core/flow-host.js +88 -106
  39. package/dist/core/provider.d.ts +4 -1
  40. package/dist/core/provider.js +6 -1
  41. package/dist/core/sandbox-routes.d.ts +0 -9
  42. package/dist/core/sandbox-routes.js +2 -21
  43. package/dist/execution/memory.d.ts +4 -5
  44. package/dist/execution/memory.js +11 -25
  45. package/dist/execution/types.d.ts +3 -33
  46. package/dist/execution/types.js +2 -12
  47. package/dist/gates/http-tool.d.ts +86 -0
  48. package/dist/gates/http-tool.js +197 -0
  49. package/dist/gates/mcp-handler.d.ts +3 -0
  50. package/dist/gates/mcp-handler.js +1 -3
  51. package/dist/gates/meter.d.ts +2 -2
  52. package/dist/gates/meter.js +6 -0
  53. package/dist/gates/tenant-vaults.d.ts +5 -9
  54. package/dist/gates/tool-calls.d.ts +7 -15
  55. package/dist/gates/tool-calls.js +4 -4
  56. package/dist/gates/tool-client.d.ts +1 -1
  57. package/dist/gates/tool-client.js +23 -47
  58. package/dist/gates/tool-contract.d.ts +27 -39
  59. package/dist/gates/tool-contract.js +30 -21
  60. package/dist/gates/tool-gate.d.ts +25 -24
  61. package/dist/gates/tool-gate.js +11 -7
  62. package/dist/harness/calls.d.ts +10 -4
  63. package/dist/harness/calls.js +15 -6
  64. package/dist/harness/executors.d.ts +1 -1
  65. package/dist/harness/executors.js +17 -12
  66. package/dist/harness/main.d.ts +2 -2
  67. package/dist/harness/main.js +5 -13
  68. package/dist/harness/service.d.ts +4 -5
  69. package/dist/harness/service.js +9 -2
  70. package/dist/harness-api/in-process.js +2 -2
  71. package/dist/harness-api/record.d.ts +6 -3
  72. package/dist/harness-api/record.js +65 -72
  73. package/dist/harness-api/server.d.ts +1 -9
  74. package/dist/harness-api/server.js +19 -17
  75. package/dist/harness-api/start.js +1 -5
  76. package/dist/harness-api/workspace.js +2 -3
  77. package/dist/harness-api/ws-server.js +1 -1
  78. package/dist/host/app.d.ts +2 -9
  79. package/dist/host/app.js +15 -25
  80. package/dist/host/config-for.d.ts +1 -5
  81. package/dist/host/config-for.js +0 -3
  82. package/dist/host/config.d.ts +0 -5
  83. package/dist/host/create-host.d.ts +3 -33
  84. package/dist/host/create-host.js +18 -94
  85. package/dist/host/environment.d.ts +0 -3
  86. package/dist/host/environment.js +0 -9
  87. package/dist/host/gates.d.ts +4 -1
  88. package/dist/host/gates.js +3 -2
  89. package/dist/host/http.d.ts +1 -2
  90. package/dist/host/http.js +1 -7
  91. package/dist/host/index.d.ts +2 -2
  92. package/dist/host/index.js +2 -2
  93. package/dist/host/main.js +27 -34
  94. package/dist/host/operate.d.ts +29 -0
  95. package/dist/host/operate.js +223 -0
  96. package/dist/host/stack-config.d.ts +12 -12
  97. package/dist/host/stack-config.js +5 -32
  98. package/dist/index.d.ts +1 -1
  99. package/dist/index.js +1 -1
  100. package/dist/infra/workers.d.ts +1 -1
  101. package/dist/keys/keys.js +2 -2
  102. package/dist/management-openapi.json +6491 -0
  103. package/dist/mcp/connect.d.ts +8 -4
  104. package/dist/mcp/connect.js +6 -26
  105. package/dist/mcp/pool.d.ts +5 -9
  106. package/dist/mcp/pool.js +8 -19
  107. package/dist/mcp/snapshot.d.ts +8 -2
  108. package/dist/mcp/snapshot.js +29 -2
  109. package/dist/media.d.ts +1 -1
  110. package/dist/media.js +1 -1
  111. package/dist/model/pi-model.js +3 -0
  112. package/dist/openapi.json +9372 -17030
  113. package/dist/reads/access.d.ts +4 -0
  114. package/dist/reads/access.js +12 -0
  115. package/dist/reads/cursor.d.ts +11 -0
  116. package/dist/reads/cursor.js +80 -0
  117. package/dist/reads/types.d.ts +25 -0
  118. package/dist/reads/types.js +1 -0
  119. package/dist/sandbox/manager.d.ts +16 -0
  120. package/dist/sandbox/manager.js +40 -0
  121. package/dist/sandbox/session-sandbox.js +13 -3
  122. package/dist/sandbox/skills.d.ts +13 -0
  123. package/dist/sandbox/skills.js +46 -0
  124. package/dist/sandbox/types.d.ts +2 -2
  125. package/dist/store/ownership.d.ts +9 -0
  126. package/dist/store/ownership.js +13 -0
  127. package/dist/store/postgres/connect.d.ts +2 -0
  128. package/dist/store/postgres/connect.js +29 -0
  129. package/dist/store/postgres/drizzle/0011_key_roles.sql +3 -0
  130. package/dist/store/postgres/drizzle/0012_session_reads.sql +8 -0
  131. package/dist/store/postgres/drizzle/0013_definition_files.sql +16 -0
  132. package/dist/store/postgres/drizzle/0014_action_endpoints_removed.sql +2 -0
  133. package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
  134. package/dist/store/postgres/reads.d.ts +3 -0
  135. package/dist/store/postgres/reads.js +272 -0
  136. package/dist/store/postgres/schema.d.ts +294 -489
  137. package/dist/store/postgres/schema.js +41 -76
  138. package/dist/store/postgres/store.js +51 -154
  139. package/dist/store/postgres/tenant.d.ts +34 -2
  140. package/dist/store/postgres/tenant.js +16 -32
  141. package/dist/store/types.d.ts +27 -96
  142. package/dist/store/types.js +0 -1
  143. package/dist/streams/control.d.ts +0 -2
  144. package/dist/streams/control.js +0 -5
  145. package/dist/streams/types.d.ts +1 -10
  146. package/dist/tenant/advance.d.ts +3 -3
  147. package/dist/tenant/advance.js +10 -7
  148. package/dist/tenant/auth.d.ts +14 -9
  149. package/dist/tenant/auth.js +24 -28
  150. package/dist/tenant/cause.d.ts +3 -3
  151. package/dist/tenant/cause.js +1 -1
  152. package/dist/tenant/commands.d.ts +2 -18
  153. package/dist/tenant/commands.js +35 -172
  154. package/dist/tenant/context.d.ts +14 -24
  155. package/dist/tenant/definition-files.d.ts +24 -0
  156. package/dist/tenant/definition-files.js +103 -0
  157. package/dist/tenant/effects.d.ts +18 -20
  158. package/dist/tenant/effects.js +56 -119
  159. package/dist/tenant/ephemeral.d.ts +7 -6
  160. package/dist/tenant/ephemeral.js +3 -11
  161. package/dist/tenant/host-token.js +1 -1
  162. package/dist/tenant/jwt.js +1 -1
  163. package/dist/tenant/module.d.ts +1 -4
  164. package/dist/tenant/module.js +3 -12
  165. package/dist/tenant/operator-keys.d.ts +34 -8
  166. package/dist/tenant/operator-keys.js +50 -18
  167. package/dist/tenant/outbound.d.ts +10 -1
  168. package/dist/tenant/outbound.js +44 -27
  169. package/dist/tenant/paths.d.ts +1 -1
  170. package/dist/tenant/paths.js +1 -3
  171. package/dist/tenant/principals.d.ts +11 -2
  172. package/dist/tenant/principals.js +27 -8
  173. package/dist/tenant/run-token.js +3 -3
  174. package/dist/tenant/runtime.d.ts +4 -6
  175. package/dist/tenant/runtime.js +19 -18
  176. package/dist/tenant/sandboxes.d.ts +4 -1
  177. package/dist/tenant/sandboxes.js +11 -0
  178. package/dist/tenant/scheduler.d.ts +1 -5
  179. package/dist/tenant/scheduler.js +2 -5
  180. package/dist/tenant/session-streams.d.ts +2 -1
  181. package/dist/tenant/session-streams.js +39 -5
  182. package/dist/tenant/session.d.ts +8 -41
  183. package/dist/tenant/session.js +0 -37
  184. package/dist/tenant/sessions.js +16 -8
  185. package/dist/tenant/signing-keys.d.ts +3 -3
  186. package/dist/tenant/signing-keys.js +5 -5
  187. package/dist/tenant/skill-tool.d.ts +20 -0
  188. package/dist/tenant/skill-tool.js +72 -0
  189. package/dist/tenant/slim.d.ts +1 -1
  190. package/dist/tenant/slim.js +1 -1
  191. package/dist/tenant/status.js +7 -12
  192. package/dist/tenant/store-pg.d.ts +15 -0
  193. package/dist/tenant/store-pg.js +7 -3
  194. package/dist/tenant/streams.d.ts +3 -10
  195. package/dist/tenant/streams.js +1 -24
  196. package/dist/tenant/sweep.js +0 -5
  197. package/dist/tenant/transcript.d.ts +2 -2
  198. package/dist/tenant/transcript.js +2 -2
  199. package/dist/tenant/types.d.ts +15 -22
  200. package/dist/tenant/worker.d.ts +1 -3
  201. package/dist/tenant/worker.js +3 -8
  202. package/dist/vault/sources.d.ts +7 -1
  203. package/dist/vault/sources.js +22 -11
  204. package/dist/version.d.ts +1 -1
  205. package/dist/version.js +1 -1
  206. package/package.json +4 -4
  207. package/dist/admin-openapi.json +0 -904
  208. package/dist/api/http/routes/actions.d.ts +0 -8
  209. package/dist/api/http/routes/actions.js +0 -75
  210. package/dist/api/http/routes/endpoints.d.ts +0 -7
  211. package/dist/api/http/routes/endpoints.js +0 -100
  212. package/dist/host/admin-api.d.ts +0 -30
  213. package/dist/host/admin-api.js +0 -140
  214. package/dist/mcp/stdio.d.ts +0 -17
  215. package/dist/mcp/stdio.js +0 -82
  216. package/dist/tenant/actions.d.ts +0 -14
  217. package/dist/tenant/actions.js +0 -29
  218. package/dist/tenant/delivery-token.d.ts +0 -41
  219. package/dist/tenant/delivery-token.js +0 -104
  220. package/dist/tenant/delivery.d.ts +0 -62
  221. package/dist/tenant/delivery.js +0 -481
  222. package/dist/tenant/endpoints.d.ts +0 -67
  223. package/dist/tenant/endpoints.js +0 -90
@@ -1,12 +1,10 @@
1
1
  import { tenantPaths } from "../tenant/paths.js";
2
- import { tenantChildEnvironment } from "./environment.js";
3
2
  /**
4
3
  * Builds the TenantConfig of the Host's Tenant for `openTenantRuntime`.
5
4
  * The sandbox backend here is the Host default; a Tenant's seeded `sandbox.backend`
6
5
  * setting overrides it when the Tenant opens (I1, A18).
7
6
  */
8
7
  export function configForFactory(options) {
9
- const { baseline } = options;
10
8
  return (id) => {
11
9
  const tenant = tenantPaths(options.hostRoot);
12
10
  const sandboxBackend = "auto";
@@ -19,7 +17,6 @@ export function configForFactory(options) {
19
17
  ...(options.delivery ? { delivery: options.delivery } : {}),
20
18
  ...(options.issuers ? { issuers: options.issuers } : {}),
21
19
  ...(options.publicUrl ? { publicUrl: options.publicUrl } : {}),
22
- childEnv: tenantChildEnvironment(baseline, options.hostConfig, tenant),
23
20
  logger: {
24
21
  info: (message, fields) => options.logger.info(message, { tenantId: id, ...fields }),
25
22
  warn: (message, fields) => options.logger.warn(message, { tenantId: id, ...fields }),
@@ -4,11 +4,6 @@ export interface HostConfigFile {
4
4
  host: string;
5
5
  port: number;
6
6
  allowNonLoopback?: boolean;
7
- /**
8
- * The operator listener's port: the Admin API moves there (on `host`, loopback only), and
9
- * `port` serves the Tenant API alone. Absent: one port serves both.
10
- */
11
- adminPort?: number;
12
7
  proxy?: {
13
8
  httpsProxy?: string;
14
9
  noProxy?: string;
@@ -1,15 +1,14 @@
1
1
  import type { Logger, TenantModule } from "../tenant/types.js";
2
- import type { HostConfigFile, HostCredentialsFile } from "./config.js";
2
+ import type { HostConfigFile } from "./config.js";
3
3
  import type { ContainerListen } from "./stack-config.js";
4
4
  export interface CreateHostOptions {
5
5
  hostRoot: string;
6
6
  module: TenantModule;
7
7
  config: HostConfigFile;
8
- credentials: HostCredentialsFile;
9
8
  logger: Logger;
10
9
  /** Diagnostic package version of `@nylorun/core` for `/health`. */
11
10
  coreVersion: string;
12
- /** Process id reported by `/health` and admin host status. Defaults to `process.pid`. */
11
+ /** Process id reported by `/health`. Defaults to `process.pid`. */
13
12
  pid?: number;
14
13
  /**
15
14
  * Container mode: bind this address and port instead of host.json's, and
@@ -17,19 +16,6 @@ export interface CreateHostOptions {
17
16
  * loopback-only rule). host.json then describes the client-facing address.
18
17
  */
19
18
  listen?: ContainerListen;
20
- /**
21
- * The client-facing URL `/v1/admin/status` reports as `host.url`
22
- * (`NYLORUN_PUBLIC_URL`). Defaults to the bound address, which in container
23
- * mode is `http://0.0.0.0:4000`.
24
- */
25
- publicUrl?: string;
26
- /**
27
- * The operator listener: the Admin API (and the Tenant API, never to browsers) on
28
- * its own address, kept off the network that reaches the Tenant API. When set, the main
29
- * listener is public and answers admin routes with the opaque 404. Absent: one listener
30
- * serves everything, as before.
31
- */
32
- operator?: OperatorListen;
33
19
  /**
34
20
  * Infrastructure readiness (`infra/readiness.ts`). `/ready` adds its checks
35
21
  * and answers 503 while it reports not ok. Default: the listener and the
@@ -41,7 +27,7 @@ export interface CreateHostOptions {
41
27
  }>;
42
28
  /**
43
29
  * Shutdown steps around closing the Tenant. `close()` runs them whatever asked for it
44
- * (SIGTERM in `host/main.ts`, `POST /v1/admin/host/shutdown`): the listener stops, then
30
+ * (SIGTERM in `host/main.ts`): the listener stops, then
45
31
  * `beforeTenants` (stop the Worker so no advance starts on the closing Tenant), the Tenant
46
32
  * closes, then `afterTenants` (end the infrastructure clients). A failing step is logged
47
33
  * and shutdown goes on.
@@ -51,27 +37,11 @@ export interface CreateHostOptions {
51
37
  afterTenants?(): Promise<void>;
52
38
  };
53
39
  }
54
- /** Where the operator listener binds, and the `Host` values it accepts. */
55
- export interface OperatorListen {
56
- host: string;
57
- port: number;
58
- /** Exact `Host` values (lowercase `name:port`); absent means the loopback forms of the port. */
59
- allowedHosts?: readonly string[];
60
- }
61
- /**
62
- * What a listener serves. `combined`: everything (one port). `public`: the Tenant API, to
63
- * servers and to browsers with a trusted issuer's token; admin routes are the opaque 404. `operator`: the Admin API and
64
- * the Tenant API, never to browsers.
65
- */
66
- export type ListenerRole = "combined" | "public" | "operator";
67
40
  export interface HostServer {
68
41
  listen(): Promise<void>;
69
42
  close(): Promise<void>;
70
43
  /** Settles once `close()` has finished, whatever called it. */
71
44
  readonly closed: Promise<void>;
72
45
  readonly url: string;
73
- /** Where the Admin API answers: the operator listener, or `url` when there is one listener. */
74
- readonly adminUrl: string;
75
46
  }
76
- export { adminKeyMatches } from "./http.js";
77
47
  export declare function createHost(options: CreateHostOptions): HostServer;
@@ -1,15 +1,9 @@
1
1
  import { createServer, } from "node:http";
2
2
  import { getRequestListener, RequestError } from "@hono/node-server";
3
- import { HOST_PROTOCOL } from "@nylorun/core/compatibility";
4
- import { AdminStatusSchema } from "@nylorun/core/contracts";
5
- import { adminDocument } from "../api/openapi.js";
6
- import { createAdminApi } from "./admin-api.js";
7
3
  import { createHostApp } from "./app.js";
8
4
  import { bindListener as bind, EXIT_NON_LOOPBACK, headerValue, HostListenError, isAllowedRequestHost, isLoopbackHost, pathnameIsLogged, redactRoutePath, rejectedResponse, sendRejected, } from "./http.js";
9
- import { RUNTIME_VERSION } from "../version.js";
10
- export { adminKeyMatches } from "./http.js";
11
5
  export function createHost(options) {
12
- const { hostRoot, module, config, credentials, logger, coreVersion, } = options;
6
+ const { module, config, logger, coreVersion } = options;
13
7
  const pid = options.pid ?? process.pid;
14
8
  const containerListen = options.listen;
15
9
  const bindHost = containerListen?.host ?? config.host;
@@ -17,55 +11,20 @@ export function createHost(options) {
17
11
  let server;
18
12
  let url = "";
19
13
  let listenPort = bindPort;
20
- const operator = options.operator;
21
- let operatorServer;
22
- let operatorPort = operator?.port ?? 0;
23
- let adminUrl = "";
24
- const mainRole = operator ? "public" : "combined";
25
14
  let closing = false;
26
15
  let closePromise;
27
- const adminStatusBody = async () => {
28
- const aggregate = await module.summarize();
29
- return AdminStatusSchema.parse({
30
- service: "nylorun-runtime",
31
- version: RUNTIME_VERSION,
32
- protocol: {
33
- min: HOST_PROTOCOL.min,
34
- max: HOST_PROTOCOL.max,
35
- features: [...HOST_PROTOCOL.features],
36
- },
37
- tenant: module.tenant(),
38
- aggregate,
39
- host: {
40
- hostId: config.hostId,
41
- url: options.publicUrl ?? url,
42
- pid,
43
- },
44
- });
45
- };
46
- const adminApi = createAdminApi({
47
- status: adminStatusBody,
48
- shutdown: () => void close(),
49
- document: adminDocument,
50
- operatorKeys: async () => {
51
- const resolved = await module.resolve();
52
- return resolved.kind === "open" ? resolved.handle.operatorKeys?.() : undefined;
53
- },
54
- });
55
16
  const app = createHostApp({
56
17
  module,
57
18
  logger,
58
19
  hostId: config.hostId,
59
- adminKey: credentials.adminKey,
60
20
  coreVersion,
61
21
  pid,
62
22
  ...(options.readiness ? { readiness: options.readiness } : {}),
63
- listening: () => Boolean(server?.listening) && (!operator || Boolean(operatorServer?.listening)),
23
+ listening: () => Boolean(server?.listening),
64
24
  closing: () => closing,
65
- admin: async (request, node) => await adminApi.fetch(request, node),
66
25
  });
67
- /** Where a listener's requests go once their `Host` header checks out. */
68
- const pipeline = (role) => getRequestListener(async (request, node) => alreadySent(await app.fetch(request, { ...node, role })), {
26
+ /** Where requests go once their `Host` header checks out. */
27
+ const pipeline = () => getRequestListener(async (request, node) => alreadySent(await app.fetch(request, node)), {
69
28
  // The Runtime runs inside other processes (`startEphemeralRuntime`): leave their
70
29
  // `Request` and `Response` alone.
71
30
  overrideGlobalObjects: false,
@@ -79,30 +38,23 @@ export function createHost(options) {
79
38
  return rejectedResponse(500, "internal_error", "Internal error");
80
39
  },
81
40
  });
82
- /** The `Host` values a listener accepts. */
83
- const hostAllowed = (role, hostHeader) => role === "operator"
84
- ? isAllowedRequestHost(hostHeader, {
85
- port: operatorPort,
86
- host: operator.host,
87
- allowNonLoopback: false,
88
- ...(operator.allowedHosts ? { allowedHosts: operator.allowedHosts } : {}),
89
- })
90
- : isAllowedRequestHost(hostHeader, {
91
- port: listenPort,
92
- host: config.host,
93
- allowNonLoopback: config.allowNonLoopback,
94
- ...(containerListen ? { allowedHosts: containerListen.allowedHosts } : {}),
95
- });
41
+ /** The `Host` values the listener accepts. */
42
+ const hostAllowed = (hostHeader) => isAllowedRequestHost(hostHeader, {
43
+ port: listenPort,
44
+ host: config.host,
45
+ allowNonLoopback: config.allowNonLoopback,
46
+ ...(containerListen ? { allowedHosts: containerListen.allowedHosts } : {}),
47
+ });
96
48
  /** Responses not yet finished; shutdown ends the streams among them. */
97
49
  const inFlight = new Set();
98
- const serve = (role) => {
99
- const next = pipeline(role);
50
+ const serve = () => {
51
+ const next = pipeline();
100
52
  return (req, res) => {
101
53
  const started = Date.now();
102
54
  inFlight.add(res);
103
55
  res.once("close", () => inFlight.delete(res));
104
56
  // D§11: `Host` first, before anything reads the request.
105
- if (hostAllowed(role, headerValue(req, "host")))
57
+ if (hostAllowed(headerValue(req, "host")))
106
58
  return void next(req, res);
107
59
  sendRejected(res, 421, "host_rejected", "Host header is not an allowed loopback or configured address");
108
60
  if (pathnameIsLogged(req.url))
@@ -126,39 +78,15 @@ export function createHost(options) {
126
78
  !isLoopbackHost(config.host)) {
127
79
  throw new HostListenError(`Refusing to bind non-loopback host ${config.host} without allowNonLoopback`, EXIT_NON_LOOPBACK);
128
80
  }
129
- if (operator && !containerListen && !isLoopbackHost(operator.host))
130
- throw new HostListenError(`Refusing to bind the operator listener on non-loopback host ${operator.host}`, EXIT_NON_LOOPBACK);
131
- server = createServer(serve(mainRole));
81
+ server = createServer(serve());
132
82
  await bindListener(server, bindPort, bindHost);
133
83
  const address = server.address();
134
84
  listenPort =
135
85
  typeof address === "object" && address ? address.port : bindPort;
136
86
  url = `http://${bindHost}:${listenPort}`;
137
- adminUrl = url;
138
- if (operator) {
139
- operatorServer = createServer(serve("operator"));
140
- await bindListener(operatorServer, operator.port, operator.host).catch(async (error) => {
141
- // Leave nothing half-open: the main listener closes too.
142
- await new Promise((resolve) => server.close(() => resolve()));
143
- server = undefined;
144
- throw error;
145
- });
146
- const operatorAddress = operatorServer.address();
147
- operatorPort =
148
- typeof operatorAddress === "object" && operatorAddress
149
- ? operatorAddress.port
150
- : operator.port;
151
- adminUrl = `http://${operator.host}:${operatorPort}`;
152
- }
153
- // The Tenant opens once the listener is bound: `/ready` and `/v1/admin/status` answer
154
- // while it opens, and say why when it cannot.
87
+ // The Tenant opens once the listener is bound: `/ready` answers while it opens.
155
88
  await module.start();
156
- logger.info("host_listening", {
157
- hostId: config.hostId,
158
- url,
159
- ...(operator ? { adminUrl } : {}),
160
- pid,
161
- });
89
+ logger.info("host_listening", { hostId: config.hostId, url, pid });
162
90
  }
163
91
  const step = async (name, run) => {
164
92
  try {
@@ -181,7 +109,7 @@ export function createHost(options) {
181
109
  closePromise = (async () => {
182
110
  closing = true;
183
111
  logger.info("host_shutdown", { hostId: config.hostId });
184
- const listeners = [server, operatorServer].filter((listening) => listening !== undefined);
112
+ const listeners = server ? [server] : [];
185
113
  const stopped = Promise.all(listeners.map((listening) => new Promise((resolve) => {
186
114
  listening.close(() => resolve());
187
115
  listening.closeIdleConnections();
@@ -214,7 +142,6 @@ export function createHost(options) {
214
142
  await stopped;
215
143
  }
216
144
  server = undefined;
217
- operatorServer = undefined;
218
145
  await step("beforeTenants", options.shutdown?.beforeTenants);
219
146
  await step("tenants", () => module.close());
220
147
  await step("afterTenants", options.shutdown?.afterTenants);
@@ -228,9 +155,6 @@ export function createHost(options) {
228
155
  get url() {
229
156
  return url;
230
157
  },
231
- get adminUrl() {
232
- return adminUrl;
233
- },
234
158
  };
235
159
  }
236
160
  /**
@@ -1,6 +1,5 @@
1
1
  import type { HostConfigFile } from "./config.js";
2
2
  import type { HostPaths } from "../tenant/paths.js";
3
- import type { TenantPaths } from "../tenant/types.js";
4
3
  /**
5
4
  * Allowlisted ambient keys for Host and Tenant child processes.
6
5
  * `NODE_OPTIONS` is never copied.
@@ -9,5 +8,3 @@ export declare function baselineEnvironment(env: NodeJS.ProcessEnv | Readonly<Re
9
8
  export type HostProcessPathInputs = Pick<HostPaths, "root" | "home" | "tmp">;
10
9
  /** Environment for the Runtime Host process itself (CLI spawn / `runtime run`). */
11
10
  export declare function hostProcessEnvironment(baseline: Readonly<Record<string, string>>, hostConfig: HostConfigFile, paths: HostProcessPathInputs): Record<string, string>;
12
- /** Environment for Tenant-owned children (MCP stdio, sandboxes). */
13
- export declare function tenantChildEnvironment(baseline: Readonly<Record<string, string>>, hostConfig: HostConfigFile, tenantPaths: TenantPaths): Record<string, string>;
@@ -42,12 +42,3 @@ export function hostProcessEnvironment(baseline, hostConfig, paths) {
42
42
  NYLORUN_HOME: paths.root,
43
43
  };
44
44
  }
45
- /** Environment for Tenant-owned children (MCP stdio, sandboxes). */
46
- export function tenantChildEnvironment(baseline, hostConfig, tenantPaths) {
47
- return {
48
- ...baseline,
49
- ...proxyEnv(hostConfig),
50
- HOME: tenantPaths.home,
51
- TMPDIR: tenantPaths.tmp,
52
- };
53
- }
@@ -40,7 +40,10 @@ export interface StartGatesOptions {
40
40
  readonly drainMs?: number;
41
41
  /** How long keyed outcomes are kept, and how many (tests). */
42
42
  readonly inflight?: InflightCallsOptions;
43
- /** How the gate may call Action endpoints (`NYLORUN_ENDPOINT_*`). Default: no limits. */
43
+ /**
44
+ * How the gate may call MCP servers and HTTP tools (`NYLORUN_ENDPOINT_*`).
45
+ * Default: no limits.
46
+ */
44
47
  readonly delivery?: OutboundPolicy;
45
48
  /** How long an MCP connection may sit unused. Default `GATE_MCP_IDLE_MS`. */
46
49
  readonly mcpIdleMs?: number;
@@ -17,6 +17,7 @@ import { createModelCallHandler } from "../gates/handler.js";
17
17
  import { createInflightCalls } from "../gates/inflight.js";
18
18
  import { createMcpHandler, GATE_MCP_IDLE_MS } from "../gates/mcp-handler.js";
19
19
  import { createToolCalls } from "../gates/tool-calls.js";
20
+ import { gateHttpTools } from "../gates/http-tool.js";
20
21
  import { existsSync } from "node:fs";
21
22
  import { tenantPaths } from "../tenant/paths.js";
22
23
  import { staleRun, verifyRunToken } from "../tenant/run-token.js";
@@ -52,10 +53,10 @@ export async function startGates(options) {
52
53
  logger,
53
54
  idleMs: mcpIdleMs,
54
55
  ...(options.openMcp ? { open: options.openMcp } : {}),
56
+ ...(options.delivery ? { delivery: options.delivery } : {}),
55
57
  });
56
58
  const toolCalls = createToolCalls({
57
59
  vaults,
58
- mcp,
59
60
  logger,
60
61
  ...(options.inflight ? { inflight: options.inflight } : {}),
61
62
  });
@@ -98,8 +99,8 @@ export async function startGates(options) {
98
99
  },
99
100
  ...(options.maxBodyBytes !== undefined ? { maxBodyBytes: options.maxBodyBytes } : {}),
100
101
  mcp,
102
+ http: gateHttpTools(vaults, options.delivery ?? {}, logger),
101
103
  toolCalls,
102
- delivery: options.delivery ?? {},
103
104
  logger,
104
105
  ...(options.keys ? { keys: async () => (await vaults.open()).keys() } : {}),
105
106
  });
@@ -48,11 +48,10 @@ export declare function pathnameIsLogged(rawUrl: string | undefined): boolean;
48
48
  * (a credential) after `/v1/artifact-links/`.
49
49
  */
50
50
  export declare function redactRoutePath(pathname: string): string;
51
- /** Constant-time comparison of SHA-256 digests of the presented and stored admin keys. */
51
+ /** Constant-time comparison of SHA-256 digests of a presented and a stored secret. */
52
52
  export declare function adminKeyMatches(presented: string | undefined, adminKey: string): boolean;
53
53
  /** A request header's first value. */
54
54
  export declare function headerValue(incoming: IncomingMessage, name: string): string | undefined;
55
- export declare function readBearer(authorization: string | undefined): string | undefined;
56
55
  /** A JSON request body, capped at `limit` bytes (413); `undefined` when there is none. */
57
56
  export declare function readJsonBody(request: Request, limit?: number): Promise<unknown>;
58
57
  export declare function isLoopbackHost(host: string): boolean;
package/dist/host/http.js CHANGED
@@ -149,7 +149,7 @@ export function redactRoutePath(pathname) {
149
149
  return "/v1/artifact-links/:token";
150
150
  return pathname;
151
151
  }
152
- /** Constant-time comparison of SHA-256 digests of the presented and stored admin keys. */
152
+ /** Constant-time comparison of SHA-256 digests of a presented and a stored secret. */
153
153
  export function adminKeyMatches(presented, adminKey) {
154
154
  if (presented === undefined)
155
155
  return false;
@@ -164,12 +164,6 @@ export function headerValue(incoming, name) {
164
164
  return raw[0];
165
165
  return raw;
166
166
  }
167
- export function readBearer(authorization) {
168
- if (!authorization)
169
- return undefined;
170
- const match = /^Bearer\s+(\S+)/i.exec(authorization.trim());
171
- return match?.[1];
172
- }
173
167
  /** A JSON request body, capped at `limit` bytes (413); `undefined` when there is none. */
174
168
  export async function readJsonBody(request, limit = 1024 * 1024) {
175
169
  if (request.body === null)
@@ -1,6 +1,6 @@
1
1
  export type { HostConfigFile, HostCredentialsFile, } from "./config.js";
2
- export { baselineEnvironment, hostProcessEnvironment, tenantChildEnvironment, } from "./environment.js";
3
- export { createHost, adminKeyMatches, type CreateHostOptions, type HostServer } from "./create-host.js";
2
+ export { baselineEnvironment, hostProcessEnvironment, } from "./environment.js";
3
+ export { createHost, type CreateHostOptions, type HostServer } from "./create-host.js";
4
4
  export { createHostLogger } from "./logger.js";
5
5
  export { configForFactory } from "./config-for.js";
6
6
  export { createHostExecution, type CreateHostExecutionOptions, type HostExecution, } from "./execution.js";
@@ -1,5 +1,5 @@
1
- export { baselineEnvironment, hostProcessEnvironment, tenantChildEnvironment, } from "./environment.js";
2
- export { createHost, adminKeyMatches } from "./create-host.js";
1
+ export { baselineEnvironment, hostProcessEnvironment, } from "./environment.js";
2
+ export { createHost } from "./create-host.js";
3
3
  export { createHostLogger } from "./logger.js";
4
4
  export { configForFactory } from "./config-for.js";
5
5
  export { createHostExecution, } from "./execution.js";
package/dist/host/main.js CHANGED
@@ -83,6 +83,20 @@ function loadIssuers(path) {
83
83
  }
84
84
  return createTrustedIssuers(parseIdentityFile(text, path));
85
85
  }
86
+ /** The bootstrap management key: 64 hex characters, surrounding whitespace ignored. */
87
+ function loadManagementKey(path) {
88
+ let text;
89
+ try {
90
+ text = readFileSync(path, "utf8");
91
+ }
92
+ catch (error) {
93
+ throw new Error(`NYLORUN_MANAGEMENT_KEY_FILE names ${path}, which cannot be read: ${error instanceof Error ? error.message : String(error)}`);
94
+ }
95
+ const key = text.trim();
96
+ if (!/^[0-9a-f]{64}$/.test(key))
97
+ throw new Error(`NYLORUN_MANAGEMENT_KEY_FILE (${path}) must hold one key of 64 lowercase hex characters, such as \`openssl rand -hex 32\``);
98
+ return key;
99
+ }
86
100
  function resolveHostRoot() {
87
101
  const fromEnv = process.env.NYLORUN_HOME;
88
102
  const root = fromEnv && fromEnv.length > 0 ? fromEnv : resolve(homedir(), ".nylorun");
@@ -122,11 +136,6 @@ async function ensureBucket(store, bucket, logger) {
122
136
  }
123
137
  }
124
138
  }
125
- /** The slot's lag in WAL bytes, when the source can tell. */
126
- async function lagOf(source) {
127
- const lagBytes = await source.lag?.().catch(() => undefined);
128
- return lagBytes === undefined ? {} : { lagBytes };
129
- }
130
139
  /**
131
140
  * The gateway's services: the Model Gate's listener (gates, keys) and egress-gate's (egress), over
132
141
  * the Postgres pool and the Host's tenant directory. Writes nothing to the Host root (the local
@@ -242,14 +251,11 @@ export async function main() {
242
251
  const database = infra.database;
243
252
  if (!database)
244
253
  throw new Error("NYLORUN_DATABASE_URL did not yield a Postgres pool");
245
- const baseline = baselineEnvironment(process.env);
246
254
  // A Tenant that should answer with the fixture model carries the Tenant
247
255
  // setting (`tenant/model-setting.ts`); the Host has no fixture mode.
248
256
  const configFor = configForFactory({
249
257
  hostRoot,
250
- hostConfig: config,
251
258
  logger,
252
- baseline,
253
259
  ...(stack.delivery ? { delivery: stack.delivery } : {}),
254
260
  ...(issuers ? { issuers } : {}),
255
261
  ...(stack.publicUrl ? { publicUrl: stack.publicUrl } : {}),
@@ -270,7 +276,6 @@ export async function main() {
270
276
  // running core runs one, once the Tenant is open (the relay reads its id); the replication
271
277
  // slot lets exactly one be active. Without S2, the Tenant relays its own commits.
272
278
  let relay;
273
- let relayLag;
274
279
  if (streams && stack.services.has("core"))
275
280
  await assertLogicalReplication(database);
276
281
  const startRelay = (tenantId) => {
@@ -287,14 +292,12 @@ export async function main() {
287
292
  streams,
288
293
  log: (message, fields) => logger.info(message, fields),
289
294
  });
290
- const status = relay.status;
291
- relayLag = async () => ({ ...status(), ...(await lagOf(source)) });
292
295
  relay.start();
293
296
  };
294
- // With the gates service the Tenant's vault-backed model calls, remote MCP calls and Action
295
- // deliveries cross it, and this process never reads a model or MCP credential.
296
- // Their credentials (F5): each advance's run token for its session's model and MCP calls,
297
- // core's credential (NYLORUN_GATES_TOKEN) for deliveries and MCP requests outside a run.
297
+ // With the gates service the Tenant's vault-backed model calls, remote MCP calls and HTTP
298
+ // tool calls cross it, and this process never reads a model or MCP credential.
299
+ // Their credentials (F5): each advance's run token for its session's model and tool calls,
300
+ // core's credential (NYLORUN_GATES_TOKEN) for MCP requests outside a run.
298
301
  const runGrants = stack.modelGate ? createRunGrants() : undefined;
299
302
  const modelGate = stack.modelGate && runGrants
300
303
  ? httpModelGate({ url: stack.modelGate.url, runTokens: runGrants })
@@ -326,7 +329,12 @@ export async function main() {
326
329
  create: {
327
330
  ...(tenantSettings.id ? { tenantId: tenantSettings.id } : {}),
328
331
  name: tenantSettings.name,
329
- principals: hostPrincipals({ adminKey: credentials.adminKey }),
332
+ principals: hostPrincipals({
333
+ adminKey: credentials.adminKey,
334
+ ...(stack.tenant?.managementKeyFile
335
+ ? { bootstrapKey: loadManagementKey(stack.tenant.managementKeyFile) }
336
+ : {}),
337
+ }),
330
338
  },
331
339
  configFor,
332
340
  logger,
@@ -345,31 +353,16 @@ export async function main() {
345
353
  }),
346
354
  logger,
347
355
  onOpen: (handle) => startRelay(handle.envelope.id),
348
- relayStatus: async () => {
349
- if (!relayLag)
350
- throw new Error("no relay");
351
- return relayLag();
352
- },
353
356
  });
354
357
  const options = {
355
358
  hostRoot,
356
359
  module,
357
360
  config,
358
- credentials,
359
361
  logger,
360
362
  coreVersion: coreVersion(),
361
363
  ...(stack.listen ? { listen: stack.listen } : {}),
362
- ...(stack.publicUrl ? { publicUrl: stack.publicUrl } : {}),
363
- // The Admin API on its own listener: from the container environment, or from host.json
364
- // (loopback, on the same host) when the Host runs outside a container.
365
- ...(stack.operator
366
- ? { operator: stack.operator }
367
- : !stack.listen && typeof config.adminPort === "number"
368
- ? { operator: { host: config.host, port: config.adminPort } }
369
- : {}),
370
364
  ...(infra.readiness ? { readiness: infra.readiness } : {}),
371
- // SIGTERM and POST /v1/admin/host/shutdown both close the Host this way:
372
- // stop the Worker and the relay, close the Tenant, then end the infrastructure clients.
365
+ // SIGTERM closes the Host this way: stop the Worker and the relay, close the Tenant, then end the infrastructure clients.
373
366
  shutdown: {
374
367
  beforeTenants: async () => {
375
368
  await hostExecution.stop();
@@ -392,8 +385,8 @@ export async function main() {
392
385
  // listener starts and opens the Tenant (migrating its database, creating it on
393
386
  // first start); opening arms its sweep, which recovers wakes lost with
394
387
  // Restate's state (§14.8), and starts the stream relay. A Tenant that cannot
395
- // be opened leaves the Host listening but not ready, with the cause in
396
- // `/v1/admin/status`.
388
+ // be opened leaves the Host listening but not ready; `nylorun-operate status` names the
389
+ // cause.
397
390
  // The Harness API listener (NYLORUN_HARNESS=remote): only harnesses connect here, with the
398
391
  // harness credential, and only while the Tenant is open.
399
392
  let harnessListener;
@@ -0,0 +1,29 @@
1
+ import type { TenantCause } from "@nylorun/core/contracts";
2
+ export declare const EXIT_REFUSED = 1;
3
+ export declare const EXIT_TENANT = 2;
4
+ export declare const EXIT_USAGE = 64;
5
+ export interface OperateIo {
6
+ env: Readonly<Record<string, string | undefined>>;
7
+ out(line: string): void;
8
+ err(line: string): void;
9
+ /** For the Host's `/ready`. Default: the global `fetch`. */
10
+ fetch?: typeof fetch;
11
+ }
12
+ /** `nylorun-operate status --json`. */
13
+ export interface OperateStatus {
14
+ version: string;
15
+ protocol: {
16
+ min: number;
17
+ max: number;
18
+ features: string[];
19
+ };
20
+ tenant: {
21
+ id: string | null;
22
+ name: string | null;
23
+ state: "open" | "unavailable";
24
+ cause?: TenantCause["code"];
25
+ message?: string;
26
+ };
27
+ }
28
+ /** Runs one command; resolves to the exit code. */
29
+ export declare function runOperate(argv: readonly string[], io: OperateIo): Promise<number>;