@nylorun/runtime 0.26.0-beta → 0.28.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 (379) hide show
  1. package/CHANGELOG.md +171 -0
  2. package/README.md +18 -14
  3. package/dist/adapters/execution/restate.d.ts +15 -18
  4. package/dist/adapters/execution/restate.js +31 -27
  5. package/dist/adapters/replication/pgoutput.d.ts +1 -1
  6. package/dist/adapters/replication/pgoutput.js +3 -2
  7. package/dist/adapters/sandbox/local.js +8 -5
  8. package/dist/adapters/sandbox/virtual.js +2 -2
  9. package/dist/adapters/streams/s2.d.ts +1 -1
  10. package/dist/adapters/streams/s2.js +18 -14
  11. package/dist/api/a2a/card.d.ts +4 -3
  12. package/dist/api/a2a/card.js +4 -4
  13. package/dist/api/a2a/endpoint.js +3 -4
  14. package/dist/api/a2a/protocol.js +4 -0
  15. package/dist/api/a2a/routes.d.ts +2 -6
  16. package/dist/api/a2a/routes.js +30 -25
  17. package/dist/api/ag-ui/endpoint.js +2 -3
  18. package/dist/api/ag-ui/routes.js +8 -11
  19. package/dist/api/components.d.ts +6789 -3405
  20. package/dist/api/components.js +43 -30
  21. package/dist/api/gate/keys.js +7 -4
  22. package/dist/api/gate/routes.d.ts +6 -2
  23. package/dist/api/gate/routes.js +15 -11
  24. package/dist/api/http/app.js +9 -4
  25. package/dist/api/http/body.d.ts +17 -3
  26. package/dist/api/http/body.js +35 -7
  27. package/dist/api/http/respond.js +6 -2
  28. package/dist/api/http/routes/access.js +5 -4
  29. package/dist/api/http/routes/agent-manifests.d.ts +10 -0
  30. package/dist/api/http/routes/agent-manifests.js +135 -0
  31. package/dist/api/http/routes/agents.d.ts +11 -0
  32. package/dist/api/http/routes/agents.js +418 -0
  33. package/dist/api/http/routes/artifacts.js +21 -9
  34. package/dist/api/http/routes/coverage.d.ts +4 -4
  35. package/dist/api/http/routes/coverage.js +13 -7
  36. package/dist/api/http/routes/files.d.ts +2 -1
  37. package/dist/api/http/routes/files.js +31 -5
  38. package/dist/api/http/routes/mcp.js +3 -3
  39. package/dist/api/http/routes/me.d.ts +1 -1
  40. package/dist/api/http/routes/me.js +1 -2
  41. package/dist/api/http/routes/memories.d.ts +9 -0
  42. package/dist/api/http/routes/memories.js +152 -0
  43. package/dist/api/http/routes/reads.d.ts +10 -111
  44. package/dist/api/http/routes/reads.js +51 -29
  45. package/dist/api/http/routes/sessions.js +26 -54
  46. package/dist/api/http/routes/tenant.d.ts +2 -2
  47. package/dist/api/http/routes/tenant.js +77 -24
  48. package/dist/api/http/routes/triggers.d.ts +10 -0
  49. package/dist/api/http/routes/triggers.js +180 -0
  50. package/dist/api/inbound-files.d.ts +3 -9
  51. package/dist/api/inbound-files.js +18 -24
  52. package/dist/api/openapi.js +73 -47
  53. package/dist/artifacts/disk.d.ts +10 -14
  54. package/dist/artifacts/disk.js +99 -95
  55. package/dist/artifacts/files.d.ts +13 -4
  56. package/dist/artifacts/files.js +27 -9
  57. package/dist/artifacts/folder-upload.d.ts +75 -0
  58. package/dist/artifacts/folder-upload.js +263 -0
  59. package/dist/artifacts/folders.d.ts +14 -8
  60. package/dist/artifacts/folders.js +19 -11
  61. package/dist/artifacts/links.js +7 -6
  62. package/dist/artifacts/parts.d.ts +9 -14
  63. package/dist/artifacts/parts.js +11 -9
  64. package/dist/artifacts/pipe.d.ts +15 -0
  65. package/dist/artifacts/pipe.js +71 -0
  66. package/dist/artifacts/quota.d.ts +36 -0
  67. package/dist/artifacts/quota.js +44 -0
  68. package/dist/artifacts/service.d.ts +25 -1
  69. package/dist/artifacts/service.js +72 -27
  70. package/dist/blob/fs.js +2 -1
  71. package/dist/blob/s3.d.ts +6 -2
  72. package/dist/blob/s3.js +9 -7
  73. package/dist/blob/types.d.ts +1 -1
  74. package/dist/blob/types.js +1 -1
  75. package/dist/core/flow-host.d.ts +0 -1
  76. package/dist/core/limits.d.ts +11 -35
  77. package/dist/core/limits.js +5 -73
  78. package/dist/core/sandbox-routes.js +0 -1
  79. package/dist/core/turn-manifest.d.ts +1 -2
  80. package/dist/core/turn-manifest.js +4 -28
  81. package/dist/execution/memory.d.ts +8 -5
  82. package/dist/execution/memory.js +8 -6
  83. package/dist/execution/types.d.ts +1 -6
  84. package/dist/execution/types.js +0 -5
  85. package/dist/gates/contract.d.ts +3 -2
  86. package/dist/gates/contract.js +1 -1
  87. package/dist/gates/egress-credentials.js +5 -4
  88. package/dist/gates/egress.d.ts +13 -13
  89. package/dist/gates/egress.js +19 -21
  90. package/dist/gates/fixture-model.js +13 -2
  91. package/dist/gates/handler.d.ts +1 -3
  92. package/dist/gates/handler.js +4 -3
  93. package/dist/gates/http-client.d.ts +4 -3
  94. package/dist/gates/http-client.js +5 -7
  95. package/dist/gates/http-tool.d.ts +24 -4
  96. package/dist/gates/http-tool.js +41 -13
  97. package/dist/gates/in-process.d.ts +2 -1
  98. package/dist/gates/in-process.js +17 -9
  99. package/dist/gates/inflight.d.ts +2 -2
  100. package/dist/gates/inflight.js +3 -2
  101. package/dist/gates/journal.js +5 -7
  102. package/dist/gates/mcp-handler.d.ts +4 -3
  103. package/dist/gates/mcp-handler.js +15 -5
  104. package/dist/gates/meter.js +6 -5
  105. package/dist/gates/model-gate.d.ts +11 -2
  106. package/dist/gates/policy.d.ts +34 -6
  107. package/dist/gates/policy.js +66 -18
  108. package/dist/gates/provider-gate.d.ts +22 -0
  109. package/dist/gates/provider-gate.js +30 -0
  110. package/dist/gates/tool-calls.d.ts +1 -3
  111. package/dist/gates/tool-calls.js +2 -3
  112. package/dist/gates/tool-client.d.ts +1 -1
  113. package/dist/gates/tool-client.js +5 -6
  114. package/dist/gates/tool-contract.d.ts +8 -5
  115. package/dist/gates/tool-contract.js +1 -1
  116. package/dist/gates/tool-gate.d.ts +18 -7
  117. package/dist/gates/tool-gate.js +49 -7
  118. package/dist/harness/calls.d.ts +27 -25
  119. package/dist/harness/calls.js +40 -46
  120. package/dist/harness/executors.d.ts +10 -3
  121. package/dist/harness/executors.js +53 -23
  122. package/dist/harness/main.js +2 -2
  123. package/dist/harness/pod.d.ts +2 -2
  124. package/dist/harness/pod.js +7 -6
  125. package/dist/harness/run-tokens.d.ts +4 -1
  126. package/dist/harness/run-tokens.js +6 -2
  127. package/dist/harness/service.d.ts +17 -14
  128. package/dist/harness/service.js +21 -35
  129. package/dist/harness/setup.d.ts +0 -2
  130. package/dist/harness/setup.js +21 -15
  131. package/dist/harness/ws-client.d.ts +2 -2
  132. package/dist/harness/ws-client.js +7 -5
  133. package/dist/harness/ws-port.d.ts +3 -4
  134. package/dist/harness/ws-port.js +3 -4
  135. package/dist/harness-api/artifacts.d.ts +20 -0
  136. package/dist/harness-api/artifacts.js +478 -0
  137. package/dist/harness-api/in-process.d.ts +2 -2
  138. package/dist/harness-api/in-process.js +18 -16
  139. package/dist/harness-api/record.d.ts +10 -9
  140. package/dist/harness-api/record.js +113 -157
  141. package/dist/harness-api/renew.d.ts +1 -1
  142. package/dist/harness-api/renew.js +2 -2
  143. package/dist/harness-api/server.d.ts +1 -16
  144. package/dist/harness-api/server.js +86 -73
  145. package/dist/harness-api/start.d.ts +27 -5
  146. package/dist/harness-api/start.js +102 -19
  147. package/dist/harness-api/tool-journal.d.ts +15 -10
  148. package/dist/harness-api/tool-journal.js +26 -24
  149. package/dist/harness-api/workspace.d.ts +0 -4
  150. package/dist/harness-api/workspace.js +4 -47
  151. package/dist/harness-api/ws-server.js +10 -10
  152. package/dist/host/create-host.js +2 -3
  153. package/dist/host/execution.js +2 -3
  154. package/dist/host/gates.d.ts +2 -9
  155. package/dist/host/gates.js +15 -12
  156. package/dist/host/http.d.ts +2 -4
  157. package/dist/host/http.js +5 -29
  158. package/dist/host/main.js +10 -3
  159. package/dist/host/operate.js +12 -3
  160. package/dist/host/stack-config.d.ts +11 -0
  161. package/dist/host/stack-config.js +21 -1
  162. package/dist/index.d.ts +0 -1
  163. package/dist/index.js +0 -1
  164. package/dist/infra/index.js +2 -1
  165. package/dist/infra/readiness.d.ts +0 -2
  166. package/dist/infra/readiness.js +4 -3
  167. package/dist/keys/client.d.ts +1 -0
  168. package/dist/keys/client.js +2 -3
  169. package/dist/keys/contract.d.ts +0 -2
  170. package/dist/keys/contract.js +0 -2
  171. package/dist/keys/egress-ca.d.ts +0 -2
  172. package/dist/keys/egress-ca.js +2 -3
  173. package/dist/keys/keys.d.ts +15 -4
  174. package/dist/keys/keys.js +22 -1
  175. package/dist/keys/webhooks.d.ts +25 -0
  176. package/dist/keys/webhooks.js +62 -0
  177. package/dist/keys/x509.d.ts +8 -4
  178. package/dist/keys/x509.js +14 -5
  179. package/dist/management-openapi.json +3043 -1954
  180. package/dist/mcp/connect.d.ts +35 -26
  181. package/dist/mcp/connect.js +112 -141
  182. package/dist/mcp/pool.d.ts +27 -22
  183. package/dist/mcp/pool.js +34 -48
  184. package/dist/mcp/preview.d.ts +12 -5
  185. package/dist/mcp/preview.js +43 -26
  186. package/dist/mcp/snapshot.d.ts +35 -86
  187. package/dist/mcp/snapshot.js +93 -135
  188. package/dist/memory/service.d.ts +97 -0
  189. package/dist/memory/service.js +335 -0
  190. package/dist/memory/tool.d.ts +36 -0
  191. package/dist/memory/tool.js +84 -0
  192. package/dist/model/auth-store.js +5 -3
  193. package/dist/model/capabilities.d.ts +1 -1
  194. package/dist/model/catalog.d.ts +9 -36
  195. package/dist/model/catalog.js +6 -33
  196. package/dist/model/descriptor.d.ts +51 -0
  197. package/dist/model/descriptor.js +83 -0
  198. package/dist/model/models.d.ts +0 -3
  199. package/dist/model/models.js +3 -5
  200. package/dist/model/native-blocks.js +1 -1
  201. package/dist/model/pi-model.d.ts +16 -5
  202. package/dist/model/pi-model.js +86 -105
  203. package/dist/openapi.json +13653 -9986
  204. package/dist/reads/cursor.js +3 -1
  205. package/dist/reads/types.d.ts +3 -3
  206. package/dist/record/agent.d.ts +27 -0
  207. package/dist/record/agent.js +46 -0
  208. package/dist/record/append.d.ts +34 -2
  209. package/dist/record/append.js +50 -1
  210. package/dist/record/index.d.ts +2 -2
  211. package/dist/record/index.js +2 -2
  212. package/dist/redact.d.ts +1 -1
  213. package/dist/redact.js +3 -2
  214. package/dist/sandbox/egress-token.d.ts +3 -3
  215. package/dist/sandbox/egress-token.js +3 -3
  216. package/dist/sandbox/join.js +8 -7
  217. package/dist/sandbox/manager.d.ts +41 -41
  218. package/dist/sandbox/manager.js +112 -44
  219. package/dist/sandbox/placement.d.ts +2 -2
  220. package/dist/sandbox/placement.js +7 -10
  221. package/dist/sandbox/pods/client.d.ts +1 -10
  222. package/dist/sandbox/pods/client.js +8 -4
  223. package/dist/sandbox/pods/lifecycle.d.ts +25 -17
  224. package/dist/sandbox/pods/lifecycle.js +51 -29
  225. package/dist/sandbox/pods/network-gate.d.ts +4 -5
  226. package/dist/sandbox/pods/network-gate.js +7 -7
  227. package/dist/sandbox/pods/reconcile.js +14 -3
  228. package/dist/sandbox/pods/spec.js +8 -5
  229. package/dist/sandbox/policy.d.ts +7 -1
  230. package/dist/sandbox/policy.js +10 -3
  231. package/dist/sandbox/resolve.d.ts +11 -6
  232. package/dist/sandbox/resolve.js +19 -9
  233. package/dist/sandbox/setup.d.ts +6 -2
  234. package/dist/sandbox/setup.js +47 -14
  235. package/dist/sandbox/share.d.ts +5 -14
  236. package/dist/sandbox/share.js +5 -34
  237. package/dist/sandbox/skills.d.ts +7 -13
  238. package/dist/sandbox/skills.js +16 -31
  239. package/dist/sandbox/tenant-config.d.ts +2 -2
  240. package/dist/sandbox/tenant-config.js +4 -3
  241. package/dist/sandbox/tools.d.ts +7 -1
  242. package/dist/sandbox/tools.js +26 -26
  243. package/dist/sandbox/types.d.ts +10 -3
  244. package/dist/sandbox/types.js +9 -0
  245. package/dist/store/postgres/connect.d.ts +6 -5
  246. package/dist/store/postgres/connect.js +14 -11
  247. package/dist/store/postgres/control.js +13 -13
  248. package/dist/store/postgres/drizzle/0028_artifact_write_keys.sql +2 -0
  249. package/dist/store/postgres/drizzle/0029_protocol_15.sql +11 -0
  250. package/dist/store/postgres/drizzle/0030_protocol_15_runtime_block.sql +11 -0
  251. package/dist/store/postgres/drizzle/0031_protocol_15_mcp_snapshot.sql +8 -0
  252. package/dist/store/postgres/drizzle/0032_agents.sql +54 -0
  253. package/dist/store/postgres/drizzle/0033_agent_versions.sql +7 -0
  254. package/dist/store/postgres/drizzle/0034_protocol_17_session_queue.sql +13 -0
  255. package/dist/store/postgres/drizzle/0035_t4_triggers.sql +41 -0
  256. package/dist/store/postgres/drizzle/0036_t5_memories.sql +30 -0
  257. package/dist/store/postgres/drizzle/meta/_journal.json +63 -0
  258. package/dist/store/postgres/reads.js +17 -82
  259. package/dist/store/postgres/record-writer.d.ts +4 -4
  260. package/dist/store/postgres/record-writer.js +9 -9
  261. package/dist/store/postgres/schema.d.ts +1382 -151
  262. package/dist/store/postgres/schema.js +191 -37
  263. package/dist/store/postgres/store.js +384 -105
  264. package/dist/store/postgres/tenant.js +1 -2
  265. package/dist/store/types.d.ts +172 -57
  266. package/dist/store/types.js +0 -1
  267. package/dist/streams/relay/core.d.ts +3 -3
  268. package/dist/streams/relay/core.js +9 -10
  269. package/dist/tenant/advance.js +41 -39
  270. package/dist/tenant/agent-manifests.d.ts +72 -0
  271. package/dist/tenant/agent-manifests.js +359 -0
  272. package/dist/tenant/agent-versions.d.ts +126 -0
  273. package/dist/tenant/agent-versions.js +389 -0
  274. package/dist/tenant/agents.d.ts +230 -0
  275. package/dist/tenant/{environments.js → agents.js} +493 -328
  276. package/dist/tenant/auth.d.ts +1 -1
  277. package/dist/tenant/auth.js +3 -3
  278. package/dist/tenant/commands.d.ts +63 -2
  279. package/dist/tenant/commands.js +240 -90
  280. package/dist/tenant/context.d.ts +57 -33
  281. package/dist/tenant/context.js +6 -2
  282. package/dist/tenant/definition-files.d.ts +25 -4
  283. package/dist/tenant/definition-files.js +85 -47
  284. package/dist/tenant/effects.js +15 -51
  285. package/dist/tenant/ephemeral.js +2 -1
  286. package/dist/tenant/history.d.ts +1 -1
  287. package/dist/tenant/history.js +1 -1
  288. package/dist/tenant/host-token.d.ts +3 -3
  289. package/dist/tenant/host-token.js +11 -10
  290. package/dist/tenant/http.d.ts +5 -8
  291. package/dist/tenant/http.js +9 -0
  292. package/dist/tenant/identity-file.d.ts +29 -6
  293. package/dist/tenant/identity-file.js +45 -15
  294. package/dist/tenant/issuers.d.ts +5 -5
  295. package/dist/tenant/issuers.js +34 -26
  296. package/dist/tenant/jwt.d.ts +8 -2
  297. package/dist/tenant/jwt.js +10 -2
  298. package/dist/tenant/limits.d.ts +87 -0
  299. package/dist/tenant/limits.js +172 -0
  300. package/dist/tenant/memories.d.ts +38 -0
  301. package/dist/tenant/memories.js +119 -0
  302. package/dist/tenant/module.d.ts +4 -1
  303. package/dist/tenant/module.js +9 -8
  304. package/dist/tenant/outbound.d.ts +11 -14
  305. package/dist/tenant/outbound.js +10 -15
  306. package/dist/tenant/reset.js +2 -2
  307. package/dist/tenant/run-grants.d.ts +6 -0
  308. package/dist/tenant/run-grants.js +7 -2
  309. package/dist/tenant/run-token.d.ts +0 -4
  310. package/dist/tenant/run-token.js +6 -11
  311. package/dist/tenant/runtime-limits.d.ts +24 -0
  312. package/dist/tenant/runtime-limits.js +27 -0
  313. package/dist/tenant/runtime.d.ts +1 -1
  314. package/dist/tenant/runtime.js +38 -43
  315. package/dist/tenant/session-streams.js +7 -6
  316. package/dist/tenant/session.d.ts +1 -1
  317. package/dist/tenant/session.js +1 -1
  318. package/dist/tenant/sessions.d.ts +28 -45
  319. package/dist/tenant/sessions.js +89 -209
  320. package/dist/tenant/signing-keys.d.ts +1 -1
  321. package/dist/tenant/signing-keys.js +5 -6
  322. package/dist/tenant/status.js +10 -10
  323. package/dist/tenant/streams.js +18 -20
  324. package/dist/tenant/sweep.d.ts +1 -21
  325. package/dist/tenant/sweep.js +51 -51
  326. package/dist/tenant/transcript.d.ts +70 -23
  327. package/dist/tenant/transcript.js +28 -22
  328. package/dist/tenant/triggers.d.ts +143 -0
  329. package/dist/tenant/triggers.js +715 -0
  330. package/dist/tenant/turn-timing.d.ts +11 -1
  331. package/dist/tenant/turn-timing.js +3 -1
  332. package/dist/tenant/types.d.ts +9 -14
  333. package/dist/tenant/worker.d.ts +4 -10
  334. package/dist/tenant/worker.js +10 -14
  335. package/dist/traces/ids.js +5 -1
  336. package/dist/traces/map.d.ts +10 -7
  337. package/dist/traces/map.js +16 -12
  338. package/dist/traces/otlp.d.ts +3 -3
  339. package/dist/traces/otlp.js +5 -3
  340. package/dist/traces/projector.d.ts +9 -4
  341. package/dist/traces/projector.js +13 -7
  342. package/dist/traces/read.js +6 -1
  343. package/dist/tuning.d.ts +390 -0
  344. package/dist/tuning.js +677 -0
  345. package/dist/vault/headers.js +4 -2
  346. package/dist/vault/service.d.ts +13 -8
  347. package/dist/vault/service.js +61 -17
  348. package/dist/version.d.ts +1 -1
  349. package/dist/version.js +1 -1
  350. package/package.json +13 -12
  351. package/dist/api/http/routes/environment-manifests.d.ts +0 -9
  352. package/dist/api/http/routes/environment-manifests.js +0 -118
  353. package/dist/api/http/routes/environments.d.ts +0 -11
  354. package/dist/api/http/routes/environments.js +0 -332
  355. package/dist/artifacts/export.d.ts +0 -8
  356. package/dist/artifacts/export.js +0 -204
  357. package/dist/artifacts/media-types.d.ts +0 -30
  358. package/dist/artifacts/media-types.js +0 -137
  359. package/dist/artifacts/workspace.d.ts +0 -60
  360. package/dist/artifacts/workspace.js +0 -45
  361. package/dist/media.d.ts +0 -10
  362. package/dist/media.js +0 -58
  363. package/dist/record/environment.d.ts +0 -25
  364. package/dist/record/environment.js +0 -44
  365. package/dist/sandbox/runtime-tools.d.ts +0 -9
  366. package/dist/sandbox/runtime-tools.js +0 -16
  367. package/dist/sandbox/session-sandbox.d.ts +0 -16
  368. package/dist/sandbox/session-sandbox.js +0 -77
  369. package/dist/tenant/artifact-tool.d.ts +0 -13
  370. package/dist/tenant/artifact-tool.js +0 -243
  371. package/dist/tenant/environment-manifests.d.ts +0 -57
  372. package/dist/tenant/environment-manifests.js +0 -200
  373. package/dist/tenant/environments.d.ts +0 -174
  374. package/dist/tenant/skill-tool.d.ts +0 -20
  375. package/dist/tenant/skill-tool.js +0 -67
  376. package/dist/tenant/tool-results.d.ts +0 -57
  377. package/dist/tenant/tool-results.js +0 -298
  378. package/dist/tenant/tool-search.d.ts +0 -39
  379. package/dist/tenant/tool-search.js +0 -103
@@ -41,11 +41,14 @@
41
41
  * `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`, with `OTEL_EXPORTER_OTLP_HEADERS`), what they say of
42
42
  * their source (`OTEL_SERVICE_NAME`, `OTEL_RESOURCE_ATTRIBUTES`) and which content they carry
43
43
  * (`NYLORUN_TRACE_CONTENT`, or all of it with `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`).
44
+ * So is the Host tuning (`NYLORUN_TUNING`, `tuning.ts`), for every service: its timings,
45
+ * retries and internal sizes.
44
46
  */
45
47
  import { DEFAULT_SERVICE_NAME } from "../traces/otlp.js";
46
48
  import { TRACE_CONTENT_KINDS } from "../traces/map.js";
47
49
  import { isTenantId } from "@nylorun/core/compatibility";
48
50
  import { blockedAddresses } from "../sandbox/pods/network-gate.js";
51
+ import { HostTuningError, parseHostTuning } from "../tuning.js";
49
52
  export const RUNTIME_SERVICES = ["core", "loop", "gates", "keys", "egress", "harness"];
50
53
  /**
51
54
  * Services that may share a process (D12): they hold the same secrets and parse the same
@@ -254,8 +257,9 @@ function parseIdentityKeys(env) {
254
257
  /** Parse the stack configuration; throws `StackConfigError` naming the variable. */
255
258
  export function parseStackConfig(env, argv) {
256
259
  const { services, deprecatedRole } = parseServices(argv);
260
+ const tuning = parseTuning(env);
257
261
  if (services.has("harness"))
258
- return { services, harness: parseHarnessService(env), endpoints: {} };
262
+ return { services, harness: parseHarnessService(env), endpoints: {}, ...(tuning ? { tuning } : {}) };
259
263
  // The image sets NYLORUN_LISTEN_*: only the API's processes read them.
260
264
  const servesApi = services.has("core") || services.has("loop");
261
265
  const listen = servesApi ? parseListen(env) : undefined;
@@ -345,8 +349,24 @@ export function parseStackConfig(env, argv) {
345
349
  ...(tenant ? { tenant } : {}),
346
350
  ...(objectStore ? { objectStore } : {}),
347
351
  ...(traces ? { traces } : {}),
352
+ ...(tuning ? { tuning } : {}),
348
353
  };
349
354
  }
355
+ /** `StackConfig.tuning` from `NYLORUN_TUNING`, or undefined when it is unset. */
356
+ function parseTuning(env) {
357
+ const raw = read(env, "NYLORUN_TUNING");
358
+ if (raw === undefined)
359
+ return undefined;
360
+ try {
361
+ const { tuning, overrides } = parseHostTuning(raw);
362
+ return { values: tuning, overrides: overrides ?? {} };
363
+ }
364
+ catch (error) {
365
+ if (error instanceof HostTuningError)
366
+ throw new StackConfigError(error.message);
367
+ throw error;
368
+ }
369
+ }
350
370
  /**
351
371
  * `StackConfig.traces` (R7.2). The OTLP endpoint is the collector's base URL, to which
352
372
  * `/v1/traces` is added; `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` is the full URL and wins. Headers
package/dist/index.d.ts CHANGED
@@ -5,7 +5,6 @@ export { createTrustedIssuers, type TrustedIssuers, type TrustedIssuersOptions,
5
5
  export type { TenantConfig, TenantHandle, TenantPaths, Logger, OpenTenantRuntime, } from "./tenant/types.js";
6
6
  export { scriptedModel, gatewayModel, type ModelProvider } from "./core/provider.js";
7
7
  export type { RuntimeAgent } from "./contracts.js";
8
- export { IMAGE_MEDIA_TYPES, MAX_IMAGE_BYTES, decodeImageBase64, validateImageBytes, } from "./media.js";
9
8
  export type { RuntimeModelAdapter } from "./contracts.js";
10
9
  export { baselineEnvironment, hostProcessEnvironment, } from "./host/environment.js";
11
10
  export type { HostProcessPathInputs } from "./host/environment.js";
package/dist/index.js CHANGED
@@ -3,5 +3,4 @@ export { startEphemeralRuntime, } from "./tenant/ephemeral.js";
3
3
  export { parseIdentityFile, IdentityFileError, } from "./tenant/identity-file.js";
4
4
  export { createTrustedIssuers, } from "./tenant/issuers.js";
5
5
  export { scriptedModel, gatewayModel } from "./core/provider.js";
6
- export { IMAGE_MEDIA_TYPES, MAX_IMAGE_BYTES, decodeImageBase64, validateImageBytes, } from "./media.js";
7
6
  export { baselineEnvironment, hostProcessEnvironment, } from "./host/environment.js";
@@ -2,6 +2,7 @@ import { createDatabase } from "./database.js";
2
2
  import { createExecution, executionKind } from "./execution.js";
3
3
  import { createReadiness, infraProbes } from "./readiness.js";
4
4
  import { createStreams, streamsKind } from "./streams.js";
5
+ import { hostTuning } from "../tuning.js";
5
6
  export { createDatabase, probeDatabase } from "./database.js";
6
7
  export { createExecution, probeExecution, WORKER_LISTEN } from "./execution.js";
7
8
  export { createReadiness, infraProbes } from "./readiness.js";
@@ -30,7 +31,7 @@ export function createInfra(config, options = {}) {
30
31
  ...(streams ? { streams } : {}),
31
32
  ...(readiness ? { readiness } : {}),
32
33
  async close() {
33
- await Promise.allSettled([database?.end({ timeout: 5 }), streams?.close()]);
34
+ await Promise.allSettled([database?.end({ timeout: hostTuning().postgres.closeTimeoutSeconds }), streams?.close()]);
34
35
  },
35
36
  };
36
37
  }
@@ -21,8 +21,6 @@ export interface ReadinessReport {
21
21
  errors: Record<string, string>;
22
22
  }
23
23
  export type Readiness = () => Promise<ReadinessReport>;
24
- /** Default bound on one readiness pass. Compose probes `/ready` every 2 s with a 5 s timeout. */
25
- export declare const READINESS_TIMEOUT_MS = 2000;
26
24
  export declare function createReadiness(probes: Readonly<Record<string, Probe>>, options?: {
27
25
  timeoutMs?: number;
28
26
  }): Readiness;
@@ -1,9 +1,10 @@
1
1
  import { probeDatabase } from "./database.js";
2
2
  import { probeExecution } from "./execution.js";
3
- /** Default bound on one readiness pass. Compose probes `/ready` every 2 s with a 5 s timeout. */
4
- export const READINESS_TIMEOUT_MS = 2000;
3
+ import { hostTuning } from "../tuning.js";
5
4
  export function createReadiness(probes, options = {}) {
6
- const timeoutMs = options.timeoutMs ?? READINESS_TIMEOUT_MS;
5
+ // The bound on one pass: `host.readinessTimeoutMs` (Host tuning). Compose probes `/ready` every
6
+ // 2 s with a 5 s timeout.
7
+ const timeoutMs = options.timeoutMs ?? hostTuning().host.readinessTimeoutMs;
7
8
  return async () => {
8
9
  const entries = Object.entries(probes);
9
10
  const results = await Promise.all(entries.map(async ([name, probe]) => [name, await runProbe(probe, timeoutMs)]));
@@ -4,6 +4,7 @@ export interface HttpKeysOptions {
4
4
  readonly url: string;
5
5
  /** `NYLORUN_GATES_TOKEN`. */
6
6
  readonly token: string;
7
+ /** Default `keys.timeoutMs` (Host tuning): signing and vault writes are short. */
7
8
  readonly timeoutMs?: number;
8
9
  }
9
10
  export declare function httpKeys(options: HttpKeysOptions): Keys;
@@ -10,12 +10,11 @@ import { HttpError } from "../tenant/http.js";
10
10
  import { VaultError } from "../vault/error.js";
11
11
  import { KEYS_PATH } from "./contract.js";
12
12
  import { KEYS_OPERATIONS } from "./keys.js";
13
- /** Signing and vault writes are short; anything slower is a gateway in trouble. */
14
- const KEYS_TIMEOUT_MS = 30_000;
13
+ import { hostTuning } from "../tuning.js";
15
14
  export function httpKeys(options) {
16
15
  const where = new URL(options.url).origin;
17
16
  function call(operation, args) {
18
- const timeoutMs = options.timeoutMs ?? KEYS_TIMEOUT_MS;
17
+ const timeoutMs = options.timeoutMs ?? hostTuning().keys.timeoutMs;
19
18
  const url = new URL(`${KEYS_PATH}/${operation}`, options.url);
20
19
  const send = url.protocol === "https:" ? httpsRequest : httpRequest;
21
20
  const payload = Buffer.from(JSON.stringify({ args }), "utf8");
@@ -8,8 +8,6 @@
8
8
  * exactly what it answered when the operation ran in its own process.
9
9
  */
10
10
  export declare const KEYS_PATH = "/nylorun/v1/keys";
11
- /** Largest request body: a credential with its secret, or a token's claims. */
12
- export declare const MAX_KEYS_BODY_BYTES: number;
13
11
  export interface KeysError {
14
12
  readonly kind: "vault" | "http";
15
13
  readonly status: number;
@@ -8,5 +8,3 @@
8
8
  * exactly what it answered when the operation ran in its own process.
9
9
  */
10
10
  export const KEYS_PATH = "/nylorun/v1/keys";
11
- /** Largest request body: a credential with its secret, or a token's claims. */
12
- export const MAX_KEYS_BODY_BYTES = 1024 * 1024;
@@ -1,6 +1,4 @@
1
1
  import type { SessionStore } from "../store/types.js";
2
- /** How long a leaf egress-gate presents is valid: one connection rarely lasts this long. */
3
- export declare const EGRESS_LEAF_MINUTES = 60;
4
2
  export declare class EgressAuthority {
5
3
  private readonly store;
6
4
  private readonly kek;
@@ -10,8 +10,6 @@ import { X509Certificate } from "node:crypto";
10
10
  import { decryptSecret, encryptSecret } from "../vault/crypto.js";
11
11
  import { createCertificateAuthority, issueLeafCertificate } from "./x509.js";
12
12
  const SETTING = "egress.ca";
13
- /** How long a leaf egress-gate presents is valid: one connection rarely lasts this long. */
14
- export const EGRESS_LEAF_MINUTES = 60;
15
13
  function aad(certificate) {
16
14
  return Buffer.from(`nylorun.egress-ca:${new X509Certificate(certificate).fingerprint256}`);
17
15
  }
@@ -30,7 +28,8 @@ export class EgressAuthority {
30
28
  }
31
29
  /** A leaf for `host` and the caller's P-256 public key (SPKI PEM); the caller keeps its key. */
32
30
  async signLeaf(host, publicKey) {
33
- return issueLeafCertificate({ authority: await this.load(), host, publicKey, minutes: EGRESS_LEAF_MINUTES });
31
+ // Valid for `keys.leafLifetimeMs` (Host tuning): one connection rarely lasts this long.
32
+ return issueLeafCertificate({ authority: await this.load(), host, publicKey });
34
33
  }
35
34
  async load() {
36
35
  if (this.authority)
@@ -1,4 +1,4 @@
1
- import type { EnvironmentSecretView, HostModelView, McpPreview, McpPreviewRequest, PutEnvironmentSecretRequest, PutHostModelRequest, SelectHostModelRequest, SigningKeyView } from "@nylorun/core/contracts";
1
+ import type { AgentSecretView, HostModelView, McpPreview, McpPreviewRequest, PutAgentSecretRequest, 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";
@@ -13,9 +13,10 @@ export interface Signed {
13
13
  /** The signing key's id, the token's `kid`. */
14
14
  readonly keyId: string;
15
15
  }
16
+ import { type SealedWebhookSecret, type WebhookDelivery } from "./webhooks.js";
16
17
  export interface Keys {
17
- /** `PUT /v1/environments/{id}/secrets/{name}` (`VaultService.putEnvironmentSecret`). */
18
- putEnvironmentSecret(environmentId: string, name: string, body: PutEnvironmentSecretRequest, actor: string): Promise<EnvironmentSecretView>;
18
+ /** `PUT /v1/agents/{id}/secrets/{name}` (`VaultService.putEnvironmentSecret`). */
19
+ putEnvironmentSecret(environmentId: string, name: string, body: PutAgentSecretRequest, actor: string): Promise<AgentSecretView>;
19
20
  putHostModel(body: PutHostModelRequest): Promise<HostModelView>;
20
21
  selectHostModel(body: SelectHostModelRequest): Promise<HostModelView>;
21
22
  /**
@@ -46,9 +47,19 @@ export interface Keys {
46
47
  * egress CA (R2c). The leaf's private key never reaches keys; the CA's never leaves it.
47
48
  */
48
49
  signEgressLeaf(host: string, publicKey: string): Promise<string>;
50
+ /**
51
+ * A new Standard Webhooks secret for Agent `agentId`'s trigger `triggerId` (track T4, D75), and
52
+ * the same sealed with the vault key for the trigger's row. The secret leaves keys this once.
53
+ */
54
+ makeWebhookSecret(agentId: string, triggerId: string): Promise<{
55
+ secret: string;
56
+ sealed: SealedWebhookSecret;
57
+ }>;
58
+ /** Whether a delivery to that trigger carries a signature its sealed secret made. */
59
+ verifyWebhook(agentId: string, triggerId: string, sealed: SealedWebhookSecret, delivery: WebhookDelivery): Promise<boolean>;
49
60
  }
50
61
  /** The operations, by the name the HTTP route carries. */
51
- export declare const KEYS_OPERATIONS: readonly ["putEnvironmentSecret", "putHostModel", "selectHostModel", "sealEnvironmentSecretCopies", "sign", "rotateSigningKeys", "ensureSigningKeys", "previewMcp", "egressCa", "signEgressLeaf"];
62
+ export declare const KEYS_OPERATIONS: readonly ["putEnvironmentSecret", "putHostModel", "selectHostModel", "sealEnvironmentSecretCopies", "sign", "rotateSigningKeys", "ensureSigningKeys", "previewMcp", "egressCa", "signEgressLeaf", "makeWebhookSecret", "verifyWebhook"];
52
63
  export type KeysOperation = (typeof KEYS_OPERATIONS)[number];
53
64
  export interface InProcessKeysOptions {
54
65
  readonly store: SessionStore;
package/dist/keys/keys.js CHANGED
@@ -16,7 +16,10 @@
16
16
  import { EgressAuthority } from "./egress-ca.js";
17
17
  import { SignJWT } from "jose";
18
18
  import { previewMcpServer } from "../mcp/preview.js";
19
+ import { readRuntimeLimits } from "../tenant/runtime-limits.js";
19
20
  import { signingKeyView } from "../tenant/signing-keys.js";
21
+ import { makeWebhookSecret, verifyWebhook } from "./webhooks.js";
22
+ import { VaultCryptoError } from "../vault/crypto.js";
20
23
  /** The operations, by the name the HTTP route carries. */
21
24
  export const KEYS_OPERATIONS = [
22
25
  "putEnvironmentSecret",
@@ -29,6 +32,8 @@ export const KEYS_OPERATIONS = [
29
32
  "previewMcp",
30
33
  "egressCa",
31
34
  "signEgressLeaf",
35
+ "makeWebhookSecret",
36
+ "verifyWebhook",
32
37
  ];
33
38
  export function inProcessKeys(options) {
34
39
  const { store, vault, signingKeys, kek } = options;
@@ -56,8 +61,24 @@ export function inProcessKeys(options) {
56
61
  const key = kek();
57
62
  await store.tx((t) => signingKeys.ensure(t, key));
58
63
  },
59
- previewMcp: (body) => previewMcpServer(body, { vault, policy: options.policy ?? {} }),
64
+ previewMcp: async (body) => previewMcpServer(body, {
65
+ vault,
66
+ policy: options.policy ?? {},
67
+ limits: await store.tx((t) => readRuntimeLimits(t)),
68
+ }),
60
69
  egressCa: () => egress.certificate(),
61
70
  signEgressLeaf: (host, publicKey) => egress.signLeaf(host, publicKey),
71
+ makeWebhookSecret: async (agentId, triggerId) => makeWebhookSecret(kek(), agentId, triggerId),
72
+ verifyWebhook: async (agentId, triggerId, sealed, delivery) => {
73
+ try {
74
+ return verifyWebhook(kek(), agentId, triggerId, sealed, delivery);
75
+ }
76
+ catch (error) {
77
+ // A secret sealed with another vault key, or not one: it verifies nothing.
78
+ if (error instanceof VaultCryptoError)
79
+ return false;
80
+ throw error;
81
+ }
82
+ },
62
83
  };
63
84
  }
@@ -0,0 +1,25 @@
1
+ /** A secret sealed with the vault key, as the trigger's row keeps it. */
2
+ export interface SealedWebhookSecret {
3
+ readonly nonce: string;
4
+ readonly ciphertext: string;
5
+ readonly wrappedDek: string;
6
+ readonly kekId: string;
7
+ }
8
+ /** What a delivery says, as it arrived: its headers, and its body in base64. */
9
+ export interface WebhookDelivery {
10
+ readonly id: string;
11
+ readonly timestamp: string;
12
+ readonly signature: string;
13
+ readonly body: string;
14
+ }
15
+ /** A new secret for Agent `agentId`'s trigger `triggerId`, and the same sealed for its row. */
16
+ export declare function makeWebhookSecret(kek: Buffer, agentId: string, triggerId: string): {
17
+ secret: string;
18
+ sealed: SealedWebhookSecret;
19
+ };
20
+ /** The signature of `delivery`'s id, timestamp and body with `key` (base64). */
21
+ export declare function signWebhook(key: Buffer, id: string, timestamp: string, body: Buffer): string;
22
+ /** The key bytes of a `whsec_` secret. */
23
+ export declare function webhookKeyOf(secret: string): Buffer;
24
+ /** Whether `delivery` carries a signature the sealed secret made. */
25
+ export declare function verifyWebhook(kek: Buffer, agentId: string, triggerId: string, sealed: SealedWebhookSecret, delivery: WebhookDelivery): boolean;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Webhook signing secrets (track T4, D75): the keys service makes a trigger's Standard Webhooks
3
+ * secret, seals it with the Tenant's vault key for the trigger's row, and is the only place that
4
+ * opens it, to verify a delivery. The secret leaves keys once, when it is made, for its owner.
5
+ *
6
+ * Standard Webhooks (https://www.standardwebhooks.com): the secret is `whsec_` and the base64 of
7
+ * its key bytes; a delivery's `webhook-signature` is space-separated `v1,<base64>` entries, each an
8
+ * HMAC-SHA256 with the key over `<webhook-id>.<webhook-timestamp>.<body>`. One that matches, in
9
+ * constant time, verifies it. The timestamp's tolerance is the route's to check.
10
+ */
11
+ import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
12
+ import { decryptSecret, encryptSecret } from "../vault/crypto.js";
13
+ /** The Standard Webhooks secret format: the prefix, and 32 random bytes of key. */
14
+ const SECRET_PREFIX = "whsec_";
15
+ const SECRET_BYTES = 32;
16
+ const aadOf = (agentId, triggerId) => Buffer.from(`nylorun-webhook:${agentId}:${triggerId}`, "utf8");
17
+ /** A new secret for Agent `agentId`'s trigger `triggerId`, and the same sealed for its row. */
18
+ export function makeWebhookSecret(kek, agentId, triggerId) {
19
+ const key = randomBytes(SECRET_BYTES);
20
+ try {
21
+ const sealed = encryptSecret(kek, aadOf(agentId, triggerId), key);
22
+ return {
23
+ secret: `${SECRET_PREFIX}${key.toString("base64")}`,
24
+ sealed: {
25
+ nonce: sealed.nonce.toString("base64"),
26
+ ciphertext: sealed.ciphertext.toString("base64"),
27
+ wrappedDek: sealed.wrappedDek.toString("base64"),
28
+ kekId: sealed.kekId,
29
+ },
30
+ };
31
+ }
32
+ finally {
33
+ key.fill(0);
34
+ }
35
+ }
36
+ /** The signature of `delivery`'s id, timestamp and body with `key` (base64). */
37
+ export function signWebhook(key, id, timestamp, body) {
38
+ return createHmac("sha256", key).update(`${id}.${timestamp}.`).update(body).digest("base64");
39
+ }
40
+ /** The key bytes of a `whsec_` secret. */
41
+ export function webhookKeyOf(secret) {
42
+ return Buffer.from(secret.startsWith(SECRET_PREFIX) ? secret.slice(SECRET_PREFIX.length) : secret, "base64");
43
+ }
44
+ /** Whether `delivery` carries a signature the sealed secret made. */
45
+ export function verifyWebhook(kek, agentId, triggerId, sealed, delivery) {
46
+ const key = decryptSecret(kek, aadOf(agentId, triggerId), Buffer.from(sealed.nonce, "base64"), Buffer.from(sealed.ciphertext, "base64"), Buffer.from(sealed.wrappedDek, "base64"), sealed.kekId);
47
+ try {
48
+ const expected = Buffer.from(signWebhook(key, delivery.id, delivery.timestamp, Buffer.from(delivery.body, "base64")), "base64");
49
+ for (const entry of delivery.signature.split(" ")) {
50
+ const [version, value] = entry.split(",", 2);
51
+ if (version !== "v1" || !value)
52
+ continue;
53
+ const given = Buffer.from(value, "base64");
54
+ if (given.length === expected.length && timingSafeEqual(given, expected))
55
+ return true;
56
+ }
57
+ return false;
58
+ }
59
+ finally {
60
+ key.fill(0);
61
+ }
62
+ }
@@ -4,22 +4,26 @@ export interface CertificateAuthority {
4
4
  /** PKCS#8 PEM of the CA key: sealed in the vault, used only by keys. */
5
5
  readonly privateKey: string;
6
6
  }
7
- /** A new P-256 CA, valid from an hour ago for `days` days. */
7
+ /**
8
+ * A new P-256 CA, valid from `keys.caBackdateMs` ago (Host tuning, an hour) for `days` days
9
+ * (default `keys.caLifetimeDays`).
10
+ */
8
11
  export declare function createCertificateAuthority(options: {
9
12
  readonly commonName: string;
10
13
  readonly days?: number;
11
14
  readonly now?: Date;
12
15
  }): CertificateAuthority;
13
16
  /**
14
- * A leaf for `host` and the caller's `publicKey` (SPKI PEM), signed by the CA, valid from five
15
- * minutes ago for `minutes` minutes. The caller keeps the leaf's private key: only the
17
+ * A leaf for `host` and the caller's `publicKey` (SPKI PEM), signed by the CA, valid from
18
+ * `keys.leafBackdateMs` ago (Host tuning, five minutes) for `lifetimeMs` (default
19
+ * `keys.leafLifetimeMs`, an hour: one connection rarely lasts this long). The caller keeps the leaf's private key: only the
16
20
  * certificate comes back, so no private key leaves whoever holds it.
17
21
  */
18
22
  export declare function issueLeafCertificate(options: {
19
23
  readonly authority: CertificateAuthority;
20
24
  readonly host: string;
21
25
  readonly publicKey: string;
22
- readonly minutes?: number;
26
+ readonly lifetimeMs?: number;
23
27
  readonly now?: Date;
24
28
  }): string;
25
29
  /** A P-256 key pair for a leaf, as PEM: SPKI for the public key, PKCS#8 for the private key. */
package/dist/keys/x509.js CHANGED
@@ -10,6 +10,7 @@
10
10
  * CA's key identifier. Go (`gh`), OpenSSL (`curl`, `git`), Node and Python accept that shape.
11
11
  */
12
12
  import { isIP } from "node:net";
13
+ import { hostTuning } from "../tuning.js";
13
14
  import { createHash, createPrivateKey, createPublicKey, generateKeyPairSync, randomBytes, sign, X509Certificate, } from "node:crypto";
14
15
  const OID = {
15
16
  ecdsaWithSha256: "1.2.840.10045.4.3.2",
@@ -96,21 +97,28 @@ function pem(der) {
96
97
  const body = der.toString("base64").replace(/(.{64})/g, "$1\n").replace(/\n$/, "");
97
98
  return `-----BEGIN CERTIFICATE-----\n${body}\n-----END CERTIFICATE-----\n`;
98
99
  }
99
- /** A new P-256 CA, valid from an hour ago for `days` days. */
100
+ /** Milliseconds in a day: a unit, not a setting. */
101
+ const DAY_MS = 86_400_000;
102
+ /**
103
+ * A new P-256 CA, valid from `keys.caBackdateMs` ago (Host tuning, an hour) for `days` days
104
+ * (default `keys.caLifetimeDays`).
105
+ */
100
106
  export function createCertificateAuthority(options) {
101
107
  const { publicKey, privateKey } = generateKeyPairSync("ec", { namedCurve: "P-256" });
102
108
  const now = options.now ?? new Date();
109
+ const tuning = hostTuning().keys;
103
110
  const subject = name({ commonName: options.commonName, organization: "Nylorun" });
104
111
  const ski = keyIdentifier(publicKey);
105
- const tbs = seq(explicit(0, integer(Buffer.from([2]))), serial(), seq(oid(OID.ecdsaWithSha256)), subject, seq(time(new Date(now.getTime() - 3_600_000)), time(new Date(now.getTime() + (options.days ?? 3650) * 86_400_000))), subject, spki(publicKey), explicit(3, seq(extension(OID.basicConstraints, true, seq(bool(true), integer(Buffer.from([0])))), extension(OID.keyUsage, true, bits(Buffer.from([0x06]), 1)), extension(OID.subjectKeyIdentifier, false, octets(ski)))));
112
+ const tbs = seq(explicit(0, integer(Buffer.from([2]))), serial(), seq(oid(OID.ecdsaWithSha256)), subject, seq(time(new Date(now.getTime() - tuning.caBackdateMs)), time(new Date(now.getTime() + (options.days ?? tuning.caLifetimeDays) * DAY_MS))), subject, spki(publicKey), explicit(3, seq(extension(OID.basicConstraints, true, seq(bool(true), integer(Buffer.from([0])))), extension(OID.keyUsage, true, bits(Buffer.from([0x06]), 1)), extension(OID.subjectKeyIdentifier, false, octets(ski)))));
106
113
  return {
107
114
  certificate: pem(certificate(tbs, privateKey)),
108
115
  privateKey: privateKey.export({ type: "pkcs8", format: "pem" }).toString(),
109
116
  };
110
117
  }
111
118
  /**
112
- * A leaf for `host` and the caller's `publicKey` (SPKI PEM), signed by the CA, valid from five
113
- * minutes ago for `minutes` minutes. The caller keeps the leaf's private key: only the
119
+ * A leaf for `host` and the caller's `publicKey` (SPKI PEM), signed by the CA, valid from
120
+ * `keys.leafBackdateMs` ago (Host tuning, five minutes) for `lifetimeMs` (default
121
+ * `keys.leafLifetimeMs`, an hour: one connection rarely lasts this long). The caller keeps the leaf's private key: only the
114
122
  * certificate comes back, so no private key leaves whoever holds it.
115
123
  */
116
124
  export function issueLeafCertificate(options) {
@@ -122,8 +130,9 @@ export function issueLeafCertificate(options) {
122
130
  if (publicKey.asymmetricKeyType !== "ec" || publicKey.asymmetricKeyDetails?.namedCurve !== "prime256v1")
123
131
  throw new Error("A leaf key must be ECDSA P-256");
124
132
  const now = options.now ?? new Date();
133
+ const tuning = hostTuning().keys;
125
134
  const issuer = Buffer.from(ca.raw);
126
- const tbs = seq(explicit(0, integer(Buffer.from([2]))), serial(), seq(oid(OID.ecdsaWithSha256)), subjectOf(issuer), seq(time(new Date(now.getTime() - 300_000)), time(new Date(now.getTime() + (options.minutes ?? 60) * 60_000))), name({ commonName: options.host }), spki(publicKey), explicit(3, seq(extension(OID.keyUsage, true, bits(Buffer.from([0x80]), 7)), extension(OID.extKeyUsage, false, seq(oid(OID.serverAuth))), extension(OID.subjectAltName, false, seq(tlv(0x82, Buffer.from(options.host, "ascii")))), extension(OID.authorityKeyIdentifier, false, seq(tlv(0x80, keyIdentifier(ca.publicKey)))))));
135
+ const tbs = seq(explicit(0, integer(Buffer.from([2]))), serial(), seq(oid(OID.ecdsaWithSha256)), subjectOf(issuer), seq(time(new Date(now.getTime() - tuning.leafBackdateMs)), time(new Date(now.getTime() + (options.lifetimeMs ?? tuning.leafLifetimeMs)))), name({ commonName: options.host }), spki(publicKey), explicit(3, seq(extension(OID.keyUsage, true, bits(Buffer.from([0x80]), 7)), extension(OID.extKeyUsage, false, seq(oid(OID.serverAuth))), extension(OID.subjectAltName, false, seq(tlv(0x82, Buffer.from(options.host, "ascii")))), extension(OID.authorityKeyIdentifier, false, seq(tlv(0x80, keyIdentifier(ca.publicKey)))))));
127
136
  return pem(certificate(tbs, caKey));
128
137
  }
129
138
  /** A P-256 key pair for a leaf, as PEM: SPKI for the public key, PKCS#8 for the private key. */