@nylorun/runtime 0.23.0-beta → 0.25.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 (301) hide show
  1. package/CHANGELOG.md +252 -0
  2. package/README.md +87 -28
  3. package/dist/adapters/execution/restate.js +1 -1
  4. package/dist/adapters/replication/pgoutput.d.ts +2 -0
  5. package/dist/adapters/replication/pgoutput.js +6 -3
  6. package/dist/adapters/sandbox/local.d.ts +9 -1
  7. package/dist/adapters/sandbox/local.js +16 -4
  8. package/dist/adapters/sandbox/virtual.js +4 -1
  9. package/dist/api/a2a/card.d.ts +11 -1
  10. package/dist/api/a2a/card.js +26 -2
  11. package/dist/api/a2a/endpoint.js +9 -3
  12. package/dist/api/a2a/protocol.d.ts +25 -2
  13. package/dist/api/a2a/protocol.js +20 -3
  14. package/dist/api/a2a/routes.d.ts +6 -1
  15. package/dist/api/a2a/routes.js +67 -9
  16. package/dist/api/a2a/tasks.js +2 -1
  17. package/dist/api/ag-ui/endpoint.js +8 -3
  18. package/dist/api/ag-ui/history.d.ts +6 -1
  19. package/dist/api/ag-ui/history.js +39 -3
  20. package/dist/api/ag-ui/routes.d.ts +4 -1
  21. package/dist/api/ag-ui/routes.js +63 -20
  22. package/dist/api/ag-ui/translate.js +3 -2
  23. package/dist/api/components.d.ts +5327 -1091
  24. package/dist/api/components.js +40 -16
  25. package/dist/api/gate/keys.d.ts +2 -1
  26. package/dist/api/gate/keys.js +14 -1
  27. package/dist/api/gate/routes.d.ts +6 -0
  28. package/dist/api/gate/routes.js +52 -3
  29. package/dist/api/http/app.js +17 -6
  30. package/dist/api/http/body.d.ts +3 -3
  31. package/dist/api/http/body.js +7 -7
  32. package/dist/api/http/define.d.ts +6 -0
  33. package/dist/api/http/define.js +13 -1
  34. package/dist/api/http/respond.js +1 -1
  35. package/dist/api/http/routes/artifacts.js +35 -8
  36. package/dist/api/http/routes/coverage.d.ts +9 -0
  37. package/dist/api/http/routes/coverage.js +34 -0
  38. package/dist/api/http/routes/environment-manifests.d.ts +9 -0
  39. package/dist/api/http/routes/environment-manifests.js +118 -0
  40. package/dist/api/http/routes/environments.d.ts +11 -0
  41. package/dist/api/http/routes/environments.js +332 -0
  42. package/dist/api/http/routes/mcp.d.ts +1 -1
  43. package/dist/api/http/routes/mcp.js +2 -2
  44. package/dist/api/http/routes/me.js +1 -1
  45. package/dist/api/http/routes/reads.d.ts +94 -9
  46. package/dist/api/http/routes/reads.js +54 -5
  47. package/dist/api/http/routes/sessions.js +24 -9
  48. package/dist/api/http/routes/tenant.js +65 -15
  49. package/dist/api/inbound-files.d.ts +65 -0
  50. package/dist/api/inbound-files.js +115 -0
  51. package/dist/api/openapi.js +48 -28
  52. package/dist/artifacts/disk.d.ts +59 -0
  53. package/dist/artifacts/disk.js +569 -0
  54. package/dist/artifacts/export.d.ts +1 -1
  55. package/dist/artifacts/export.js +2 -3
  56. package/dist/artifacts/folders.d.ts +6 -3
  57. package/dist/artifacts/folders.js +17 -12
  58. package/dist/artifacts/media-types.d.ts +15 -0
  59. package/dist/artifacts/media-types.js +56 -4
  60. package/dist/artifacts/parts.d.ts +12 -3
  61. package/dist/artifacts/parts.js +15 -4
  62. package/dist/artifacts/service.d.ts +2 -1
  63. package/dist/artifacts/service.js +6 -1
  64. package/dist/artifacts/workspace.d.ts +5 -5
  65. package/dist/artifacts/workspace.js +7 -19
  66. package/dist/core/flow-host.d.ts +3 -4
  67. package/dist/core/flow-host.js +5 -2
  68. package/dist/core/provider.d.ts +3 -2
  69. package/dist/core/provider.js +5 -37
  70. package/dist/core/sandbox-routes.d.ts +2 -21
  71. package/dist/core/sandbox-routes.js +11 -44
  72. package/dist/core/turn-manifest.d.ts +3 -2
  73. package/dist/core/turn-manifest.js +29 -5
  74. package/dist/execution/types.d.ts +1 -1
  75. package/dist/gates/contract.d.ts +3 -24
  76. package/dist/gates/contract.js +1 -36
  77. package/dist/gates/egress-credentials.d.ts +9 -0
  78. package/dist/gates/egress-credentials.js +11 -4
  79. package/dist/gates/egress.d.ts +22 -1
  80. package/dist/gates/egress.js +116 -10
  81. package/dist/gates/fixture-model.d.ts +28 -0
  82. package/dist/gates/fixture-model.js +92 -0
  83. package/dist/gates/handler.js +4 -4
  84. package/dist/gates/http-tool.d.ts +9 -6
  85. package/dist/gates/http-tool.js +11 -17
  86. package/dist/gates/in-process.d.ts +5 -0
  87. package/dist/gates/in-process.js +12 -1
  88. package/dist/gates/journal.d.ts +30 -0
  89. package/dist/gates/journal.js +51 -0
  90. package/dist/gates/mcp-handler.d.ts +4 -0
  91. package/dist/gates/mcp-handler.js +7 -4
  92. package/dist/gates/meter.d.ts +10 -2
  93. package/dist/gates/meter.js +20 -5
  94. package/dist/gates/policy.d.ts +49 -0
  95. package/dist/gates/policy.js +132 -0
  96. package/dist/gates/tenant-vaults.d.ts +7 -7
  97. package/dist/gates/tenant-vaults.js +5 -5
  98. package/dist/gates/tool-calls.d.ts +10 -5
  99. package/dist/gates/tool-calls.js +28 -10
  100. package/dist/gates/tool-client.d.ts +8 -0
  101. package/dist/gates/tool-client.js +27 -11
  102. package/dist/gates/tool-contract.d.ts +11 -69
  103. package/dist/gates/tool-contract.js +1 -85
  104. package/dist/gates/tool-gate.d.ts +1 -1
  105. package/dist/harness/calls.d.ts +14 -7
  106. package/dist/harness/calls.js +30 -24
  107. package/dist/harness/executors.js +12 -9
  108. package/dist/harness/main.js +24 -5
  109. package/dist/harness/pod.d.ts +39 -0
  110. package/dist/harness/pod.js +46 -0
  111. package/dist/harness/service.d.ts +0 -1
  112. package/dist/harness/service.js +17 -9
  113. package/dist/harness/setup.d.ts +29 -0
  114. package/dist/harness/setup.js +174 -0
  115. package/dist/harness/ws-client.js +3 -2
  116. package/dist/harness/ws-port.d.ts +1 -5
  117. package/dist/harness/ws-port.js +1 -5
  118. package/dist/harness-api/in-process.js +3 -3
  119. package/dist/harness-api/record.d.ts +15 -1
  120. package/dist/harness-api/record.js +180 -50
  121. package/dist/harness-api/redact.d.ts +3 -0
  122. package/dist/harness-api/redact.js +45 -0
  123. package/dist/harness-api/server.d.ts +35 -4
  124. package/dist/harness-api/server.js +130 -17
  125. package/dist/harness-api/start.d.ts +10 -3
  126. package/dist/harness-api/start.js +40 -10
  127. package/dist/harness-api/tool-journal.d.ts +105 -0
  128. package/dist/harness-api/tool-journal.js +132 -0
  129. package/dist/harness-api/tool-status.d.ts +13 -0
  130. package/dist/harness-api/tool-status.js +26 -0
  131. package/dist/harness-api/workspace.d.ts +12 -7
  132. package/dist/harness-api/workspace.js +47 -25
  133. package/dist/harness-api/ws-server.d.ts +11 -3
  134. package/dist/harness-api/ws-server.js +94 -15
  135. package/dist/host/gates.d.ts +2 -1
  136. package/dist/host/gates.js +10 -2
  137. package/dist/host/main.js +54 -5
  138. package/dist/host/operate.js +67 -5
  139. package/dist/host/stack-config.d.ts +64 -0
  140. package/dist/host/stack-config.js +62 -2
  141. package/dist/keys/client.js +4 -1
  142. package/dist/keys/keys.d.ts +13 -6
  143. package/dist/keys/keys.js +9 -8
  144. package/dist/management-openapi.json +1264 -2171
  145. package/dist/mcp/connect.d.ts +26 -22
  146. package/dist/mcp/connect.js +30 -34
  147. package/dist/mcp/pool.d.ts +3 -6
  148. package/dist/mcp/pool.js +14 -28
  149. package/dist/mcp/preview.d.ts +3 -3
  150. package/dist/mcp/preview.js +10 -21
  151. package/dist/mcp/snapshot.d.ts +16 -24
  152. package/dist/mcp/snapshot.js +33 -45
  153. package/dist/model/capabilities.d.ts +26 -0
  154. package/dist/model/capabilities.js +44 -0
  155. package/dist/model/catalog.d.ts +34 -3
  156. package/dist/model/catalog.js +38 -2
  157. package/dist/model/models.d.ts +2 -0
  158. package/dist/model/models.js +1 -1
  159. package/dist/model/native-blocks.d.ts +35 -0
  160. package/dist/model/native-blocks.js +170 -0
  161. package/dist/model/pi-model.js +114 -94
  162. package/dist/openapi.json +12822 -3943
  163. package/dist/reads/types.d.ts +16 -3
  164. package/dist/record/append.d.ts +3 -1
  165. package/dist/record/append.js +1 -0
  166. package/dist/record/envelope.d.ts +5 -1
  167. package/dist/record/envelope.js +7 -3
  168. package/dist/record/environment.d.ts +25 -0
  169. package/dist/record/environment.js +44 -0
  170. package/dist/record/index.d.ts +1 -1
  171. package/dist/record/index.js +1 -1
  172. package/dist/sandbox/disk-fs.d.ts +95 -0
  173. package/dist/sandbox/disk-fs.js +206 -0
  174. package/dist/sandbox/join.d.ts +17 -18
  175. package/dist/sandbox/join.js +46 -15
  176. package/dist/sandbox/manager.d.ts +47 -21
  177. package/dist/sandbox/manager.js +162 -107
  178. package/dist/sandbox/placement.d.ts +14 -13
  179. package/dist/sandbox/placement.js +31 -14
  180. package/dist/sandbox/pods/client.d.ts +9 -2
  181. package/dist/sandbox/pods/lifecycle.d.ts +36 -27
  182. package/dist/sandbox/pods/lifecycle.js +29 -26
  183. package/dist/sandbox/pods/reconcile.js +18 -12
  184. package/dist/sandbox/pods/spec.d.ts +15 -13
  185. package/dist/sandbox/pods/spec.js +17 -20
  186. package/dist/sandbox/records.d.ts +2 -2
  187. package/dist/sandbox/records.js +3 -3
  188. package/dist/sandbox/resolve.d.ts +37 -27
  189. package/dist/sandbox/resolve.js +73 -41
  190. package/dist/sandbox/runtime-tools.d.ts +9 -0
  191. package/dist/sandbox/runtime-tools.js +16 -0
  192. package/dist/sandbox/session-sandbox.d.ts +4 -6
  193. package/dist/sandbox/session-sandbox.js +47 -94
  194. package/dist/sandbox/setup.d.ts +64 -0
  195. package/dist/sandbox/setup.js +160 -0
  196. package/dist/sandbox/share.d.ts +21 -26
  197. package/dist/sandbox/share.js +36 -63
  198. package/dist/sandbox/skills.d.ts +8 -6
  199. package/dist/sandbox/skills.js +24 -23
  200. package/dist/sandbox/tenant-config.d.ts +12 -14
  201. package/dist/sandbox/tenant-config.js +12 -17
  202. package/dist/sandbox/types.d.ts +6 -0
  203. package/dist/store/postgres/drizzle/0020_environments.sql +63 -0
  204. package/dist/store/postgres/drizzle/0021_disk_versions.sql +7 -0
  205. package/dist/store/postgres/drizzle/0022_model_usage_timing.sql +5 -0
  206. package/dist/store/postgres/drizzle/0023_artifact_pages.sql +1 -0
  207. package/dist/store/postgres/drizzle/0024_flat_manifest.sql +7 -0
  208. package/dist/store/postgres/drizzle/0025_environment_manifests.sql +14 -0
  209. package/dist/store/postgres/drizzle/0026_environment_forks.sql +1 -0
  210. package/dist/store/postgres/drizzle/0027_environment_image_digest.sql +1 -0
  211. package/dist/store/postgres/drizzle/meta/_journal.json +56 -0
  212. package/dist/store/postgres/reads.js +123 -17
  213. package/dist/store/postgres/record-writer.d.ts +4 -4
  214. package/dist/store/postgres/record-writer.js +9 -9
  215. package/dist/store/postgres/record.d.ts +7 -0
  216. package/dist/store/postgres/record.js +23 -2
  217. package/dist/store/postgres/schema.d.ts +1126 -736
  218. package/dist/store/postgres/schema.js +136 -73
  219. package/dist/store/postgres/store.js +195 -67
  220. package/dist/store/types.d.ts +165 -50
  221. package/dist/tenant/advance.d.ts +7 -6
  222. package/dist/tenant/advance.js +118 -42
  223. package/dist/tenant/artifact-tool.d.ts +1 -1
  224. package/dist/tenant/artifact-tool.js +46 -26
  225. package/dist/tenant/commands.d.ts +15 -2
  226. package/dist/tenant/commands.js +115 -14
  227. package/dist/tenant/context.d.ts +56 -24
  228. package/dist/tenant/context.js +4 -15
  229. package/dist/tenant/effects.d.ts +1 -1
  230. package/dist/tenant/effects.js +44 -29
  231. package/dist/tenant/environment-manifests.d.ts +57 -0
  232. package/dist/tenant/environment-manifests.js +200 -0
  233. package/dist/tenant/environments.d.ts +174 -0
  234. package/dist/tenant/environments.js +1044 -0
  235. package/dist/tenant/harness-tokens.d.ts +21 -0
  236. package/dist/tenant/harness-tokens.js +61 -0
  237. package/dist/tenant/host-token.d.ts +1 -1
  238. package/dist/tenant/host-token.js +2 -4
  239. package/dist/tenant/identity-file.d.ts +4 -4
  240. package/dist/tenant/identity-file.js +17 -8
  241. package/dist/tenant/issuers.js +3 -3
  242. package/dist/tenant/model-setting.d.ts +14 -0
  243. package/dist/tenant/model-setting.js +51 -0
  244. package/dist/tenant/principals.js +1 -1
  245. package/dist/tenant/reset.js +3 -3
  246. package/dist/tenant/runtime.d.ts +6 -1
  247. package/dist/tenant/runtime.js +7 -1
  248. package/dist/tenant/session.d.ts +3 -3
  249. package/dist/tenant/session.js +0 -14
  250. package/dist/tenant/sessions.d.ts +18 -14
  251. package/dist/tenant/sessions.js +99 -143
  252. package/dist/tenant/signing-keys.d.ts +5 -1
  253. package/dist/tenant/skill-tool.d.ts +3 -3
  254. package/dist/tenant/skill-tool.js +6 -11
  255. package/dist/tenant/status.js +3 -3
  256. package/dist/tenant/sweep.js +13 -7
  257. package/dist/tenant/tool-results.js +1 -1
  258. package/dist/tenant/tool-search.d.ts +7 -11
  259. package/dist/tenant/tool-search.js +8 -5
  260. package/dist/tenant/transcript.d.ts +245 -36
  261. package/dist/tenant/transcript.js +64 -12
  262. package/dist/tenant/turn-timing.d.ts +43 -0
  263. package/dist/tenant/turn-timing.js +39 -0
  264. package/dist/tenant/types.d.ts +2 -0
  265. package/dist/traces/ids.d.ts +41 -0
  266. package/dist/traces/ids.js +81 -0
  267. package/dist/traces/key.d.ts +6 -0
  268. package/dist/traces/key.js +37 -0
  269. package/dist/traces/map.d.ts +113 -0
  270. package/dist/traces/map.js +289 -0
  271. package/dist/traces/options.d.ts +7 -0
  272. package/dist/traces/options.js +1 -0
  273. package/dist/traces/otlp.d.ts +42 -0
  274. package/dist/traces/otlp.js +84 -0
  275. package/dist/traces/projector.d.ts +58 -0
  276. package/dist/traces/projector.js +200 -0
  277. package/dist/traces/read.d.ts +23 -0
  278. package/dist/traces/read.js +16 -0
  279. package/dist/vault/coverage.d.ts +26 -0
  280. package/dist/vault/coverage.js +62 -0
  281. package/dist/vault/error.d.ts +11 -1
  282. package/dist/vault/error.js +5 -1
  283. package/dist/vault/host-model.d.ts +4 -1
  284. package/dist/vault/host-model.js +10 -2
  285. package/dist/vault/service.d.ts +92 -48
  286. package/dist/vault/service.js +462 -376
  287. package/dist/vault/sources.d.ts +5 -16
  288. package/dist/vault/sources.js +6 -8
  289. package/dist/version.d.ts +1 -1
  290. package/dist/version.js +2 -2
  291. package/package.json +13 -13
  292. package/dist/api/http/routes/sandboxes.d.ts +0 -9
  293. package/dist/api/http/routes/sandboxes.js +0 -153
  294. package/dist/api/http/routes/vaults.d.ts +0 -10
  295. package/dist/api/http/routes/vaults.js +0 -139
  296. package/dist/record/sandbox.d.ts +0 -25
  297. package/dist/record/sandbox.js +0 -43
  298. package/dist/sandbox/capability.d.ts +0 -3
  299. package/dist/sandbox/capability.js +0 -10
  300. package/dist/tenant/sandboxes.d.ts +0 -123
  301. package/dist/tenant/sandboxes.js +0 -566
@@ -9,12 +9,16 @@
9
9
  * nylorun-operate keys list [--json]
10
10
  * nylorun-operate keys put <id> [--role application|management] [--json]
11
11
  * nylorun-operate keys rm <id> [--json]
12
+ * nylorun-operate harness token put <id> [--json]
13
+ * nylorun-operate harness token list [--json]
14
+ * nylorun-operate harness token rm <id> [--json]
12
15
  *
13
16
  * `status` says whether the Tenant is open and, when it is not, why: from the database (no
14
17
  * Tenant yet, an old layout, a schema newer or older than this Runtime) and from the running
15
18
  * Host's `/ready` on this container's listen port. It needs no key, so it works when the Tenant
16
19
  * cannot open. `keys put` prints the new key once on stdout (with `--json`, the whole
17
- * response). Exit codes: 0 done (`status`: the Tenant is open), 1 refused (an id, a role),
20
+ * response). `harness token put` makes a custom harness's own token (R4.1), bound to its harness
21
+ * id, and prints it once; putting it again rotates it. Exit codes: 0 done (`status`: the Tenant is open), 1 refused (an id, a role),
18
22
  * 2 the Tenant cannot be opened (`status` still prints it), 64 usage.
19
23
  */
20
24
  import { pathToFileURL } from "node:url";
@@ -26,6 +30,7 @@ import { createPostgresSessionStore } from "../store/postgres/store.js";
26
30
  import { readTenantEnvelope } from "../store/postgres/tenant.js";
27
31
  import { openError, TenantOpenError } from "../tenant/cause.js";
28
32
  import { operatorKeys } from "../tenant/operator-keys.js";
33
+ import { deleteHarnessToken, listHarnessTokens, putHarnessToken } from "../tenant/harness-tokens.js";
29
34
  import { RUNTIME_VERSION } from "../version.js";
30
35
  import { DEFAULT_CONTAINER_LISTEN_PORT } from "./stack-config.js";
31
36
  export const EXIT_REFUSED = 1;
@@ -35,7 +40,10 @@ const USAGE = `Usage:
35
40
  nylorun-operate status [--json]
36
41
  nylorun-operate keys list [--json]
37
42
  nylorun-operate keys put <id> [--role application|management] [--json]
38
- nylorun-operate keys rm <id> [--json]`;
43
+ nylorun-operate keys rm <id> [--json]
44
+ nylorun-operate harness token put <id> [--json]
45
+ nylorun-operate harness token list [--json]
46
+ nylorun-operate harness token rm <id> [--json]`;
39
47
  class Exit extends Error {
40
48
  code;
41
49
  constructor(code, message) {
@@ -52,14 +60,14 @@ export async function runOperate(argv, io) {
52
60
  throw new Exit(EXIT_USAGE, USAGE);
53
61
  return await status(io, json);
54
62
  }
55
- const command = parse(argv);
63
+ const command = argv[0] === "harness" ? harnessCommand(argv) : parse(argv);
56
64
  const url = io.env.NYLORUN_DATABASE_URL;
57
65
  if (!url)
58
66
  throw new Exit(EXIT_TENANT, "NYLORUN_DATABASE_URL is not set: run this inside the runtime container");
59
67
  const sql = createPostgresClient(url, { max: 1 });
60
68
  try {
61
- const keys = operatorKeys(await openStore(sql));
62
- return await command(keys, io);
69
+ const store = await openStore(sql);
70
+ return await command(operatorKeys(store), io, store);
63
71
  }
64
72
  finally {
65
73
  await sql.end({ timeout: 5 });
@@ -74,6 +82,60 @@ export async function runOperate(argv, io) {
74
82
  return EXIT_TENANT;
75
83
  }
76
84
  }
85
+ /** `harness token put|list|rm` (R4.1). */
86
+ function harnessCommand(argv) {
87
+ const [, noun, verb, ...rest] = argv;
88
+ const json = rest.includes("--json");
89
+ const args = rest.filter((arg) => arg !== "--json");
90
+ if (noun !== "token")
91
+ throw new Exit(EXIT_USAGE, USAGE);
92
+ switch (verb) {
93
+ case "list":
94
+ if (args.length > 0)
95
+ throw new Exit(EXIT_USAGE, USAGE);
96
+ return async (_keys, io, store) => {
97
+ const tokens = await listHarnessTokens(store);
98
+ if (json)
99
+ io.out(JSON.stringify({ tokens }));
100
+ else
101
+ for (const token of tokens)
102
+ io.out(`${token.id}\t${token.createdAt}`);
103
+ return 0;
104
+ };
105
+ case "put": {
106
+ const [id] = args;
107
+ if (!id || args.length !== 1)
108
+ throw new Exit(EXIT_USAGE, USAGE);
109
+ return async (_keys, io, store) => {
110
+ const put = await putHarnessToken(store, id);
111
+ if ("reason" in put)
112
+ throw new Exit(EXIT_REFUSED, put.message);
113
+ if (json)
114
+ io.out(JSON.stringify(put));
115
+ else {
116
+ io.out(put.token);
117
+ io.err(`${put.rotated ? "Rotated" : "Created"} the token of harness ${put.id}. It is shown this once: the harness connects with it as NYLORUN_HARNESS_TOKEN and must say ${put.id} in hello.`);
118
+ }
119
+ return 0;
120
+ };
121
+ }
122
+ case "rm": {
123
+ const [id] = args;
124
+ if (!id || args.length !== 1)
125
+ throw new Exit(EXIT_USAGE, USAGE);
126
+ return async (_keys, io, store) => {
127
+ const deleted = await deleteHarnessToken(store, id);
128
+ if (json)
129
+ io.out(JSON.stringify({ id, deleted }));
130
+ else
131
+ io.err(deleted ? `Deleted the token of harness ${id}.` : `Harness ${id} has no token.`);
132
+ return 0;
133
+ };
134
+ }
135
+ default:
136
+ throw new Exit(EXIT_USAGE, USAGE);
137
+ }
138
+ }
77
139
  function parse(argv) {
78
140
  const [group, verb, ...rest] = argv;
79
141
  if (group !== "keys")
@@ -1,3 +1,49 @@
1
+ /**
2
+ * Stack configuration for the Runtime process, parsed from an environment
3
+ * snapshot and argv. Pure: `host/main.ts` passes the process environment and
4
+ * arguments; nothing here reads ambient state.
5
+ *
6
+ * `--service core,loop` names the Runtime services the process runs (blueprint
7
+ * §19, D12): one image runs every service, and a container may pack several.
8
+ * Without the flag a process runs core and loop. `--role api|worker|all` is
9
+ * the deprecated name of the same choice (core, loop, or both). `gates` (the
10
+ * Model Gate) never shares a process with core or loop: it holds the
11
+ * credentials they must not. Only a process that runs core or loop parses the
12
+ * API listener (`NYLORUN_LISTEN_*`); only one that runs gates parses its
13
+ * listener (`NYLORUN_GATES_LISTEN_*`), and only one that runs loop parses
14
+ * where to reach the gate (`NYLORUN_GATES_URL`). Both read
15
+ * `NYLORUN_GATES_TOKEN`. In a container, loop requires the gate: the loop
16
+ * process must never hold a model credential. `egress` (egress-gate, F7.2) joins gates and keys
17
+ * in the gateway and parses its listener (`NYLORUN_EGRESS_LISTEN_*`, default `0.0.0.0:4200`).
18
+ * The credential resolver is gone (protocol 10): its `NYLORUN_RESOLVER_*` variables are ignored,
19
+ * and the Host logs `resolver_removed` when one is set.
20
+ *
21
+ * `harness` (F6.2) runs alone: it holds the harness credential and nothing else. It connects to
22
+ * core's Harness API listener (`NYLORUN_HARNESS_URL`, `NYLORUN_HARNESS_TOKEN`), calls models and
23
+ * remote MCP servers through the gates service (`NYLORUN_GATES_URL`, with run tokens only), and
24
+ * keeps its workspaces under `NYLORUN_HARNESS_ROOT`. It refuses to start with a database, the
25
+ * gates' or keys' credential, or Restate settings. A process running core starts the Harness API
26
+ * listener when `NYLORUN_HARNESS=remote` (`NYLORUN_HARNESS_LISTEN_*`, `NYLORUN_HARNESS_TOKEN`).
27
+ *
28
+ * Two listen modes:
29
+ * - **local** (no `NYLORUN_LISTEN_*` / `NYLORUN_ALLOWED_HOSTS`): the Host binds
30
+ * what `host.json` names, loopback only unless `allowNonLoopback`.
31
+ * - **container**: the Host binds `NYLORUN_LISTEN_HOST:NYLORUN_LISTEN_PORT`
32
+ * (default `0.0.0.0:4000`) and accepts only the `Host` headers in
33
+ * `NYLORUN_ALLOWED_HOSTS`, plus the loopback forms of the listen port for
34
+ * probes from inside the container.
35
+ *
36
+ * The Postgres, Restate and S2 endpoints are parsed and validated here;
37
+ * `infra/*` builds the clients from them. So is the Object store (`NYLORUN_OBJECT_STORE_*`),
38
+ * which every service may read; `host/main.ts` builds the `BlobStore` from it. So is who the Host's Tenant is when its database
39
+ * holds none yet (`NYLORUN_TENANT_ID`, `NYLORUN_TENANT_NAME`). So are traces (R7.2), for a
40
+ * process running core: where they go (`OTEL_EXPORTER_OTLP_ENDPOINT`, or
41
+ * `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`, with `OTEL_EXPORTER_OTLP_HEADERS`), what they say of
42
+ * their source (`OTEL_SERVICE_NAME`, `OTEL_RESOURCE_ATTRIBUTES`) and which content they carry
43
+ * (`NYLORUN_TRACE_CONTENT`, or all of it with `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`).
44
+ */
45
+ import { type TraceResource } from "../traces/otlp.js";
46
+ import { type TraceContentKind } from "../traces/map.js";
1
47
  import { type BlockedAddress } from "../sandbox/pods/network-gate.js";
2
48
  /** A Runtime service this release has. */
3
49
  export type RuntimeService = "core" | "loop" | "gates" | "keys" | "egress" | "harness";
@@ -197,6 +243,24 @@ export interface StackConfig {
197
243
  * (the `fs` adapter, `TenantPaths.blobs`).
198
244
  */
199
245
  objectStore?: ObjectStoreConfig;
246
+ /**
247
+ * Present when the process runs core and an `OTEL_*` or `NYLORUN_TRACE_CONTENT` variable is set:
248
+ * how spans are made, for the trace projector and the trace route (R7.2, R7.5). `export` is
249
+ * present only with an endpoint (Q5): without one there is no replication slot and no cost.
250
+ */
251
+ traces?: TraceConfig;
252
+ }
253
+ /** Traces (R7.2), from the standard `OTEL_*` variables and `NYLORUN_TRACE_CONTENT`. */
254
+ export interface TraceConfig {
255
+ /** `OTEL_SERVICE_NAME` (default `nylorun`) and `OTEL_RESOURCE_ATTRIBUTES`. */
256
+ resource: TraceResource;
257
+ /** The content kinds spans carry: none by default. */
258
+ content: TraceContentKind[];
259
+ /** Where the projector posts: the traces URL and `OTEL_EXPORTER_OTLP_HEADERS`. */
260
+ export?: {
261
+ url: string;
262
+ headers: Record<string, string>;
263
+ };
200
264
  }
201
265
  /** Where the Object store is and the credential for it (`NYLORUN_OBJECT_STORE_*`). */
202
266
  export interface ObjectStoreConfig {
@@ -36,8 +36,14 @@
36
36
  * The Postgres, Restate and S2 endpoints are parsed and validated here;
37
37
  * `infra/*` builds the clients from them. So is the Object store (`NYLORUN_OBJECT_STORE_*`),
38
38
  * which every service may read; `host/main.ts` builds the `BlobStore` from it. So is who the Host's Tenant is when its database
39
- * holds none yet (`NYLORUN_TENANT_ID`, `NYLORUN_TENANT_NAME`).
39
+ * holds none yet (`NYLORUN_TENANT_ID`, `NYLORUN_TENANT_NAME`). So are traces (R7.2), for a
40
+ * process running core: where they go (`OTEL_EXPORTER_OTLP_ENDPOINT`, or
41
+ * `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`, with `OTEL_EXPORTER_OTLP_HEADERS`), what they say of
42
+ * their source (`OTEL_SERVICE_NAME`, `OTEL_RESOURCE_ATTRIBUTES`) and which content they carry
43
+ * (`NYLORUN_TRACE_CONTENT`, or all of it with `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`).
40
44
  */
45
+ import { DEFAULT_SERVICE_NAME } from "../traces/otlp.js";
46
+ import { TRACE_CONTENT_KINDS } from "../traces/map.js";
41
47
  import { isTenantId } from "@nylorun/core/compatibility";
42
48
  import { blockedAddresses } from "../sandbox/pods/network-gate.js";
43
49
  export const RUNTIME_SERVICES = ["core", "loop", "gates", "keys", "egress", "harness"];
@@ -316,6 +322,7 @@ export function parseStackConfig(env, argv) {
316
322
  const delivery = parseDelivery(env);
317
323
  const tenant = servesApi ? parseTenant(env) : undefined;
318
324
  const objectStore = parseObjectStore(env);
325
+ const traces = services.has("core") ? parseTraces(env) : undefined;
319
326
  const removedResolverVariables = Object.keys(env)
320
327
  .filter((name) => name.startsWith(REMOVED_RESOLVER_PREFIX) && read(env, name) !== undefined)
321
328
  .sort();
@@ -337,8 +344,61 @@ export function parseStackConfig(env, argv) {
337
344
  ...(delivery ? { delivery } : {}),
338
345
  ...(tenant ? { tenant } : {}),
339
346
  ...(objectStore ? { objectStore } : {}),
347
+ ...(traces ? { traces } : {}),
340
348
  };
341
349
  }
350
+ /**
351
+ * `StackConfig.traces` (R7.2). The OTLP endpoint is the collector's base URL, to which
352
+ * `/v1/traces` is added; `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` is the full URL and wins. Headers
353
+ * and resource attributes are `key=value` lists separated by commas, values percent-encoded (the
354
+ * OTel SDK environment specification).
355
+ */
356
+ function parseTraces(env) {
357
+ const traces = parseUrl(env, "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", ["http:", "https:"]);
358
+ const base = parseUrl(env, "OTEL_EXPORTER_OTLP_ENDPOINT", ["http:", "https:"]);
359
+ const url = traces ?? (base === undefined ? undefined : `${base.replace(/\/+$/, "")}/v1/traces`);
360
+ const resource = keyValues(env, "OTEL_RESOURCE_ATTRIBUTES");
361
+ const all = read(env, "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT")?.toLowerCase() === "true";
362
+ const content = new Set(all ? TRACE_CONTENT_KINDS : []);
363
+ for (const kind of (read(env, "NYLORUN_TRACE_CONTENT") ?? "").split(",").map((part) => part.trim())) {
364
+ if (kind === "")
365
+ continue;
366
+ if (!TRACE_CONTENT_KINDS.includes(kind))
367
+ throw new StackConfigError(`NYLORUN_TRACE_CONTENT lists ${TRACE_CONTENT_KINDS.join(", ")}, not ${kind}`);
368
+ content.add(kind);
369
+ }
370
+ const serviceName = read(env, "OTEL_SERVICE_NAME") ?? resource["service.name"];
371
+ // Nothing set: the defaults (`service.name` nylorun, no content, no export).
372
+ if (url === undefined && serviceName === undefined && Object.keys(resource).length === 0 && content.size === 0)
373
+ return undefined;
374
+ return {
375
+ resource: {
376
+ serviceName: serviceName ?? DEFAULT_SERVICE_NAME,
377
+ attributes: resource,
378
+ },
379
+ content: TRACE_CONTENT_KINDS.filter((kind) => content.has(kind)),
380
+ ...(url ? { export: { url, headers: keyValues(env, "OTEL_EXPORTER_OTLP_HEADERS") } } : {}),
381
+ };
382
+ }
383
+ /** A `key=value,key=value` variable, values percent-decoded; a malformed one is refused. */
384
+ function keyValues(env, name) {
385
+ const out = {};
386
+ for (const pair of (read(env, name) ?? "").split(",")) {
387
+ if (pair.trim() === "")
388
+ continue;
389
+ const at = pair.indexOf("=");
390
+ const key = at < 0 ? "" : pair.slice(0, at).trim();
391
+ if (key === "")
392
+ throw new StackConfigError(`${name} must be key=value pairs separated by commas`);
393
+ try {
394
+ out[key] = decodeURIComponent(pair.slice(at + 1).trim());
395
+ }
396
+ catch {
397
+ throw new StackConfigError(`${name} has a value that is not percent-encoded: ${key}`);
398
+ }
399
+ }
400
+ return out;
401
+ }
342
402
  /** S3 bucket names: 3 to 63 lowercase letters, digits, dots and hyphens. */
343
403
  const BUCKET = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/;
344
404
  /** `StackConfig.objectStore` from `NYLORUN_OBJECT_STORE_*`, or `undefined` without an endpoint. */
@@ -399,7 +459,7 @@ function parseSandboxes(env) {
399
459
  return undefined;
400
460
  const token = read(env, "NYLORUN_SANDBOXES_TOKEN");
401
461
  if (token === undefined || token.length < 32)
402
- throw new StackConfigError("NYLORUN_SANDBOXES_TOKEN (at least 32 characters) is required with NYLORUN_SANDBOXES_URL (`nylorun sandbox enable` sets both)");
462
+ throw new StackConfigError("NYLORUN_SANDBOXES_TOKEN (at least 32 characters) is required with NYLORUN_SANDBOXES_URL (`nylorun local sandbox enable` sets both)");
403
463
  const harnessImage = read(env, "NYLORUN_SANDBOX_HARNESS_IMAGE");
404
464
  if (harnessImage === undefined || /\s/.test(harnessImage))
405
465
  throw new StackConfigError("NYLORUN_SANDBOX_HARNESS_IMAGE is required with NYLORUN_SANDBOXES_URL: the Runtime image sandbox pods copy the engine from (`nylorun start` sets it)");
@@ -51,7 +51,10 @@ export function httpKeys(options) {
51
51
  return resolve(answer.result);
52
52
  const error = answer.error;
53
53
  reject(error.kind === "vault"
54
- ? new VaultError(error.status, error.message)
54
+ ? new VaultError(error.status, error.message, {
55
+ ...(error.code === undefined ? {} : { code: error.code }),
56
+ ...(error.details === undefined ? {} : { details: error.details }),
57
+ })
55
58
  : new HttpError(error.status, error.message, {
56
59
  ...(error.code === undefined ? {} : { code: error.code }),
57
60
  ...(error.details === undefined ? {} : { details: error.details }),
@@ -1,8 +1,8 @@
1
- import type { CreateCredentialRequest, CredentialInfo, HostModelView, McpPreview, McpPreviewRequest, PutHostModelRequest, RotateCredentialRequest, SelectHostModelRequest, SigningKeyView } from "@nylorun/core/contracts";
1
+ import type { EnvironmentSecretView, HostModelView, McpPreview, McpPreviewRequest, PutEnvironmentSecretRequest, PutHostModelRequest, SelectHostModelRequest, SigningKeyView } from "@nylorun/core/contracts";
2
2
  import type { SessionStore } from "../store/types.js";
3
3
  import type { OutboundPolicy } from "../tenant/outbound.js";
4
4
  import { type SigningKeys } from "../tenant/signing-keys.js";
5
- import type { VaultService } from "../vault/service.js";
5
+ import type { SealedSecretCopies, VaultService } from "../vault/service.js";
6
6
  /** One JWT to sign: its `typ` and every claim, `iss`, `aud`, `sub`, `iat`, `exp` and `jti` included. */
7
7
  export interface SignRequest {
8
8
  readonly typ: string;
@@ -14,10 +14,17 @@ export interface Signed {
14
14
  readonly keyId: string;
15
15
  }
16
16
  export interface Keys {
17
- createCredential(vaultId: string, body: CreateCredentialRequest): Promise<CredentialInfo>;
18
- rotateCredential(vaultId: string, id: string, body: RotateCredentialRequest): Promise<CredentialInfo>;
17
+ /** `PUT /v1/environments/{id}/secrets/{name}` (`VaultService.putEnvironmentSecret`). */
18
+ putEnvironmentSecret(environmentId: string, name: string, body: PutEnvironmentSecretRequest, actor: string): Promise<EnvironmentSecretView>;
19
19
  putHostModel(body: PutHostModelRequest): Promise<HostModelView>;
20
20
  selectHostModel(body: SelectHostModelRequest): Promise<HostModelView>;
21
+ /**
22
+ * Environment `from`'s secrets sealed again for a fork's new vault `vaultId`, limited to
23
+ * `slots` when given (`VaultService.sealEnvironmentSecretCopies`, Q15). Writes nothing: the
24
+ * fork's transaction inserts the copies; call it before that transaction opens. `null`, not
25
+ * `undefined`, crosses the wire.
26
+ */
27
+ sealEnvironmentSecretCopies(from: string, vaultId: string, slots: Readonly<Record<string, Record<string, unknown>>> | null): Promise<SealedSecretCopies>;
21
28
  /** Signs with the current key, creating the Tenant's keys if it has none. */
22
29
  sign(request: SignRequest): Promise<Signed>;
23
30
  /** Rotates the signing keys (`SigningKeys.rotate`); 409 while the previous key may verify live tokens. */
@@ -28,7 +35,7 @@ export interface Keys {
28
35
  /** Creates the current and standby keys when the Tenant has none. */
29
36
  ensureSigningKeys(): Promise<void>;
30
37
  /**
31
- * Lists a remote MCP server's tools with the installation vault's credential for its URL
38
+ * Lists a remote MCP server's tools with the Environment's credential for its URL
32
39
  * (R2b C12, `mcp/preview.ts`); never calls a tool.
33
40
  */
34
41
  previewMcp(body: McpPreviewRequest): Promise<McpPreview>;
@@ -41,7 +48,7 @@ export interface Keys {
41
48
  signEgressLeaf(host: string, publicKey: string): Promise<string>;
42
49
  }
43
50
  /** The operations, by the name the HTTP route carries. */
44
- export declare const KEYS_OPERATIONS: readonly ["createCredential", "rotateCredential", "putHostModel", "selectHostModel", "sign", "rotateSigningKeys", "ensureSigningKeys", "previewMcp", "egressCa", "signEgressLeaf"];
51
+ export declare const KEYS_OPERATIONS: readonly ["putEnvironmentSecret", "putHostModel", "selectHostModel", "sealEnvironmentSecretCopies", "sign", "rotateSigningKeys", "ensureSigningKeys", "previewMcp", "egressCa", "signEgressLeaf"];
45
52
  export type KeysOperation = (typeof KEYS_OPERATIONS)[number];
46
53
  export interface InProcessKeysOptions {
47
54
  readonly store: SessionStore;
package/dist/keys/keys.js CHANGED
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * The keys seam (blueprint §14, F4.2): everything that needs the vault key. Vault writes that
3
- * touch a secret (creating and rotating a credential, setting and selecting the host model;
4
- * rotation and selection decrypt the old secret first) and all token signing (capability
3
+ * touch a secret (writing an Environment's secret, setting and selecting the host model;
4
+ * rotation and selection decrypt the old secret first; sealing a fork's copies of its source's
5
+ * secrets, which the fork's transaction then inserts) and all token signing (capability
5
6
  * links, run and host tokens, signing-key rotation) run here, whole, so the process that calls them never
6
7
  * holds the key or a private signing key.
7
8
  *
8
- * A tool preview (R2b C12, `previewMcp`) runs here too: it sends an installation vault's
9
- * credential to the server it lists, so the process that reads the plaintext makes the request.
9
+ * A tool preview (R2b C12, `previewMcp`) runs here too: it sends an Environment's credential to
10
+ * the server it lists, so the process that reads the plaintext makes the request.
10
11
  *
11
12
  * Two implementations: `inProcessKeys` (embedding, the ephemeral Runtime, tests, and the gates
12
13
  * service itself, which serves the HTTP routes with it), and `httpKeys` (`client.ts`), the
@@ -18,10 +19,10 @@ import { previewMcpServer } from "../mcp/preview.js";
18
19
  import { signingKeyView } from "../tenant/signing-keys.js";
19
20
  /** The operations, by the name the HTTP route carries. */
20
21
  export const KEYS_OPERATIONS = [
21
- "createCredential",
22
- "rotateCredential",
22
+ "putEnvironmentSecret",
23
23
  "putHostModel",
24
24
  "selectHostModel",
25
+ "sealEnvironmentSecretCopies",
25
26
  "sign",
26
27
  "rotateSigningKeys",
27
28
  "ensureSigningKeys",
@@ -33,10 +34,10 @@ export function inProcessKeys(options) {
33
34
  const { store, vault, signingKeys, kek } = options;
34
35
  const egress = new EgressAuthority(store, kek);
35
36
  return {
36
- createCredential: (vaultId, body) => vault.createCredential(vaultId, body),
37
- rotateCredential: (vaultId, id, body) => vault.rotateCredential(vaultId, id, body),
37
+ putEnvironmentSecret: (environmentId, name, body, actor) => vault.putEnvironmentSecret(environmentId, name, body, actor),
38
38
  putHostModel: (body) => vault.putHostModel(body),
39
39
  selectHostModel: (body) => vault.selectHostModel(body),
40
+ sealEnvironmentSecretCopies: (from, vaultId, slots) => vault.sealEnvironmentSecretCopies(from, vaultId, slots),
40
41
  async sign(request) {
41
42
  const key = kek();
42
43
  const row = await store.tx((t) => signingKeys.ensure(t, key));