@nylorun/runtime 0.16.0-beta → 0.19.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.
Files changed (256) hide show
  1. package/CHANGELOG.md +207 -0
  2. package/README.md +105 -63
  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/endpoint.js +1 -2
  9. package/dist/api/ag-ui/routes.d.ts +1 -1
  10. package/dist/api/ag-ui/routes.js +11 -13
  11. package/dist/api/ag-ui/translate.d.ts +1 -3
  12. package/dist/api/ag-ui/translate.js +2 -17
  13. package/dist/api/components.d.ts +4783 -2588
  14. package/dist/api/components.js +19 -21
  15. package/dist/api/gate/routes.d.ts +7 -8
  16. package/dist/api/gate/routes.js +58 -52
  17. package/dist/api/http/app.d.ts +6 -0
  18. package/dist/api/http/app.js +20 -14
  19. package/dist/api/http/define.d.ts +22 -13
  20. package/dist/api/http/define.js +43 -39
  21. package/dist/api/http/routes/access.d.ts +3 -19
  22. package/dist/api/http/routes/access.js +25 -184
  23. package/dist/api/http/routes/artifacts.js +3 -7
  24. package/dist/api/http/routes/files.d.ts +8 -0
  25. package/dist/api/http/routes/files.js +77 -0
  26. package/dist/api/http/routes/keys.d.ts +10 -0
  27. package/dist/api/http/routes/keys.js +64 -0
  28. package/dist/api/http/routes/me.d.ts +13 -0
  29. package/dist/api/http/routes/me.js +48 -0
  30. package/dist/api/http/routes/reads.d.ts +66 -0
  31. package/dist/api/http/routes/reads.js +127 -0
  32. package/dist/api/http/routes/sandboxes.d.ts +2 -2
  33. package/dist/api/http/routes/sandboxes.js +8 -7
  34. package/dist/api/http/routes/sessions.js +31 -20
  35. package/dist/api/http/routes/tenant.js +15 -18
  36. package/dist/api/http/routes/vaults.d.ts +13 -3
  37. package/dist/api/http/routes/vaults.js +136 -64
  38. package/dist/api/openapi.d.ts +10 -11
  39. package/dist/api/openapi.js +292 -68
  40. package/dist/artifacts/links.js +6 -6
  41. package/dist/artifacts/service.js +1 -1
  42. package/dist/core/flow-host.d.ts +44 -23
  43. package/dist/core/flow-host.js +88 -106
  44. package/dist/core/provider.d.ts +4 -1
  45. package/dist/core/provider.js +6 -1
  46. package/dist/core/sandbox-routes.d.ts +0 -9
  47. package/dist/core/sandbox-routes.js +2 -21
  48. package/dist/execution/memory.d.ts +4 -5
  49. package/dist/execution/memory.js +11 -25
  50. package/dist/execution/types.d.ts +3 -33
  51. package/dist/execution/types.js +2 -12
  52. package/dist/gates/http-tool.d.ts +86 -0
  53. package/dist/gates/http-tool.js +197 -0
  54. package/dist/gates/mcp-handler.d.ts +3 -0
  55. package/dist/gates/mcp-handler.js +6 -4
  56. package/dist/gates/meter.d.ts +2 -2
  57. package/dist/gates/meter.js +6 -0
  58. package/dist/gates/tenant-vaults.d.ts +19 -14
  59. package/dist/gates/tenant-vaults.js +17 -12
  60. package/dist/gates/tool-calls.d.ts +7 -15
  61. package/dist/gates/tool-calls.js +4 -4
  62. package/dist/gates/tool-client.d.ts +1 -1
  63. package/dist/gates/tool-client.js +23 -47
  64. package/dist/gates/tool-contract.d.ts +27 -39
  65. package/dist/gates/tool-contract.js +30 -21
  66. package/dist/gates/tool-gate.d.ts +25 -24
  67. package/dist/gates/tool-gate.js +11 -7
  68. package/dist/harness/calls.d.ts +10 -4
  69. package/dist/harness/calls.js +15 -6
  70. package/dist/harness/executors.d.ts +1 -1
  71. package/dist/harness/executors.js +17 -12
  72. package/dist/harness/main.d.ts +2 -2
  73. package/dist/harness/main.js +5 -13
  74. package/dist/harness/service.d.ts +4 -5
  75. package/dist/harness/service.js +9 -2
  76. package/dist/harness-api/in-process.js +2 -2
  77. package/dist/harness-api/record.d.ts +6 -3
  78. package/dist/harness-api/record.js +65 -72
  79. package/dist/harness-api/server.d.ts +1 -9
  80. package/dist/harness-api/server.js +19 -17
  81. package/dist/harness-api/start.js +1 -5
  82. package/dist/harness-api/workspace.js +2 -3
  83. package/dist/harness-api/ws-server.js +1 -1
  84. package/dist/host/app.d.ts +2 -10
  85. package/dist/host/app.js +24 -44
  86. package/dist/host/config-for.d.ts +5 -5
  87. package/dist/host/config-for.js +2 -3
  88. package/dist/host/config.d.ts +0 -11
  89. package/dist/host/create-host.d.ts +3 -40
  90. package/dist/host/create-host.js +18 -91
  91. package/dist/host/environment.d.ts +0 -3
  92. package/dist/host/environment.js +0 -9
  93. package/dist/host/gates.d.ts +4 -1
  94. package/dist/host/gates.js +9 -3
  95. package/dist/host/http.d.ts +1 -2
  96. package/dist/host/http.js +1 -7
  97. package/dist/host/index.d.ts +2 -2
  98. package/dist/host/index.js +2 -2
  99. package/dist/host/main.js +55 -40
  100. package/dist/host/operate.d.ts +29 -0
  101. package/dist/host/operate.js +223 -0
  102. package/dist/host/stack-config.d.ts +23 -20
  103. package/dist/host/stack-config.js +28 -44
  104. package/dist/index.d.ts +4 -1
  105. package/dist/index.js +3 -1
  106. package/dist/infra/workers.d.ts +1 -1
  107. package/dist/keys/client.js +9 -1
  108. package/dist/keys/keys.d.ts +12 -2
  109. package/dist/keys/keys.js +10 -2
  110. package/dist/management-openapi.json +6491 -0
  111. package/dist/mcp/connect.d.ts +8 -4
  112. package/dist/mcp/connect.js +8 -27
  113. package/dist/mcp/pool.d.ts +6 -9
  114. package/dist/mcp/pool.js +8 -18
  115. package/dist/mcp/snapshot.d.ts +8 -2
  116. package/dist/mcp/snapshot.js +29 -2
  117. package/dist/media.d.ts +1 -1
  118. package/dist/media.js +1 -1
  119. package/dist/model/pi-model.js +3 -0
  120. package/dist/openapi.json +6228 -15615
  121. package/dist/reads/access.d.ts +4 -0
  122. package/dist/reads/access.js +12 -0
  123. package/dist/reads/cursor.d.ts +11 -0
  124. package/dist/reads/cursor.js +80 -0
  125. package/dist/reads/types.d.ts +25 -0
  126. package/dist/reads/types.js +1 -0
  127. package/dist/sandbox/manager.d.ts +16 -0
  128. package/dist/sandbox/manager.js +40 -0
  129. package/dist/sandbox/session-sandbox.js +13 -3
  130. package/dist/sandbox/skills.d.ts +13 -0
  131. package/dist/sandbox/skills.js +46 -0
  132. package/dist/sandbox/types.d.ts +2 -2
  133. package/dist/store/ownership.d.ts +9 -0
  134. package/dist/store/ownership.js +13 -0
  135. package/dist/store/postgres/connect.d.ts +2 -0
  136. package/dist/store/postgres/connect.js +29 -0
  137. package/dist/store/postgres/drizzle/0008_installation_vaults.sql +2 -0
  138. package/dist/store/postgres/drizzle/0009_oauth_pending.sql +19 -0
  139. package/dist/store/postgres/drizzle/0010_oss_auth_removals.sql +3 -0
  140. package/dist/store/postgres/drizzle/0011_key_roles.sql +3 -0
  141. package/dist/store/postgres/drizzle/0012_session_reads.sql +8 -0
  142. package/dist/store/postgres/drizzle/0013_definition_files.sql +16 -0
  143. package/dist/store/postgres/drizzle/0014_action_endpoints_removed.sql +2 -0
  144. package/dist/store/postgres/drizzle/meta/_journal.json +49 -0
  145. package/dist/store/postgres/reads.d.ts +3 -0
  146. package/dist/store/postgres/reads.js +272 -0
  147. package/dist/store/postgres/schema.d.ts +423 -594
  148. package/dist/store/postgres/schema.js +84 -101
  149. package/dist/store/postgres/store.js +96 -248
  150. package/dist/store/postgres/tenant.d.ts +34 -2
  151. package/dist/store/postgres/tenant.js +16 -32
  152. package/dist/store/types.d.ts +44 -119
  153. package/dist/store/types.js +0 -1
  154. package/dist/streams/control.d.ts +0 -7
  155. package/dist/streams/control.js +0 -13
  156. package/dist/streams/types.d.ts +2 -20
  157. package/dist/streams/types.js +1 -1
  158. package/dist/tenant/advance.d.ts +3 -3
  159. package/dist/tenant/advance.js +10 -7
  160. package/dist/tenant/auth.d.ts +24 -15
  161. package/dist/tenant/auth.js +44 -55
  162. package/dist/tenant/cause.d.ts +3 -3
  163. package/dist/tenant/cause.js +1 -1
  164. package/dist/tenant/commands.d.ts +2 -18
  165. package/dist/tenant/commands.js +38 -179
  166. package/dist/tenant/context.d.ts +33 -45
  167. package/dist/tenant/definition-files.d.ts +24 -0
  168. package/dist/tenant/definition-files.js +103 -0
  169. package/dist/tenant/effects.d.ts +20 -18
  170. package/dist/tenant/effects.js +59 -133
  171. package/dist/tenant/ephemeral.d.ts +21 -8
  172. package/dist/tenant/ephemeral.js +11 -12
  173. package/dist/tenant/host-token.js +5 -5
  174. package/dist/tenant/identity-file.d.ts +75 -0
  175. package/dist/tenant/identity-file.js +202 -0
  176. package/dist/tenant/issuers.d.ts +48 -0
  177. package/dist/tenant/issuers.js +311 -0
  178. package/dist/tenant/jwt.js +3 -3
  179. package/dist/tenant/module.d.ts +1 -4
  180. package/dist/tenant/module.js +3 -12
  181. package/dist/tenant/operator-keys.d.ts +44 -0
  182. package/dist/tenant/operator-keys.js +87 -0
  183. package/dist/tenant/outbound.d.ts +40 -1
  184. package/dist/tenant/outbound.js +167 -18
  185. package/dist/tenant/paths.d.ts +1 -1
  186. package/dist/tenant/paths.js +1 -3
  187. package/dist/tenant/principals.d.ts +13 -13
  188. package/dist/tenant/principals.js +35 -36
  189. package/dist/tenant/run-token.js +6 -6
  190. package/dist/tenant/runtime.d.ts +5 -3
  191. package/dist/tenant/runtime.js +29 -15
  192. package/dist/tenant/sandboxes.d.ts +9 -6
  193. package/dist/tenant/sandboxes.js +16 -5
  194. package/dist/tenant/scheduler.d.ts +1 -5
  195. package/dist/tenant/scheduler.js +2 -5
  196. package/dist/tenant/session-streams.d.ts +8 -11
  197. package/dist/tenant/session-streams.js +48 -32
  198. package/dist/tenant/session.d.ts +8 -41
  199. package/dist/tenant/session.js +0 -37
  200. package/dist/tenant/sessions.d.ts +2 -2
  201. package/dist/tenant/sessions.js +17 -9
  202. package/dist/tenant/signing-keys.d.ts +3 -2
  203. package/dist/tenant/signing-keys.js +5 -5
  204. package/dist/tenant/skill-tool.d.ts +20 -0
  205. package/dist/tenant/skill-tool.js +72 -0
  206. package/dist/tenant/slim.d.ts +1 -1
  207. package/dist/tenant/slim.js +1 -1
  208. package/dist/tenant/status.js +7 -12
  209. package/dist/tenant/store-pg.d.ts +15 -0
  210. package/dist/tenant/store-pg.js +7 -3
  211. package/dist/tenant/streams.d.ts +3 -11
  212. package/dist/tenant/streams.js +2 -29
  213. package/dist/tenant/sweep.js +0 -5
  214. package/dist/tenant/transcript.d.ts +2 -2
  215. package/dist/tenant/transcript.js +2 -2
  216. package/dist/tenant/types.d.ts +33 -19
  217. package/dist/tenant/worker.d.ts +1 -3
  218. package/dist/tenant/worker.js +3 -8
  219. package/dist/vault/crypto.d.ts +7 -0
  220. package/dist/vault/crypto.js +16 -0
  221. package/dist/vault/oauth.d.ts +55 -0
  222. package/dist/vault/oauth.js +186 -0
  223. package/dist/vault/service.d.ts +54 -7
  224. package/dist/vault/service.js +261 -17
  225. package/dist/vault/sources.d.ts +60 -0
  226. package/dist/vault/sources.js +196 -0
  227. package/dist/version.d.ts +1 -1
  228. package/dist/version.js +1 -1
  229. package/package.json +5 -4
  230. package/dist/admin-openapi.json +0 -604
  231. package/dist/api/http/routes/actions.d.ts +0 -8
  232. package/dist/api/http/routes/actions.js +0 -76
  233. package/dist/api/http/routes/endpoints.d.ts +0 -7
  234. package/dist/api/http/routes/endpoints.js +0 -100
  235. package/dist/host/admin-api.d.ts +0 -23
  236. package/dist/host/admin-api.js +0 -74
  237. package/dist/host/cors.d.ts +0 -27
  238. package/dist/host/cors.js +0 -64
  239. package/dist/mcp/stdio.d.ts +0 -17
  240. package/dist/mcp/stdio.js +0 -82
  241. package/dist/tenant/access-policy.d.ts +0 -30
  242. package/dist/tenant/access-policy.js +0 -52
  243. package/dist/tenant/actions.d.ts +0 -14
  244. package/dist/tenant/actions.js +0 -29
  245. package/dist/tenant/browser.d.ts +0 -15
  246. package/dist/tenant/browser.js +0 -39
  247. package/dist/tenant/delivery-token.d.ts +0 -41
  248. package/dist/tenant/delivery-token.js +0 -104
  249. package/dist/tenant/delivery.d.ts +0 -62
  250. package/dist/tenant/delivery.js +0 -481
  251. package/dist/tenant/endpoints.d.ts +0 -67
  252. package/dist/tenant/endpoints.js +0 -90
  253. package/dist/tenant/subject-limits.d.ts +0 -16
  254. package/dist/tenant/subject-limits.js +0 -45
  255. package/dist/tenant/tokens.d.ts +0 -14
  256. package/dist/tenant/tokens.js +0 -187
package/dist/host/app.js CHANGED
@@ -1,11 +1,10 @@
1
1
  import { Hono } from "hono";
2
2
  import { RESPONSE_ALREADY_SENT } from "@hono/node-server/utils/response";
3
- import { checkCompatibility, HOST_PROTOCOL, PROTOCOL_HEADER, PUBLISHABLE_KEY_HEADER, TENANT_HEADER, tenantOfPublishableKey, } from "@nylorun/core/compatibility";
3
+ import { checkCompatibility, HOST_PROTOCOL, PROTOCOL_HEADER, TENANT_HEADER, } from "@nylorun/core/compatibility";
4
4
  import { RUNTIME_VERSION } from "../version.js";
5
5
  import { findTenantRoute } from "../api/http/app.js";
6
- import { tenantDocument } from "../api/openapi.js";
7
- import { answerPreflight } from "./cors.js";
8
- import { adminKeyMatches, headerValue, isJsonContentType, jsonResponse, opaqueNotFoundResponse, protocolRejectedResponse, readBearer, pathnameIsLogged, redactRoutePath, rejectedResponse, requestHasBody, } from "./http.js";
6
+ import { managementDocument, runtimeDocument } from "../api/openapi.js";
7
+ import { headerValue, isJsonContentType, jsonResponse, opaqueNotFoundResponse, protocolRejectedResponse, pathnameIsLogged, redactRoutePath, rejectedResponse, requestHasBody, } from "./http.js";
9
8
  export function createHostApp(options) {
10
9
  const { module, logger } = options;
11
10
  const app = new Hono();
@@ -27,20 +26,16 @@ export function createHostApp(options) {
27
26
  }
28
27
  });
29
28
  app.use(async (c, next) => {
30
- const { incoming, outgoing, role } = c.env;
29
+ const { incoming } = c.env;
31
30
  if (headerValue(incoming, "origin") !== undefined) {
32
- const route = pathnameOf(incoming).split("/").filter(Boolean);
33
- // Only Tenant routes, and only when the operator allows browsers; the Tenant then
34
- // checks the publishable key and its origins before adding any CORS header.
35
- const tenantRoute = route[0] === "v1" && route[1] !== "admin";
36
- // The operator listener never serves browsers.
37
- if (!options.browserAccess || role === "operator" || !tenantRoute)
31
+ // Only Tenant routes; the Tenant then accepts only a trusted issuer's token from a
32
+ // browser.
33
+ if (pathnameOf(incoming).split("/").filter(Boolean)[0] !== "v1")
38
34
  return rejectedResponse(403, "origin_rejected", "Browser Origin headers are not accepted");
39
- if (incoming.method === "OPTIONS") {
40
- c.set("status", answerPreflight(incoming, outgoing, route, browserRoute));
41
- return RESPONSE_ALREADY_SENT;
42
- }
43
35
  }
36
+ // CORS is the operator's proxy's: an OPTIONS request gets no CORS header.
37
+ if (incoming.method === "OPTIONS")
38
+ return new Response(null, { status: 204, headers: { allow: ALLOWED_METHODS } });
44
39
  await next();
45
40
  });
46
41
  app.use(async (c, next) => {
@@ -53,7 +48,7 @@ export function createHostApp(options) {
53
48
  await next();
54
49
  });
55
50
  app.all("*", async (c) => {
56
- const { incoming, outgoing, role } = c.env;
51
+ const { incoming, outgoing } = c.env;
57
52
  const url = new URL(incoming.url ?? "/", "http://runtime.local");
58
53
  const pathname = url.pathname;
59
54
  if (pathname === "/health")
@@ -75,32 +70,20 @@ export function createHostApp(options) {
75
70
  const tenant = module.ready;
76
71
  const infra = await options.readiness?.();
77
72
  const ready = listener && tenant && !options.closing() && (infra?.ok ?? true);
73
+ const harness = module.harnessStatus();
78
74
  return jsonResponse(ready ? 200 : 503, {
79
75
  status: ready ? "ready" : "not_ready",
80
76
  service: "nylorun-runtime",
81
77
  checks: { listener, tenant, ...infra?.checks },
78
+ ...(harness ? { harness: { mode: harness.mode, connected: harness.connected } } : {}),
82
79
  });
83
80
  }
84
- // This API's own description: public, as the npm package that ships it.
85
- if (pathname === "/openapi.json" && incoming.method === "GET")
86
- return jsonResponse(200, tenantDocument(), { "cache-control": "no-cache" });
87
- const segments = pathname.split("/").filter(Boolean);
88
- if (segments[0] === "v1" && segments[1] === "admin") {
89
- // A public listener has no admin routes: the same 404 as a wrong admin key.
90
- if (role === "public")
91
- return opaqueNotFoundResponse();
92
- if (!protocolAccepted(headerValue(incoming, PROTOCOL_HEADER)))
93
- return protocolRejectedResponse();
94
- const token = readBearer(headerValue(incoming, "authorization"));
95
- if (!adminKeyMatches(token, options.adminKey))
96
- return opaqueNotFoundResponse();
97
- return await options.admin(c.req.raw, { incoming, outgoing });
98
- }
99
- // Tenant routes: key pattern → protocol → the Tenant → selection → the Tenant's routes.
100
- const keyHeader = headerValue(incoming, PUBLISHABLE_KEY_HEADER);
101
- const keyTenant = keyHeader === undefined ? undefined : tenantOfPublishableKey(keyHeader);
102
- if (keyHeader !== undefined && keyTenant === undefined)
103
- return rejectedResponse(400, "invalid_request", `${PUBLISHABLE_KEY_HEADER} header is malformed`);
81
+ // The two APIs' descriptions: public, as the npm package that ships them.
82
+ if ((pathname === "/openapi.json" || pathname === "/openapi/runtime.json") && incoming.method === "GET")
83
+ return jsonResponse(200, runtimeDocument(), { "cache-control": "no-cache" });
84
+ if (pathname === "/openapi/management.json" && incoming.method === "GET")
85
+ return jsonResponse(200, managementDocument(), { "cache-control": "no-cache" });
86
+ // Tenant routes: protocol → the Tenant → selection → the Tenant's routes.
104
87
  const tenantHeader = headerValue(incoming, TENANT_HEADER);
105
88
  const named = tenantHeader === undefined || tenantHeader.trim() === "" ? undefined : tenantHeader.trim();
106
89
  const protocol = headerValue(incoming, PROTOCOL_HEADER);
@@ -119,9 +102,8 @@ export function createHostApp(options) {
119
102
  }
120
103
  const { handle } = resolution;
121
104
  const tenantId = handle.envelope.id;
122
- // A protocol 4 client names the Tenant; a publishable key carries one. Either naming
123
- // another Tenant (or a malformed header) reached the wrong installation.
124
- if ((named !== undefined && named !== tenantId) || (keyTenant !== undefined && keyTenant !== tenantId))
105
+ // A protocol 4 client names the Tenant: naming another reached the wrong installation.
106
+ if (named !== undefined && named !== tenantId)
125
107
  return opaqueNotFoundResponse();
126
108
  c.set("tenantId", tenantId);
127
109
  return await handle.fetch(c.req.raw, { incoming, outgoing });
@@ -154,10 +136,8 @@ export function createHostApp(options) {
154
136
  });
155
137
  return app;
156
138
  }
157
- /** A route a browser page may call, decided from the route alone (`api/http/define.ts`). */
158
- function browserRoute(method, segments) {
159
- return findTenantRoute(method, segments)?.browser === true;
160
- }
139
+ /** The methods the Runtime serves, for an `OPTIONS` answer's `Allow`. */
140
+ const ALLOWED_METHODS = "GET, POST, PUT, DELETE, OPTIONS";
161
141
  /** The Tenant route a request names, if any; a malformed path names none. */
162
142
  function tenantRouteOf(incoming) {
163
143
  const segments = [];
@@ -169,7 +149,7 @@ function tenantRouteOf(incoming) {
169
149
  return undefined;
170
150
  }
171
151
  }
172
- if (segments[0] !== "v1" || segments[1] === "admin")
152
+ if (segments[0] !== "v1")
173
153
  return undefined;
174
154
  return findTenantRoute(incoming.method ?? "GET", segments);
175
155
  }
@@ -1,4 +1,3 @@
1
- import type { HostConfigFile } from "./config.js";
2
1
  import type { Logger, TenantConfig } from "../tenant/types.js";
3
2
  /**
4
3
  * Builds the TenantConfig of the Host's Tenant for `openTenantRuntime`.
@@ -7,13 +6,14 @@ import type { Logger, TenantConfig } from "../tenant/types.js";
7
6
  */
8
7
  export declare function configForFactory(options: {
9
8
  hostRoot: string;
10
- hostConfig: HostConfigFile;
11
9
  logger: Logger;
12
- /** Allowlisted baseline from `baselineEnvironment` (built in `host/main.ts`). */
13
- baseline: Readonly<Record<string, string>>;
14
10
  mode?: TenantConfig["mode"];
15
11
  /** Override model; default vault. Fixture/scripted require ephemeral/test mode. */
16
12
  model?: TenantConfig["model"];
17
- /** How Tenants may call Action endpoints (`StackConfig.delivery`). */
13
+ /** How Tenants may call MCP servers and HTTP tools (`StackConfig.delivery`). */
18
14
  delivery?: TenantConfig["delivery"];
15
+ /** The trusted issuers of the identity file (`NYLORUN_IDENTITY_FILE`). */
16
+ issuers?: TenantConfig["issuers"];
17
+ /** `NYLORUN_PUBLIC_URL`: the base of the MCP OAuth callback (F9 C2). */
18
+ publicUrl?: string;
19
19
  }): (id: string) => TenantConfig;
@@ -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";
@@ -17,7 +15,8 @@ export function configForFactory(options) {
17
15
  sandbox: { backend: sandboxBackend },
18
16
  model: options.model ?? { kind: "vault" },
19
17
  ...(options.delivery ? { delivery: options.delivery } : {}),
20
- childEnv: tenantChildEnvironment(baseline, options.hostConfig, tenant),
18
+ ...(options.issuers ? { issuers: options.issuers } : {}),
19
+ ...(options.publicUrl ? { publicUrl: options.publicUrl } : {}),
21
20
  logger: {
22
21
  info: (message, fields) => options.logger.info(message, { tenantId: id, ...fields }),
23
22
  warn: (message, fields) => options.logger.warn(message, { tenantId: id, ...fields }),
@@ -4,17 +4,6 @@ export interface HostConfigFile {
4
4
  host: string;
5
5
  port: number;
6
6
  allowNonLoopback?: boolean;
7
- /**
8
- * Whether browser requests (an `Origin` with a publishable key) may reach Tenant routes.
9
- * Default off for a Host started from host.json; container mode reads
10
- * `NYLORUN_BROWSER_ACCESS` instead.
11
- */
12
- browserAccess?: boolean;
13
- /**
14
- * The operator listener's port: the Admin API moves there (on `host`, loopback only), and
15
- * `port` serves the Tenant API alone. Absent: one port serves both.
16
- */
17
- adminPort?: number;
18
7
  proxy?: {
19
8
  httpsProxy?: string;
20
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,26 +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
- * Browser access (Host feature `browser-access`): requests with an `Origin` may reach Tenant
28
- * routes, where the publishable key's origin allowlist decides. Off by default: every
29
- * `Origin` is `403 origin_rejected`, as before. Admin routes, `/health` and `/ready` refuse
30
- * `Origin` either way.
31
- */
32
- browserAccess?: boolean;
33
- /**
34
- * The operator listener: the Admin API (and the Tenant API, never with browser access) on
35
- * its own address, kept off the network that reaches the Tenant API. When set, the main
36
- * listener is public and answers admin routes with the opaque 404. Absent: one listener
37
- * serves everything, as before.
38
- */
39
- operator?: OperatorListen;
40
19
  /**
41
20
  * Infrastructure readiness (`infra/readiness.ts`). `/ready` adds its checks
42
21
  * and answers 503 while it reports not ok. Default: the listener and the
@@ -48,7 +27,7 @@ export interface CreateHostOptions {
48
27
  }>;
49
28
  /**
50
29
  * Shutdown steps around closing the Tenant. `close()` runs them whatever asked for it
51
- * (SIGTERM in `host/main.ts`, `POST /v1/admin/host/shutdown`): the listener stops, then
30
+ * (SIGTERM in `host/main.ts`): the listener stops, then
52
31
  * `beforeTenants` (stop the Worker so no advance starts on the closing Tenant), the Tenant
53
32
  * closes, then `afterTenants` (end the infrastructure clients). A failing step is logged
54
33
  * and shutdown goes on.
@@ -58,27 +37,11 @@ export interface CreateHostOptions {
58
37
  afterTenants?(): Promise<void>;
59
38
  };
60
39
  }
61
- /** Where the operator listener binds, and the `Host` values it accepts. */
62
- export interface OperatorListen {
63
- host: string;
64
- port: number;
65
- /** Exact `Host` values (lowercase `name:port`); absent means the loopback forms of the port. */
66
- allowedHosts?: readonly string[];
67
- }
68
- /**
69
- * What a listener serves. `combined`: everything (one port). `public`: the Tenant API, with
70
- * browser access when enabled; admin routes are the opaque 404. `operator`: the Admin API and
71
- * the Tenant API, never to browsers.
72
- */
73
- export type ListenerRole = "combined" | "public" | "operator";
74
40
  export interface HostServer {
75
41
  listen(): Promise<void>;
76
42
  close(): Promise<void>;
77
43
  /** Settles once `close()` has finished, whatever called it. */
78
44
  readonly closed: Promise<void>;
79
45
  readonly url: string;
80
- /** Where the Admin API answers: the operator listener, or `url` when there is one listener. */
81
- readonly adminUrl: string;
82
46
  }
83
- export { adminKeyMatches } from "./http.js";
84
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,52 +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
- });
51
16
  const app = createHostApp({
52
17
  module,
53
18
  logger,
54
19
  hostId: config.hostId,
55
- adminKey: credentials.adminKey,
56
20
  coreVersion,
57
21
  pid,
58
- browserAccess: options.browserAccess === true,
59
22
  ...(options.readiness ? { readiness: options.readiness } : {}),
60
- listening: () => Boolean(server?.listening) && (!operator || Boolean(operatorServer?.listening)),
23
+ listening: () => Boolean(server?.listening),
61
24
  closing: () => closing,
62
- admin: async (request, node) => await adminApi.fetch(request, node),
63
25
  });
64
- /** Where a listener's requests go once their `Host` header checks out. */
65
- 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)), {
66
28
  // The Runtime runs inside other processes (`startEphemeralRuntime`): leave their
67
29
  // `Request` and `Response` alone.
68
30
  overrideGlobalObjects: false,
@@ -76,30 +38,23 @@ export function createHost(options) {
76
38
  return rejectedResponse(500, "internal_error", "Internal error");
77
39
  },
78
40
  });
79
- /** The `Host` values a listener accepts. */
80
- const hostAllowed = (role, hostHeader) => role === "operator"
81
- ? isAllowedRequestHost(hostHeader, {
82
- port: operatorPort,
83
- host: operator.host,
84
- allowNonLoopback: false,
85
- ...(operator.allowedHosts ? { allowedHosts: operator.allowedHosts } : {}),
86
- })
87
- : isAllowedRequestHost(hostHeader, {
88
- port: listenPort,
89
- host: config.host,
90
- allowNonLoopback: config.allowNonLoopback,
91
- ...(containerListen ? { allowedHosts: containerListen.allowedHosts } : {}),
92
- });
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
+ });
93
48
  /** Responses not yet finished; shutdown ends the streams among them. */
94
49
  const inFlight = new Set();
95
- const serve = (role) => {
96
- const next = pipeline(role);
50
+ const serve = () => {
51
+ const next = pipeline();
97
52
  return (req, res) => {
98
53
  const started = Date.now();
99
54
  inFlight.add(res);
100
55
  res.once("close", () => inFlight.delete(res));
101
56
  // D§11: `Host` first, before anything reads the request.
102
- if (hostAllowed(role, headerValue(req, "host")))
57
+ if (hostAllowed(headerValue(req, "host")))
103
58
  return void next(req, res);
104
59
  sendRejected(res, 421, "host_rejected", "Host header is not an allowed loopback or configured address");
105
60
  if (pathnameIsLogged(req.url))
@@ -123,39 +78,15 @@ export function createHost(options) {
123
78
  !isLoopbackHost(config.host)) {
124
79
  throw new HostListenError(`Refusing to bind non-loopback host ${config.host} without allowNonLoopback`, EXIT_NON_LOOPBACK);
125
80
  }
126
- if (operator && !containerListen && !isLoopbackHost(operator.host))
127
- throw new HostListenError(`Refusing to bind the operator listener on non-loopback host ${operator.host}`, EXIT_NON_LOOPBACK);
128
- server = createServer(serve(mainRole));
81
+ server = createServer(serve());
129
82
  await bindListener(server, bindPort, bindHost);
130
83
  const address = server.address();
131
84
  listenPort =
132
85
  typeof address === "object" && address ? address.port : bindPort;
133
86
  url = `http://${bindHost}:${listenPort}`;
134
- adminUrl = url;
135
- if (operator) {
136
- operatorServer = createServer(serve("operator"));
137
- await bindListener(operatorServer, operator.port, operator.host).catch(async (error) => {
138
- // Leave nothing half-open: the main listener closes too.
139
- await new Promise((resolve) => server.close(() => resolve()));
140
- server = undefined;
141
- throw error;
142
- });
143
- const operatorAddress = operatorServer.address();
144
- operatorPort =
145
- typeof operatorAddress === "object" && operatorAddress
146
- ? operatorAddress.port
147
- : operator.port;
148
- adminUrl = `http://${operator.host}:${operatorPort}`;
149
- }
150
- // The Tenant opens once the listener is bound: `/ready` and `/v1/admin/status` answer
151
- // while it opens, and say why when it cannot.
87
+ // The Tenant opens once the listener is bound: `/ready` answers while it opens.
152
88
  await module.start();
153
- logger.info("host_listening", {
154
- hostId: config.hostId,
155
- url,
156
- ...(operator ? { adminUrl } : {}),
157
- pid,
158
- });
89
+ logger.info("host_listening", { hostId: config.hostId, url, pid });
159
90
  }
160
91
  const step = async (name, run) => {
161
92
  try {
@@ -178,7 +109,7 @@ export function createHost(options) {
178
109
  closePromise = (async () => {
179
110
  closing = true;
180
111
  logger.info("host_shutdown", { hostId: config.hostId });
181
- const listeners = [server, operatorServer].filter((listening) => listening !== undefined);
112
+ const listeners = server ? [server] : [];
182
113
  const stopped = Promise.all(listeners.map((listening) => new Promise((resolve) => {
183
114
  listening.close(() => resolve());
184
115
  listening.closeIdleConnections();
@@ -211,7 +142,6 @@ export function createHost(options) {
211
142
  await stopped;
212
143
  }
213
144
  server = undefined;
214
- operatorServer = undefined;
215
145
  await step("beforeTenants", options.shutdown?.beforeTenants);
216
146
  await step("tenants", () => module.close());
217
147
  await step("afterTenants", options.shutdown?.afterTenants);
@@ -225,9 +155,6 @@ export function createHost(options) {
225
155
  get url() {
226
156
  return url;
227
157
  },
228
- get adminUrl() {
229
- return adminUrl;
230
- },
231
158
  };
232
159
  }
233
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";
@@ -34,7 +35,12 @@ export async function startGates(options) {
34
35
  if (!vaults) {
35
36
  if (!database || options.hostRoot === undefined)
36
37
  throw new Error("startGates needs the Postgres pool and the Host root");
37
- vaults = createTenantVaults({ sql: database, hostRoot: options.hostRoot });
38
+ vaults = createTenantVaults({
39
+ sql: database,
40
+ hostRoot: options.hostRoot,
41
+ ...(gates.resolver ? { resolver: gates.resolver } : {}),
42
+ ...(options.delivery ? { delivery: options.delivery } : {}),
43
+ });
38
44
  }
39
45
  const blobs = options.blobs ??
40
46
  (options.hostRoot !== undefined
@@ -47,10 +53,10 @@ export async function startGates(options) {
47
53
  logger,
48
54
  idleMs: mcpIdleMs,
49
55
  ...(options.openMcp ? { open: options.openMcp } : {}),
56
+ ...(options.delivery ? { delivery: options.delivery } : {}),
50
57
  });
51
58
  const toolCalls = createToolCalls({
52
59
  vaults,
53
- mcp,
54
60
  logger,
55
61
  ...(options.inflight ? { inflight: options.inflight } : {}),
56
62
  });
@@ -93,8 +99,8 @@ export async function startGates(options) {
93
99
  },
94
100
  ...(options.maxBodyBytes !== undefined ? { maxBodyBytes: options.maxBodyBytes } : {}),
95
101
  mcp,
102
+ http: gateHttpTools(vaults, options.delivery ?? {}, logger),
96
103
  toolCalls,
97
- delivery: options.delivery ?? {},
98
104
  logger,
99
105
  ...(options.keys ? { keys: async () => (await vaults.open()).keys() } : {}),
100
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";