@nylorun/runtime 0.25.0-beta → 0.27.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 (326) hide show
  1. package/CHANGELOG.md +169 -0
  2. package/README.md +26 -16
  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.js +1 -1
  12. package/dist/api/a2a/endpoint.js +3 -4
  13. package/dist/api/a2a/protocol.js +4 -0
  14. package/dist/api/a2a/routes.d.ts +2 -6
  15. package/dist/api/a2a/routes.js +12 -18
  16. package/dist/api/ag-ui/endpoint.js +2 -3
  17. package/dist/api/ag-ui/routes.js +4 -4
  18. package/dist/api/components.d.ts +326 -45
  19. package/dist/api/components.js +5 -1
  20. package/dist/api/gate/keys.js +7 -4
  21. package/dist/api/gate/routes.d.ts +6 -2
  22. package/dist/api/gate/routes.js +15 -11
  23. package/dist/api/http/app.d.ts +2 -2
  24. package/dist/api/http/app.js +2 -2
  25. package/dist/api/http/body.d.ts +15 -3
  26. package/dist/api/http/body.js +15 -7
  27. package/dist/api/http/define.d.ts +1 -1
  28. package/dist/api/http/respond.js +7 -3
  29. package/dist/api/http/routes/access.js +5 -4
  30. package/dist/api/http/routes/artifacts.js +19 -7
  31. package/dist/api/http/routes/coverage.js +1 -1
  32. package/dist/api/http/routes/environment-manifests.js +1 -1
  33. package/dist/api/http/routes/environments.js +6 -6
  34. package/dist/api/http/routes/files.d.ts +2 -1
  35. package/dist/api/http/routes/files.js +31 -5
  36. package/dist/api/http/routes/mcp.js +1 -1
  37. package/dist/api/http/routes/reads.d.ts +35 -0
  38. package/dist/api/http/routes/reads.js +50 -22
  39. package/dist/api/http/routes/sessions.js +12 -11
  40. package/dist/api/http/routes/tenant.d.ts +2 -2
  41. package/dist/api/http/routes/tenant.js +69 -16
  42. package/dist/api/inbound-files.d.ts +3 -9
  43. package/dist/api/inbound-files.js +18 -24
  44. package/dist/api/openapi.js +6 -2
  45. package/dist/artifacts/disk.d.ts +0 -4
  46. package/dist/artifacts/disk.js +77 -73
  47. package/dist/artifacts/files.d.ts +13 -4
  48. package/dist/artifacts/files.js +27 -9
  49. package/dist/artifacts/folder-upload.d.ts +75 -0
  50. package/dist/artifacts/folder-upload.js +263 -0
  51. package/dist/artifacts/folders.d.ts +14 -8
  52. package/dist/artifacts/folders.js +19 -11
  53. package/dist/artifacts/links.js +7 -6
  54. package/dist/artifacts/parts.d.ts +9 -14
  55. package/dist/artifacts/parts.js +11 -9
  56. package/dist/artifacts/pipe.d.ts +15 -0
  57. package/dist/artifacts/pipe.js +71 -0
  58. package/dist/artifacts/quota.d.ts +36 -0
  59. package/dist/artifacts/quota.js +44 -0
  60. package/dist/artifacts/service.d.ts +25 -1
  61. package/dist/artifacts/service.js +70 -25
  62. package/dist/blob/fs.js +2 -1
  63. package/dist/blob/s3.d.ts +6 -2
  64. package/dist/blob/s3.js +9 -7
  65. package/dist/blob/types.d.ts +1 -1
  66. package/dist/blob/types.js +1 -1
  67. package/dist/core/limits.d.ts +11 -35
  68. package/dist/core/limits.js +5 -73
  69. package/dist/core/sandbox-routes.js +0 -1
  70. package/dist/core/turn-manifest.d.ts +1 -2
  71. package/dist/core/turn-manifest.js +4 -28
  72. package/dist/execution/memory.d.ts +8 -5
  73. package/dist/execution/memory.js +8 -6
  74. package/dist/execution/types.d.ts +1 -6
  75. package/dist/execution/types.js +0 -5
  76. package/dist/gates/contract.d.ts +3 -2
  77. package/dist/gates/contract.js +1 -1
  78. package/dist/gates/egress-credentials.js +4 -3
  79. package/dist/gates/egress.d.ts +9 -9
  80. package/dist/gates/egress.js +19 -19
  81. package/dist/gates/fixture-model.js +13 -2
  82. package/dist/gates/handler.d.ts +1 -3
  83. package/dist/gates/handler.js +4 -3
  84. package/dist/gates/http-client.d.ts +4 -3
  85. package/dist/gates/http-client.js +5 -7
  86. package/dist/gates/http-tool.d.ts +24 -4
  87. package/dist/gates/http-tool.js +53 -17
  88. package/dist/gates/in-process.d.ts +2 -1
  89. package/dist/gates/in-process.js +17 -9
  90. package/dist/gates/inflight.d.ts +2 -2
  91. package/dist/gates/inflight.js +3 -2
  92. package/dist/gates/journal.js +5 -7
  93. package/dist/gates/mcp-handler.d.ts +4 -3
  94. package/dist/gates/mcp-handler.js +15 -5
  95. package/dist/gates/model-gate.d.ts +11 -2
  96. package/dist/gates/policy.d.ts +28 -5
  97. package/dist/gates/policy.js +60 -17
  98. package/dist/gates/provider-gate.d.ts +22 -0
  99. package/dist/gates/provider-gate.js +30 -0
  100. package/dist/gates/tool-calls.d.ts +1 -3
  101. package/dist/gates/tool-calls.js +2 -3
  102. package/dist/gates/tool-client.d.ts +1 -1
  103. package/dist/gates/tool-client.js +5 -6
  104. package/dist/gates/tool-contract.d.ts +8 -5
  105. package/dist/gates/tool-contract.js +1 -1
  106. package/dist/gates/tool-gate.d.ts +18 -7
  107. package/dist/gates/tool-gate.js +49 -7
  108. package/dist/harness/calls.d.ts +27 -25
  109. package/dist/harness/calls.js +40 -46
  110. package/dist/harness/executors.d.ts +10 -3
  111. package/dist/harness/executors.js +53 -23
  112. package/dist/harness/main.js +2 -2
  113. package/dist/harness/pod.d.ts +0 -2
  114. package/dist/harness/pod.js +7 -6
  115. package/dist/harness/run-tokens.d.ts +4 -1
  116. package/dist/harness/run-tokens.js +6 -2
  117. package/dist/harness/service.d.ts +17 -14
  118. package/dist/harness/service.js +21 -35
  119. package/dist/harness/setup.d.ts +0 -2
  120. package/dist/harness/setup.js +4 -9
  121. package/dist/harness/ws-client.d.ts +2 -2
  122. package/dist/harness/ws-client.js +7 -5
  123. package/dist/harness/ws-port.d.ts +3 -4
  124. package/dist/harness/ws-port.js +3 -4
  125. package/dist/harness-api/artifacts.d.ts +20 -0
  126. package/dist/harness-api/artifacts.js +478 -0
  127. package/dist/harness-api/in-process.d.ts +2 -2
  128. package/dist/harness-api/in-process.js +18 -16
  129. package/dist/harness-api/record.d.ts +10 -9
  130. package/dist/harness-api/record.js +102 -157
  131. package/dist/harness-api/renew.d.ts +1 -1
  132. package/dist/harness-api/renew.js +2 -2
  133. package/dist/harness-api/server.d.ts +1 -16
  134. package/dist/harness-api/server.js +82 -73
  135. package/dist/harness-api/start.d.ts +20 -2
  136. package/dist/harness-api/start.js +57 -18
  137. package/dist/harness-api/tool-journal.d.ts +15 -10
  138. package/dist/harness-api/tool-journal.js +26 -24
  139. package/dist/harness-api/workspace.d.ts +0 -4
  140. package/dist/harness-api/workspace.js +2 -45
  141. package/dist/harness-api/ws-server.js +10 -10
  142. package/dist/host/create-host.js +2 -3
  143. package/dist/host/execution.js +2 -3
  144. package/dist/host/gates.d.ts +2 -9
  145. package/dist/host/gates.js +15 -12
  146. package/dist/host/http.d.ts +0 -2
  147. package/dist/host/http.js +0 -27
  148. package/dist/host/main.js +10 -3
  149. package/dist/host/operate.js +12 -3
  150. package/dist/host/stack-config.d.ts +11 -0
  151. package/dist/host/stack-config.js +21 -1
  152. package/dist/index.d.ts +0 -1
  153. package/dist/index.js +0 -1
  154. package/dist/infra/index.js +2 -1
  155. package/dist/infra/readiness.d.ts +0 -2
  156. package/dist/infra/readiness.js +4 -3
  157. package/dist/keys/client.d.ts +1 -0
  158. package/dist/keys/client.js +2 -3
  159. package/dist/keys/contract.d.ts +1 -3
  160. package/dist/keys/contract.js +1 -3
  161. package/dist/keys/egress-ca.d.ts +0 -2
  162. package/dist/keys/egress-ca.js +2 -3
  163. package/dist/keys/keys.js +6 -1
  164. package/dist/keys/x509.d.ts +8 -4
  165. package/dist/keys/x509.js +14 -5
  166. package/dist/management-openapi.json +1961 -994
  167. package/dist/mcp/connect.d.ts +35 -26
  168. package/dist/mcp/connect.js +112 -141
  169. package/dist/mcp/pool.d.ts +27 -22
  170. package/dist/mcp/pool.js +34 -48
  171. package/dist/mcp/preview.d.ts +13 -6
  172. package/dist/mcp/preview.js +37 -20
  173. package/dist/mcp/snapshot.d.ts +35 -86
  174. package/dist/mcp/snapshot.js +93 -135
  175. package/dist/model/auth-store.js +5 -3
  176. package/dist/model/capabilities.d.ts +1 -1
  177. package/dist/model/catalog.d.ts +9 -36
  178. package/dist/model/catalog.js +6 -33
  179. package/dist/model/descriptor.d.ts +51 -0
  180. package/dist/model/descriptor.js +83 -0
  181. package/dist/model/models.d.ts +0 -3
  182. package/dist/model/models.js +3 -5
  183. package/dist/model/native-blocks.js +1 -1
  184. package/dist/model/pi-model.d.ts +16 -5
  185. package/dist/model/pi-model.js +86 -105
  186. package/dist/openapi.json +866 -1218
  187. package/dist/reads/cursor.js +3 -1
  188. package/dist/record/append.d.ts +34 -2
  189. package/dist/record/append.js +50 -1
  190. package/dist/record/environment.d.ts +2 -0
  191. package/dist/record/environment.js +2 -0
  192. package/dist/record/index.d.ts +1 -1
  193. package/dist/record/index.js +1 -1
  194. package/dist/redact.d.ts +1 -1
  195. package/dist/redact.js +3 -2
  196. package/dist/sandbox/egress-token.d.ts +3 -3
  197. package/dist/sandbox/egress-token.js +3 -3
  198. package/dist/sandbox/join.js +3 -2
  199. package/dist/sandbox/manager.d.ts +41 -41
  200. package/dist/sandbox/manager.js +112 -44
  201. package/dist/sandbox/placement.d.ts +1 -1
  202. package/dist/sandbox/placement.js +5 -8
  203. package/dist/sandbox/pods/client.d.ts +1 -10
  204. package/dist/sandbox/pods/client.js +8 -4
  205. package/dist/sandbox/pods/lifecycle.d.ts +19 -11
  206. package/dist/sandbox/pods/lifecycle.js +45 -23
  207. package/dist/sandbox/pods/network-gate.d.ts +4 -5
  208. package/dist/sandbox/pods/network-gate.js +7 -7
  209. package/dist/sandbox/pods/reconcile.js +4 -1
  210. package/dist/sandbox/pods/spec.js +8 -5
  211. package/dist/sandbox/policy.d.ts +7 -1
  212. package/dist/sandbox/policy.js +10 -3
  213. package/dist/sandbox/resolve.d.ts +8 -3
  214. package/dist/sandbox/resolve.js +12 -2
  215. package/dist/sandbox/setup.d.ts +3 -1
  216. package/dist/sandbox/setup.js +34 -11
  217. package/dist/sandbox/share.d.ts +5 -14
  218. package/dist/sandbox/share.js +5 -34
  219. package/dist/sandbox/skills.d.ts +7 -13
  220. package/dist/sandbox/skills.js +16 -31
  221. package/dist/sandbox/tenant-config.d.ts +2 -2
  222. package/dist/sandbox/tenant-config.js +4 -3
  223. package/dist/sandbox/tools.d.ts +7 -1
  224. package/dist/sandbox/tools.js +26 -26
  225. package/dist/sandbox/types.d.ts +10 -3
  226. package/dist/sandbox/types.js +9 -0
  227. package/dist/store/postgres/connect.d.ts +6 -5
  228. package/dist/store/postgres/connect.js +14 -11
  229. package/dist/store/postgres/control.js +13 -13
  230. package/dist/store/postgres/drizzle/0028_artifact_write_keys.sql +2 -0
  231. package/dist/store/postgres/drizzle/0029_protocol_15.sql +11 -0
  232. package/dist/store/postgres/drizzle/0030_protocol_15_runtime_block.sql +11 -0
  233. package/dist/store/postgres/drizzle/0031_protocol_15_mcp_snapshot.sql +8 -0
  234. package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
  235. package/dist/store/postgres/migrate.js +1 -1
  236. package/dist/store/postgres/reads.js +10 -2
  237. package/dist/store/postgres/schema.d.ts +19 -0
  238. package/dist/store/postgres/schema.js +7 -0
  239. package/dist/store/postgres/store.js +55 -26
  240. package/dist/store/types.d.ts +17 -7
  241. package/dist/streams/relay/core.d.ts +3 -3
  242. package/dist/streams/relay/core.js +9 -10
  243. package/dist/tenant/advance.js +21 -31
  244. package/dist/tenant/commands.js +9 -16
  245. package/dist/tenant/context.d.ts +14 -17
  246. package/dist/tenant/definition-files.d.ts +25 -4
  247. package/dist/tenant/definition-files.js +85 -47
  248. package/dist/tenant/effects.js +9 -36
  249. package/dist/tenant/environment-manifests.js +9 -1
  250. package/dist/tenant/environments.d.ts +0 -2
  251. package/dist/tenant/environments.js +54 -30
  252. package/dist/tenant/ephemeral.d.ts +1 -1
  253. package/dist/tenant/ephemeral.js +2 -1
  254. package/dist/tenant/history.d.ts +1 -1
  255. package/dist/tenant/history.js +1 -1
  256. package/dist/tenant/host-token.d.ts +3 -3
  257. package/dist/tenant/host-token.js +12 -11
  258. package/dist/tenant/http.d.ts +5 -8
  259. package/dist/tenant/http.js +9 -0
  260. package/dist/tenant/identity-file.d.ts +24 -1
  261. package/dist/tenant/identity-file.js +35 -7
  262. package/dist/tenant/issuers.d.ts +5 -5
  263. package/dist/tenant/issuers.js +31 -23
  264. package/dist/tenant/jwt.d.ts +8 -2
  265. package/dist/tenant/jwt.js +10 -2
  266. package/dist/tenant/limits.d.ts +90 -0
  267. package/dist/tenant/limits.js +175 -0
  268. package/dist/tenant/module.d.ts +4 -1
  269. package/dist/tenant/module.js +9 -8
  270. package/dist/tenant/outbound.d.ts +17 -19
  271. package/dist/tenant/outbound.js +18 -21
  272. package/dist/tenant/run-grants.js +3 -2
  273. package/dist/tenant/run-token.d.ts +0 -4
  274. package/dist/tenant/run-token.js +6 -11
  275. package/dist/tenant/runtime-limits.d.ts +24 -0
  276. package/dist/tenant/runtime-limits.js +27 -0
  277. package/dist/tenant/runtime.js +30 -43
  278. package/dist/tenant/session-streams.js +7 -6
  279. package/dist/tenant/session.d.ts +1 -1
  280. package/dist/tenant/session.js +1 -1
  281. package/dist/tenant/sessions.js +32 -29
  282. package/dist/tenant/signing-keys.d.ts +1 -1
  283. package/dist/tenant/signing-keys.js +5 -6
  284. package/dist/tenant/status.js +4 -3
  285. package/dist/tenant/streams.js +18 -20
  286. package/dist/tenant/sweep.d.ts +1 -21
  287. package/dist/tenant/sweep.js +39 -48
  288. package/dist/tenant/transcript.d.ts +70 -23
  289. package/dist/tenant/transcript.js +28 -22
  290. package/dist/tenant/types.d.ts +11 -16
  291. package/dist/tenant/worker.d.ts +2 -10
  292. package/dist/tenant/worker.js +5 -14
  293. package/dist/traces/ids.js +5 -1
  294. package/dist/traces/map.d.ts +9 -6
  295. package/dist/traces/map.js +9 -11
  296. package/dist/traces/otlp.d.ts +3 -3
  297. package/dist/traces/otlp.js +5 -3
  298. package/dist/traces/projector.d.ts +9 -4
  299. package/dist/traces/projector.js +13 -7
  300. package/dist/traces/read.js +6 -1
  301. package/dist/tuning.d.ts +383 -0
  302. package/dist/tuning.js +658 -0
  303. package/dist/vault/headers.js +4 -2
  304. package/dist/version.d.ts +1 -1
  305. package/dist/version.js +1 -1
  306. package/package.json +3 -3
  307. package/dist/artifacts/export.d.ts +0 -8
  308. package/dist/artifacts/export.js +0 -204
  309. package/dist/artifacts/media-types.d.ts +0 -30
  310. package/dist/artifacts/media-types.js +0 -137
  311. package/dist/artifacts/workspace.d.ts +0 -60
  312. package/dist/artifacts/workspace.js +0 -45
  313. package/dist/media.d.ts +0 -10
  314. package/dist/media.js +0 -58
  315. package/dist/sandbox/runtime-tools.d.ts +0 -9
  316. package/dist/sandbox/runtime-tools.js +0 -16
  317. package/dist/sandbox/session-sandbox.d.ts +0 -16
  318. package/dist/sandbox/session-sandbox.js +0 -77
  319. package/dist/tenant/artifact-tool.d.ts +0 -13
  320. package/dist/tenant/artifact-tool.js +0 -243
  321. package/dist/tenant/skill-tool.d.ts +0 -20
  322. package/dist/tenant/skill-tool.js +0 -67
  323. package/dist/tenant/tool-results.d.ts +0 -57
  324. package/dist/tenant/tool-results.js +0 -298
  325. package/dist/tenant/tool-search.d.ts +0 -39
  326. package/dist/tenant/tool-search.js +0 -103
@@ -16,7 +16,7 @@
16
16
  *
17
17
  * The host token is accepted only by the Harness API listener (a connection serving that one
18
18
  * sandbox, and `host.renew`); the egress token only by egress-gate, as the proxy credential
19
- * (`Proxy-Authorization`). Neither is a bearer anywhere else: the Tenant API's bearer check
19
+ * (`Proxy-Authorization`). Neither is a bearer anywhere else: the Runtime API's bearer check
20
20
  * accepts trusted issuers' tokens only, and the gates run tokens only.
21
21
  *
22
22
  * Verification reads the key row on every call (a revoked key refuses at once) and, with
@@ -26,23 +26,24 @@
26
26
  import { randomUUID } from "node:crypto";
27
27
  import { errors, importJWK, jwtVerify } from "jose";
28
28
  import { tenantTokenIssuer } from "@nylorun/core/contracts";
29
- import { CLOCK_TOLERANCE_SECONDS, tokenKeyId } from "./jwt.js";
29
+ import { clockToleranceSeconds, tokenKeyId } from "./jwt.js";
30
+ import { hostTuning } from "../tuning.js";
30
31
  export const HOST_TOKEN_TYP = "nylorun-host+jwt";
31
32
  export const HOST_TOKEN_AUD = "nylorun-harness-api";
32
33
  export const EGRESS_TOKEN_TYP = "nylorun-egress+jwt";
33
34
  export const EGRESS_TOKEN_AUD = "nylorun-egress";
34
- /** How long a host or egress token lives. The engine renews them before. */
35
- export const HOST_TOKEN_TTL_SECONDS = 15 * 60;
36
- const LIFETIME_SLACK_SECONDS = 60;
35
+ /** How long a host or egress token lives (`keys.hostTokenTtlSeconds`). The engine renews them before. */
36
+ export const hostTokenTtlSeconds = () => hostTuning().keys.hostTokenTtlSeconds;
37
37
  /**
38
38
  * Mints an egress token (the only path that does): the proxy credential egress-gate accepts,
39
39
  * with egress-gate's claims (no `sub`). `mintHostTokens` mints one with every host token.
40
40
  */
41
41
  export async function mintEgressToken(signer, request) {
42
42
  const { tenantId, sandboxId, epoch, podUid } = request;
43
- const ttl = request.ttl ?? HOST_TOKEN_TTL_SECONDS;
44
- if (!Number.isSafeInteger(ttl) || ttl < 1 || ttl > HOST_TOKEN_TTL_SECONDS)
45
- throw new Error(`An egress token lives 1 to ${HOST_TOKEN_TTL_SECONDS} s, not ${ttl}`);
43
+ const most = hostTokenTtlSeconds();
44
+ const ttl = request.ttl ?? most;
45
+ if (!Number.isSafeInteger(ttl) || ttl < 1 || ttl > most)
46
+ throw new Error(`An egress token lives 1 to ${most} s, not ${ttl}`);
46
47
  if (!Number.isSafeInteger(epoch) || epoch < 1)
47
48
  throw new Error(`Host epoch ${epoch} is not a positive integer`);
48
49
  if (sandboxId === "" || podUid === "")
@@ -68,7 +69,7 @@ export async function mintEgressToken(signer, request) {
68
69
  export async function mintHostTokens(signer, host) {
69
70
  const tenantId = signer.config.tenantId;
70
71
  const iat = Math.floor(Date.now() / 1000);
71
- const exp = iat + HOST_TOKEN_TTL_SECONDS;
72
+ const exp = iat + hostTokenTtlSeconds();
72
73
  const [hostToken, egress] = await Promise.all([
73
74
  signer.keys.sign({
74
75
  typ: HOST_TOKEN_TYP,
@@ -129,7 +130,7 @@ export async function verifyHostToken(store, tenantId, raw, kind, cache = new Ma
129
130
  issuer: tenantTokenIssuer(tenantId),
130
131
  audience: aud,
131
132
  typ,
132
- clockTolerance: CLOCK_TOLERANCE_SECONDS,
133
+ clockTolerance: clockToleranceSeconds(),
133
134
  requiredClaims: ["iat", "exp", "jti"],
134
135
  });
135
136
  payload = verified.payload;
@@ -148,7 +149,7 @@ export async function verifyHostToken(store, tenantId, raw, kind, cache = new Ma
148
149
  typeof epc !== "number" ||
149
150
  !Number.isSafeInteger(epc) ||
150
151
  epc < 1 ||
151
- exp - iat > HOST_TOKEN_TTL_SECONDS + LIFETIME_SLACK_SECONDS)
152
+ exp - iat > hostTokenTtlSeconds() + hostTuning().keys.tokenLifetimeSlackSeconds)
152
153
  return refused("claims");
153
154
  return {
154
155
  ok: true,
@@ -1,11 +1,3 @@
1
- /**
2
- * The Tenant's typed rejections: what a route or the code it calls throws for the client to
3
- * see (`api/http/respond.ts` answers it), and the opaque 404 (D5) for what must not be told
4
- * apart from nothing: a capability link the Runtime did not sign, a Tenant that is not this
5
- * Host's. Credentials are `401` with a challenge since protocol 9 (`resource-server.ts`).
6
- *
7
- * Later waves: stable; the streams seam (Wave 2 / Y) keeps using these helpers.
8
- */
9
1
  /** D5's opaque failure: a 404 that says nothing of why. */
10
2
  export declare const OPAQUE_NOT_FOUND: {
11
3
  status: "rejected";
@@ -27,6 +19,11 @@ export declare class HttpError extends Error {
27
19
  headers?: Readonly<Record<string, string>>);
28
20
  }
29
21
  export declare const fail: (status: number, message: string, rejection?: Rejection, headers?: Readonly<Record<string, string>>) => never;
22
+ /**
23
+ * A 409 the client may try again: `code`, and when, in `details.retryAfterSeconds` and
24
+ * `Retry-After` (`tenant.retryAfterSeconds`, Host tuning).
25
+ */
26
+ export declare const failRetryLater: (message: string, code: string) => never;
30
27
  export declare class OpaqueAuthError extends Error {
31
28
  readonly status = 404;
32
29
  readonly body: {
@@ -6,6 +6,7 @@
6
6
  *
7
7
  * Later waves: stable; the streams seam (Wave 2 / Y) keeps using these helpers.
8
8
  */
9
+ import { hostTuning } from "../tuning.js";
9
10
  /** D5's opaque failure: a 404 that says nothing of why. */
10
11
  export const OPAQUE_NOT_FOUND = {
11
12
  status: "rejected",
@@ -28,6 +29,14 @@ export class HttpError extends Error {
28
29
  export const fail = (status, message, rejection, headers) => {
29
30
  throw new HttpError(status, message, rejection, headers);
30
31
  };
32
+ /**
33
+ * A 409 the client may try again: `code`, and when, in `details.retryAfterSeconds` and
34
+ * `Retry-After` (`tenant.retryAfterSeconds`, Host tuning).
35
+ */
36
+ export const failRetryLater = (message, code) => {
37
+ const seconds = hostTuning().tenant.retryAfterSeconds;
38
+ return fail(409, message, { code, details: { retryAfterSeconds: seconds } }, { "retry-after": String(seconds) });
39
+ };
31
40
  export class OpaqueAuthError extends Error {
32
41
  status = 404;
33
42
  body = OPAQUE_NOT_FOUND;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The identity file (Host feature `trusted-issuers`, F9 I2): the YAML file
3
- * `NYLORUN_IDENTITY_FILE` names, listing the issuers whose JWTs the Tenant API accepts as
3
+ * `NYLORUN_IDENTITY_FILE` names, listing the issuers whose JWTs the Runtime API accepts as
4
4
  * bearers. Read once at boot (`host/main.ts`); a change takes a restart. A malformed file stops
5
5
  * the boot with a message naming the issuer and the field. A key the file does not define is
6
6
  * ignored with a warning (`maxLifetime`, gone in protocol 9, among them), so an older file
@@ -18,6 +18,12 @@
18
18
  * allowedScopes: [agents:read, sessions:own, environments:write] # add studio to grant it
19
19
  * agents: [support] # absent reaches every agent
20
20
  * environments: ["{org_id}/*"] # absent reaches no Environment
21
+ * jwksFetch: # how the JWKS is fetched and cached
22
+ * timeoutMs: 5000 # one fetch
23
+ * maxBytes: 262144 # the largest JWKS read
24
+ * maxKeys: 64 # keys read from it, in order
25
+ * refetchMs: 60000 # the least time between two fetches for a new kid
26
+ * maxAgeMs: 600000 # cached keys' age before a background refresh
21
27
  * ```
22
28
  */
23
29
  import { type KeyObject } from "node:crypto";
@@ -25,6 +31,21 @@ import { type IssuerScope } from "@nylorun/core/contracts";
25
31
  /** The JWT algorithms an issuer may sign with. */
26
32
  export declare const ISSUER_ALGORITHMS: readonly ["RS256", "ES256", "EdDSA"];
27
33
  export type IssuerAlgorithm = (typeof ISSUER_ALGORITHMS)[number];
34
+ /** How an issuer's JWKS is fetched and cached (`jwksFetch`), each setting with its default. */
35
+ export interface JwksFetchSettings {
36
+ /** How long one fetch may take. */
37
+ readonly timeoutMs: number;
38
+ /** The largest JWKS read; a larger one is a failed fetch. */
39
+ readonly maxBytes: number;
40
+ /** The keys read from it, in order; the rest are ignored. */
41
+ readonly maxKeys: number;
42
+ /** The least time between two fetches for a `kid` the cache has not seen. */
43
+ readonly refetchMs: number;
44
+ /** How old cached keys get before a background refresh. */
45
+ readonly maxAgeMs: number;
46
+ }
47
+ /** The `jwksFetch` an entry that sets none fetches with. */
48
+ export declare const JWKS_FETCH_DEFAULTS: JwksFetchSettings;
28
49
  /** A public key an issuer signs with, and the one algorithm it verifies. */
29
50
  export interface IssuerKey {
30
51
  readonly key: KeyObject;
@@ -57,6 +78,8 @@ export interface TrustedIssuerConfig {
57
78
  readonly agents?: readonly string[];
58
79
  /** Environment grant templates, such as `{org_id}/*`; absent reaches no Environment. */
59
80
  readonly environments?: readonly string[];
81
+ /** How its JWKS is fetched; each setting left out is its default (`JWKS_FETCH_DEFAULTS`). */
82
+ readonly jwksFetch?: Partial<JwksFetchSettings>;
60
83
  }
61
84
  /** A malformed identity file: the message names the issuer and the field. */
62
85
  export declare class IdentityFileError extends Error {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The identity file (Host feature `trusted-issuers`, F9 I2): the YAML file
3
- * `NYLORUN_IDENTITY_FILE` names, listing the issuers whose JWTs the Tenant API accepts as
3
+ * `NYLORUN_IDENTITY_FILE` names, listing the issuers whose JWTs the Runtime API accepts as
4
4
  * bearers. Read once at boot (`host/main.ts`); a change takes a restart. A malformed file stops
5
5
  * the boot with a message naming the issuer and the field. A key the file does not define is
6
6
  * ignored with a warning (`maxLifetime`, gone in protocol 9, among them), so an older file
@@ -18,23 +18,39 @@
18
18
  * allowedScopes: [agents:read, sessions:own, environments:write] # add studio to grant it
19
19
  * agents: [support] # absent reaches every agent
20
20
  * environments: ["{org_id}/*"] # absent reaches no Environment
21
+ * jwksFetch: # how the JWKS is fetched and cached
22
+ * timeoutMs: 5000 # one fetch
23
+ * maxBytes: 262144 # the largest JWKS read
24
+ * maxKeys: 64 # keys read from it, in order
25
+ * refetchMs: 60000 # the least time between two fetches for a new kid
26
+ * maxAgeMs: 600000 # cached keys' age before a background refresh
21
27
  * ```
22
28
  */
23
29
  import { createPublicKey } from "node:crypto";
24
30
  import { parse as parseYaml } from "yaml";
25
31
  import { z } from "zod";
26
- import { ISSUER_SCOPES, isEnvironmentGrant, TOKEN_ENVIRONMENT_GRANTS_MAX, TOKEN_SCOPES, } from "@nylorun/core/contracts";
32
+ import { ISSUER_SCOPES, isEnvironmentGrant, TIMER_MAX_MS, TOKEN_SCOPES, } from "@nylorun/core/contracts";
27
33
  /** The JWT algorithms an issuer may sign with. */
28
34
  export const ISSUER_ALGORITHMS = ["RS256", "ES256", "EdDSA"];
29
35
  /** An entry's defaults: the `sub` claim, the RFC 9068 `scope` claim, every token scope but `studio`. */
30
36
  const DEFAULT_SUBJECT = "{sub}";
31
37
  const DEFAULT_SCOPES = { claim: "scope" };
32
38
  const DEFAULT_ALLOWED_SCOPES = TOKEN_SCOPES;
33
- /** At most this many static `keys` per issuer. */
34
- const MAX_STATIC_KEYS = 16;
39
+ /**
40
+ * An issuer's name, 1–32 characters as an application key's id: an identifier's grammar is the
41
+ * protocol's (it names the issuer in tokens and principals), so its length is a constant.
42
+ */
35
43
  const ISSUER_NAME = /^[a-z][a-z0-9-]{0,31}$/;
36
44
  /** A claim reference in a template: `{claim}`, a top-level claim name without braces. */
37
45
  const CLAIM_REFERENCE = /\{([^{}]+)\}/g;
46
+ /** The `jwksFetch` an entry that sets none fetches with. */
47
+ export const JWKS_FETCH_DEFAULTS = Object.freeze({
48
+ timeoutMs: 5_000,
49
+ maxBytes: 256 * 1024,
50
+ maxKeys: 64,
51
+ refetchMs: 60_000,
52
+ maxAgeMs: 10 * 60_000,
53
+ });
38
54
  /** A malformed identity file: the message names the issuer and the field. */
39
55
  export class IdentityFileError extends Error {
40
56
  constructor(message) {
@@ -79,10 +95,10 @@ export function issuerKey(key, kid) {
79
95
  throw new Error(`a ${key.asymmetricKeyType ?? "secret"} key is not accepted: use RSA (RS256), P-256 (ES256) or Ed25519 (EdDSA)`);
80
96
  }
81
97
  }
98
+ // The identity file's author writes every template; nothing a caller sends adds to one (B15).
82
99
  const template = (what) => z
83
100
  .string()
84
101
  .min(1)
85
- .max(200)
86
102
  .refine(bracesBalanced, `${what} may use braces only around a claim name, as {sub}`);
87
103
  const IssuerScopeSchema = z.enum(ISSUER_SCOPES, {
88
104
  error: (issue) => `${String(issue.input)} is not a scope an issuer may grant (one of ${ISSUER_SCOPES.join(", ")})`,
@@ -107,7 +123,8 @@ const IssuerEntrySchema = z
107
123
  }
108
124
  }, "must be an http(s) URL without credentials")
109
125
  .optional(),
110
- keys: z.array(z.string().min(1)).min(1).max(MAX_STATIC_KEYS).optional(),
126
+ // The file's author writes every key: no count of them is a limit (B15).
127
+ keys: z.array(z.string().min(1)).min(1).optional(),
111
128
  subject: template("subject")
112
129
  .refine((value) => claimReferences(value).length > 0, "must reference a claim, as {sub}: without one every token is the same person")
113
130
  .default(DEFAULT_SUBJECT),
@@ -123,7 +140,17 @@ const IssuerEntrySchema = z
123
140
  .array(template("an Environment grant").refine(
124
141
  // A rendered grant must be a grant: checked here with a sample value per claim.
125
142
  (value) => isEnvironmentGrant(renderTemplate(value, () => "x")), "must render to an Environment id or a prefix ending in /*, as {org_id}/*"))
126
- .max(TOKEN_ENVIRONMENT_GRANTS_MAX)
143
+ .optional(),
144
+ jwksFetch: z
145
+ .object({
146
+ // A fetch is timed with a JavaScript timer.
147
+ timeoutMs: z.number().int().positive().max(TIMER_MAX_MS).optional(),
148
+ maxBytes: z.number().int().positive().optional(),
149
+ maxKeys: z.number().int().positive().optional(),
150
+ refetchMs: z.number().int().nonnegative().optional(),
151
+ maxAgeMs: z.number().int().nonnegative().optional(),
152
+ })
153
+ .strict()
127
154
  .optional(),
128
155
  });
129
156
  const IdentityFileSchema = z.object({ issuers: z.array(z.unknown()) });
@@ -211,6 +238,7 @@ export function parseIdentityFile(text, source = "identity file", options = {})
211
238
  issuer: value.issuer,
212
239
  audience: value.audience,
213
240
  ...(value.jwks ? { jwks: value.jwks } : {}),
241
+ ...(value.jwksFetch ? { jwksFetch: value.jwksFetch } : {}),
214
242
  ...(keys ? { keys } : {}),
215
243
  subject: value.subject,
216
244
  scopes: value.scopes,
@@ -1,15 +1,15 @@
1
1
  import { type JWTPayload } from "jose";
2
2
  import type { AuthScope, TenantContext } from "./context.js";
3
3
  import { type TrustedIssuerConfig } from "./identity-file.js";
4
- /** The byte cap of an issuer's token; Nylorun's own token families keep 4 KiB. */
4
+ /**
5
+ * The longest issuer token read: a bearer arrives in a request's headers, which Node refuses past
6
+ * `http.maxHeaderSize` (16 KiB unless `--max-http-header-size` says otherwise). A dependency's
7
+ * constant, not a limit (B15).
8
+ */
5
9
  export declare const MAX_ISSUER_TOKEN_BYTES: number;
6
10
  export interface TrustedIssuersOptions {
7
11
  /** Fetches JWKS URLs. Default: the global fetch. */
8
12
  fetch?: typeof fetch;
9
- /** The least time between two JWKS fetches of one issuer for an unknown `kid`. Default 60 s. */
10
- refetchMs?: number;
11
- /** How old cached JWKS keys get before a background refresh. Default 10 minutes. */
12
- maxAgeMs?: number;
13
13
  /** Milliseconds since the epoch. Default `Date.now`. */
14
14
  now?: () => number;
15
15
  }
@@ -4,11 +4,12 @@
4
4
  * A token is one of theirs when its unverified `iss` names a configured issuer; it is then
5
5
  * verified like this, and nothing else is tried:
6
6
  *
7
- * - at most 16 KiB (identity providers' tokens often pass Nylorun's 4 KiB), RS256, ES256 or
8
- * EdDSA, and no header that could name another key (`jku`, `jwk`, `x5u`, `x5c`, `crit`);
7
+ * - no longer than a request's headers may be (`http.maxHeaderSize`), RS256, ES256 or EdDSA, and
8
+ * no header that could name another key (`jku`, `jwk`, `x5u`, `x5c`, `crit`);
9
9
  * - signed by a key of the issuer: its static `keys`, or its JWKS, fetched only from the
10
10
  * configured URL (`redirect: "error"`), cached by `kid`, refetched at most once a minute for
11
- * a `kid` it has not seen, and refreshed in the background once the cache is 10 minutes old.
11
+ * a `kid` it has not seen, and refreshed in the background once the cache is 10 minutes old
12
+ * (the entry's `jwksFetch` settings, these by default).
12
13
  * When the JWKS cannot be fetched, cached keys keep working and a new `kid` is
13
14
  * `401 issuer_unavailable`;
14
15
  * - `iss` and `aud` as configured, and `exp` present, with the 30 s clock tolerance of every
@@ -21,22 +22,27 @@
21
22
  * (`resource-server.ts`), its reason logged.
22
23
  */
23
24
  import { createHash, createPublicKey } from "node:crypto";
25
+ import { maxHeaderSize } from "node:http";
24
26
  import { decodeJwt, decodeProtectedHeader, errors, jwtVerify } from "jose";
25
27
  import { isSubject } from "@nylorun/core/contracts";
26
28
  import { HttpError } from "./http.js";
27
- import { CLOCK_TOLERANCE_SECONDS, looksLikeToken } from "./jwt.js";
29
+ import { clockToleranceSeconds, looksLikeToken } from "./jwt.js";
28
30
  import { bearerChallenge, failCredential } from "./resource-server.js";
29
- import { ISSUER_ALGORITHMS, issuerKey, renderTemplate, } from "./identity-file.js";
30
- /** The byte cap of an issuer's token; Nylorun's own token families keep 4 KiB. */
31
- export const MAX_ISSUER_TOKEN_BYTES = 16 * 1024;
31
+ import { ISSUER_ALGORITHMS, issuerKey, JWKS_FETCH_DEFAULTS, renderTemplate, } from "./identity-file.js";
32
+ /**
33
+ * The longest issuer token read: a bearer arrives in a request's headers, which Node refuses past
34
+ * `http.maxHeaderSize` (16 KiB unless `--max-http-header-size` says otherwise). A dependency's
35
+ * constant, not a limit (B15).
36
+ */
37
+ export const MAX_ISSUER_TOKEN_BYTES = maxHeaderSize;
32
38
  /** Subjects the Runtime keeps for itself: never an issuer token's. */
39
+ /**
40
+ * The longest token id kept as it is, 200 characters as the Runtime's other ids: an identifier's
41
+ * grammar is the protocol's, so a constant.
42
+ */
43
+ const TOKEN_ID_MAX = 200;
33
44
  const RESERVED = new Set(["host", "installation"]);
34
45
  const FORBIDDEN_HEADERS = ["jku", "jwk", "x5u", "x5c", "crit"];
35
- const JWKS_TIMEOUT_MS = 5000;
36
- const JWKS_MAX_BYTES = 256 * 1024;
37
- const JWKS_MAX_KEYS = 64;
38
- const DEFAULT_REFETCH_MS = 60_000;
39
- const DEFAULT_MAX_AGE_MS = 10 * 60_000;
40
46
  /** A grant's claim value: one Environment id segment, so a claim never widens the template. */
41
47
  const GRANT_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
42
48
  /** The issuer's public keys: static, or its JWKS's as last fetched. */
@@ -49,11 +55,13 @@ class IssuerKeys {
49
55
  okAt = Number.NEGATIVE_INFINITY;
50
56
  lastOk;
51
57
  inflight;
58
+ settings;
52
59
  constructor(config, options) {
53
60
  this.config = config;
54
61
  this.options = options;
55
62
  for (const key of config.keys ?? [])
56
63
  this.unnamed.push(key);
64
+ this.settings = { ...JWKS_FETCH_DEFAULTS, ...config.jwksFetch };
57
65
  }
58
66
  /** The keys that may have signed a token with `kid` and `alg`, or `unavailable`. */
59
67
  async candidates(kid, alg) {
@@ -75,13 +83,13 @@ class IssuerKeys {
75
83
  if (found.length === 0) {
76
84
  if (this.inflight)
77
85
  await this.inflight;
78
- else if (now - this.fetchedAt >= this.options.refetchMs)
86
+ else if (now - this.fetchedAt >= this.settings.refetchMs)
79
87
  await this.refresh();
80
88
  found = pick();
81
89
  if (found.length === 0 && this.lastOk === false)
82
90
  return "unavailable";
83
91
  }
84
- else if (now - this.okAt >= this.options.maxAgeMs && now - this.fetchedAt >= this.options.refetchMs) {
92
+ else if (now - this.okAt >= this.settings.maxAgeMs && now - this.fetchedAt >= this.settings.refetchMs) {
85
93
  // Old keys keep working while the refresh runs, and when it fails.
86
94
  void this.refresh();
87
95
  }
@@ -101,19 +109,19 @@ class IssuerKeys {
101
109
  const response = await this.options.fetch(this.config.jwks, {
102
110
  redirect: "error",
103
111
  headers: { accept: "application/jwk-set+json, application/json" },
104
- signal: AbortSignal.timeout(JWKS_TIMEOUT_MS),
112
+ signal: AbortSignal.timeout(this.settings.timeoutMs),
105
113
  });
106
114
  if (!response.ok)
107
115
  throw new Error(`JWKS answered ${response.status}`);
108
116
  const text = await response.text();
109
- if (Buffer.byteLength(text) > JWKS_MAX_BYTES)
117
+ if (Buffer.byteLength(text) > this.settings.maxBytes)
110
118
  throw new Error("JWKS is too large");
111
119
  const body = JSON.parse(text);
112
120
  if (!Array.isArray(body.keys))
113
121
  throw new Error("JWKS has no keys array");
114
122
  const byKid = new Map();
115
123
  const unnamed = [];
116
- for (const jwk of body.keys.slice(0, JWKS_MAX_KEYS)) {
124
+ for (const jwk of body.keys.slice(0, this.settings.maxKeys)) {
117
125
  const key = jwkKey(jwk);
118
126
  if (!key)
119
127
  continue;
@@ -143,7 +151,8 @@ function jwkKey(jwk) {
143
151
  return undefined; // a private key: never trusted as published
144
152
  if (entry.alg !== undefined && !ISSUER_ALGORITHMS.includes(entry.alg))
145
153
  return undefined;
146
- const kid = typeof entry.kid === "string" && entry.kid.length <= 256 ? entry.kid : undefined;
154
+ // A `kid` longer than a token may be names no token's key.
155
+ const kid = typeof entry.kid === "string" && entry.kid.length <= MAX_ISSUER_TOKEN_BYTES ? entry.kid : undefined;
147
156
  try {
148
157
  const key = issuerKey(createPublicKey({ key: jwk, format: "jwk" }), kid);
149
158
  return entry.alg === undefined || entry.alg === key.alg ? key : undefined;
@@ -186,12 +195,12 @@ function trustedIssuer(config, options) {
186
195
  algorithms: [candidate.alg],
187
196
  issuer: config.issuer,
188
197
  audience: config.audience,
189
- clockTolerance: CLOCK_TOLERANCE_SECONDS,
198
+ clockTolerance: clockToleranceSeconds(),
190
199
  requiredClaims: ["exp"],
191
200
  currentDate: new Date(options.now()),
192
201
  });
193
202
  const now = Math.floor(options.now() / 1000);
194
- if (payload.iat !== undefined && payload.iat > now + CLOCK_TOLERANCE_SECONDS)
203
+ if (payload.iat !== undefined && payload.iat > now + clockToleranceSeconds())
195
204
  return refuse("invalid", "issuer_token_future");
196
205
  return { ok: true, payload, ...(kid !== undefined ? { kid } : {}) };
197
206
  }
@@ -212,8 +221,6 @@ function trustedIssuer(config, options) {
212
221
  export function createTrustedIssuers(configs, options = {}) {
213
222
  const resolved = {
214
223
  fetch: options.fetch ?? ((input, init) => globalThis.fetch(input, init)),
215
- refetchMs: options.refetchMs ?? DEFAULT_REFETCH_MS,
216
- maxAgeMs: options.maxAgeMs ?? DEFAULT_MAX_AGE_MS,
217
224
  now: options.now ?? Date.now,
218
225
  };
219
226
  const issuers = configs.map((config) => trustedIssuer(config, resolved));
@@ -300,7 +307,8 @@ export async function verifyIssuerToken(ctx, issuer, raw, resourceMetadata) {
300
307
  // A grant whose claim is missing or not one id segment reaches nothing.
301
308
  return rendered === undefined ? [] : [rendered];
302
309
  });
303
- const jti = typeof payload.jti === "string" && payload.jti.length > 0 && payload.jti.length <= 200
310
+ // A `jti` longer than an id the Runtime keeps (`TOKEN_ID_MAX`) is replaced by the token's hash.
311
+ const jti = typeof payload.jti === "string" && payload.jti.length > 0 && payload.jti.length <= TOKEN_ID_MAX
304
312
  ? payload.jti
305
313
  : createHash("sha256").update(raw).digest("base64url");
306
314
  return {
@@ -1,5 +1,11 @@
1
- export declare const MAX_TOKEN_BYTES = 4096;
2
- export declare const CLOCK_TOLERANCE_SECONDS = 30;
1
+ /**
2
+ * The longest token read: a token arrives in a request's headers (a bearer) or its URL (a
3
+ * capability link), which Node refuses past `http.maxHeaderSize` (16 KiB unless
4
+ * `--max-http-header-size` says otherwise). A dependency's constant, not a limit (B15).
5
+ */
6
+ export declare const MAX_TOKEN_BYTES: number;
7
+ /** The clock skew accepted when a token's times are checked (`keys.clockToleranceSeconds`). */
8
+ export declare const clockToleranceSeconds: () => number;
3
9
  /** True when `bearer` has the shape of a JWT; application keys never do. */
4
10
  export declare function looksLikeToken(bearer: string): boolean;
5
11
  /** The `typ` of a JWT-shaped bearer, or `undefined` when its header cannot be read. */
@@ -3,10 +3,18 @@
3
3
  * the Tenant's ES256 JWTs, recognised by shape, told apart by their `typ`, and refused before
4
4
  * any key is read when the header is not one the Runtime issues.
5
5
  */
6
+ import { maxHeaderSize } from "node:http";
6
7
  import { decodeProtectedHeader } from "jose";
7
8
  import { SIGNING_KEY_ID_PATTERN } from "@nylorun/core/compatibility";
8
- export const MAX_TOKEN_BYTES = 4096;
9
- export const CLOCK_TOLERANCE_SECONDS = 30;
9
+ import { hostTuning } from "../tuning.js";
10
+ /**
11
+ * The longest token read: a token arrives in a request's headers (a bearer) or its URL (a
12
+ * capability link), which Node refuses past `http.maxHeaderSize` (16 KiB unless
13
+ * `--max-http-header-size` says otherwise). A dependency's constant, not a limit (B15).
14
+ */
15
+ export const MAX_TOKEN_BYTES = maxHeaderSize;
16
+ /** The clock skew accepted when a token's times are checked (`keys.clockToleranceSeconds`). */
17
+ export const clockToleranceSeconds = () => hostTuning().keys.clockToleranceSeconds;
10
18
  const JWT_SHAPE = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
11
19
  const FORBIDDEN_HEADERS = ["jku", "jwk", "x5u", "x5c", "x5t", "crit"];
12
20
  /** True when `bearer` has the shape of a JWT; application keys never do. */
@@ -0,0 +1,90 @@
1
+ /**
2
+ * What artifacts, skills and disk versions may hold: the Tenant's limits (`artifacts.config`,
3
+ * `skills.config` here, and the Runtime limits `diskVersion*` of `limits.config`), and an
4
+ * Environment's, which its manifest sets (`limits`). A limit the manifest leaves out is the
5
+ * Tenant's as it is when the limit is checked, and none is ever above the Tenant's: a manifest
6
+ * asking for more is refused when the Environment is made, and a Tenant that lowers a limit
7
+ * later lowers it for every Environment.
8
+ *
9
+ * Also the shape a manifest and an Environment's labels must fit, which the Tenant's Runtime
10
+ * limits set (`manifestShapeIssues`, `labelIssues`), checked when they are put rather than by
11
+ * their schemas (B15).
12
+ */
13
+ import { type EnvironmentFile, type EnvironmentLimits, type EnvironmentSecretSlots, type EnvironmentSetupCommand, type RuntimeLimits, type TenantSkillsConfig, type ToolRule } from "@nylorun/core/contracts";
14
+ import { type ArtifactLimits } from "../artifacts/config.js";
15
+ export declare const SKILLS_CONFIG_SETTING = "skills.config";
16
+ export interface SkillLimits {
17
+ readonly totalBytes: number;
18
+ }
19
+ /** What one version of an Environment's disk may hold. */
20
+ export interface DiskLimits {
21
+ readonly versionBytes: number;
22
+ readonly versionEntries: number;
23
+ }
24
+ /** The limits an Environment's artifacts, skills and disk versions are held to. */
25
+ export interface StorageLimits {
26
+ readonly artifacts: ArtifactLimits;
27
+ readonly skills: SkillLimits;
28
+ readonly disk: DiskLimits;
29
+ }
30
+ interface SettingsReader {
31
+ getSetting(key: string): Promise<string | undefined>;
32
+ }
33
+ interface SettingsWriter extends SettingsReader {
34
+ putSetting(key: string, value: string): Promise<void>;
35
+ }
36
+ /** The skill limits in force: the Tenant's, with the default for what it leaves unset. */
37
+ export declare function effectiveSkillLimits(config: TenantSkillsConfig): SkillLimits;
38
+ export declare function readSkillsConfig(t: SettingsReader): Promise<TenantSkillsConfig>;
39
+ export declare function readSkillLimits(t: SettingsReader): Promise<SkillLimits>;
40
+ export declare function writeSkillsConfig(t: SettingsWriter, config: TenantSkillsConfig): Promise<void>;
41
+ /** The Tenant's artifact, skill and disk version limits. */
42
+ export declare function readTenantLimits(t: SettingsReader): Promise<StorageLimits>;
43
+ /** The limits in force for an Environment whose manifest sets `set`: each within the Tenant's. */
44
+ export declare function limitsInForce(tenant: StorageLimits, set: EnvironmentLimits | undefined): StorageLimits;
45
+ /** Why a manifest's `limits` cannot be made: each limit above the Tenant's. */
46
+ export declare function limitIssues(set: EnvironmentLimits | undefined, tenant: StorageLimits): string[];
47
+ /**
48
+ * Why `labels` cannot be set: more than the Tenant's Runtime limit `count` names
49
+ * (`environmentLabels` or `artifactLabels`), or a key or a value longer than `labelKeyChars` or
50
+ * `labelValueChars`.
51
+ */
52
+ export declare function labelIssues(labels: Readonly<Record<string, string>> | undefined, limits: RuntimeLimits, count?: "environmentLabels" | "artifactLabels"): string[];
53
+ /** The parts of a manifest whose shape the Tenant's Runtime limits set. */
54
+ export interface ManifestShape {
55
+ readonly description?: string | undefined;
56
+ readonly setup?: readonly EnvironmentSetupCommand[] | undefined;
57
+ readonly files?: readonly EnvironmentFile[] | undefined;
58
+ readonly secrets?: EnvironmentSecretSlots | undefined;
59
+ }
60
+ /**
61
+ * Why a secret write does not fit the Tenant's Runtime limits: an `environment_secret`'s or an
62
+ * `environment_variable`'s value past `secretValueChars`, or more hosts than `secretHosts`.
63
+ */
64
+ export declare function secretRequestIssues(request: {
65
+ readonly type: string;
66
+ readonly value?: unknown;
67
+ readonly allowedHosts?: readonly string[];
68
+ }, limits: RuntimeLimits): string[];
69
+ /**
70
+ * Why a tool policy does not fit the Tenant's Runtime limits: more rules than `toolRules`, a
71
+ * selector or a `when` path past `toolRuleFieldChars`, a `reason` or a `matches` past
72
+ * `toolRuleTextChars`.
73
+ */
74
+ export declare function toolPolicyIssues(rules: readonly ToolRule[], limits: RuntimeLimits): string[];
75
+ /** Why a set of model budgets does not fit: more than the Tenant's Runtime limit `modelBudgets`. */
76
+ export declare function budgetIssues(budgets: readonly unknown[], limits: RuntimeLimits): string[];
77
+ /**
78
+ * Why an agent's `requires` does not fit: more secrets than an Environment's manifest may declare
79
+ * slots for (the Runtime limit `environmentSecretSlots`).
80
+ */
81
+ export declare function requiresIssues(requires: {
82
+ readonly secrets?: readonly string[];
83
+ } | undefined, limits: RuntimeLimits): string[];
84
+ /**
85
+ * Why a manifest (inline, or saved) does not fit the Tenant's Runtime limits: its description,
86
+ * its setup commands, their lengths and timeouts, its input file entries and the files of each,
87
+ * and its secret slots, their descriptions and hosts.
88
+ */
89
+ export declare function manifestShapeIssues(manifest: ManifestShape, limits: RuntimeLimits): string[];
90
+ export {};