@jr2/orchestrator 0.4.1 → 0.6.0

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 (361) hide show
  1. package/README.md +20 -2
  2. package/console/components/app.ts +2 -0
  3. package/console/components/fleet.ts +14 -1
  4. package/console/components/machine-pane.ts +41 -2
  5. package/console/main.ts +13 -1
  6. package/console/store.ts +102 -6
  7. package/console/style.css +21 -0
  8. package/docs/agents-block.md +5 -0
  9. package/docs/concepts/agents-and-turns.md +478 -0
  10. package/docs/concepts/architecture.md +243 -0
  11. package/docs/concepts/charts.md +146 -0
  12. package/docs/concepts/composition.md +357 -0
  13. package/docs/concepts/converge.md +257 -0
  14. package/docs/concepts/durable-runs.md +189 -0
  15. package/docs/concepts/effects.md +658 -0
  16. package/docs/concepts/gates.md +208 -0
  17. package/docs/concepts/machines-and-workflows.md +298 -0
  18. package/docs/concepts/pools-and-sources.md +230 -0
  19. package/docs/concepts/secrets-and-trust.md +306 -0
  20. package/docs/concepts/the-menu.md +381 -0
  21. package/docs/concepts/workspaces.md +756 -0
  22. package/docs/get-started/a-model-decides.md +563 -0
  23. package/docs/get-started/an-agent-in-a-sandbox.md +383 -0
  24. package/docs/get-started/connect-a-model.md +248 -0
  25. package/docs/get-started/create-an-instance.md +194 -0
  26. package/docs/get-started/have-your-assistant-write-a-machine.md +993 -0
  27. package/docs/get-started/install.md +130 -0
  28. package/docs/guides/add-tools-to-the-sandbox.md +308 -0
  29. package/docs/guides/answer-a-gate.md +304 -0
  30. package/docs/guides/bind-open-parts.md +224 -0
  31. package/docs/guides/cancel-a-run.md +146 -0
  32. package/docs/guides/clean-up.md +206 -0
  33. package/docs/guides/cut-a-worktree.md +560 -0
  34. package/docs/guides/fix-a-failed-converge.md +361 -0
  35. package/docs/guides/give-the-cluster-git-credentials.md +222 -0
  36. package/docs/guides/iterate-on-a-machine-on-kind.md +190 -0
  37. package/docs/guides/let-an-agent-write-a-chart.md +230 -0
  38. package/docs/guides/look-inside-a-sandbox.md +177 -0
  39. package/docs/guides/mirror-the-kit-images.md +176 -0
  40. package/docs/guides/move-from-kind-to-a-shared-cluster.md +318 -0
  41. package/docs/guides/open-the-console.md +91 -0
  42. package/docs/guides/publish-a-machine-as-an-npm-package.md +350 -0
  43. package/docs/guides/push-and-sign-a-branch.md +532 -0
  44. package/docs/guides/run-a-chart-by-name.md +270 -0
  45. package/docs/guides/run-a-command-or-call-a-url.md +587 -0
  46. package/docs/guides/run-one-worker-per-item.md +322 -0
  47. package/docs/guides/set-an-agents-access-and-dials.md +420 -0
  48. package/docs/guides/size-a-workspace.md +217 -0
  49. package/docs/guides/start-a-run-from-a-run.md +237 -0
  50. package/docs/guides/start-a-run.md +207 -0
  51. package/docs/guides/upgrade-the-kit-version.md +240 -0
  52. package/docs/guides/use-a-hosted-provider.md +214 -0
  53. package/docs/guides/use-a-local-model.md +313 -0
  54. package/docs/guides/watch-a-run.md +349 -0
  55. package/docs/guides/write-a-workflow.md +318 -0
  56. package/docs/index.md +44 -0
  57. package/docs/llms.txt +237 -0
  58. package/docs/reference/authoring-surface/agent.md +151 -0
  59. package/docs/reference/authoring-surface/answer.md +156 -0
  60. package/docs/reference/authoring-surface/arg.md +82 -0
  61. package/docs/reference/authoring-surface/basic.md +62 -0
  62. package/docs/reference/authoring-surface/bearer.md +53 -0
  63. package/docs/reference/authoring-surface/branchname.md +15 -0
  64. package/docs/reference/authoring-surface/chartschema.md +23 -0
  65. package/docs/reference/authoring-surface/chartschemafor.md +48 -0
  66. package/docs/reference/authoring-surface/command.md +206 -0
  67. package/docs/reference/authoring-surface/customize.md +64 -0
  68. package/docs/reference/authoring-surface/defineconfig.md +41 -0
  69. package/docs/reference/authoring-surface/defineevent.md +114 -0
  70. package/docs/reference/authoring-surface/dispatch.md +126 -0
  71. package/docs/reference/authoring-surface/fail.md +96 -0
  72. package/docs/reference/authoring-surface/file.md +142 -0
  73. package/docs/reference/authoring-surface/frompick.md +277 -0
  74. package/docs/reference/authoring-surface/http.md +259 -0
  75. package/docs/reference/authoring-surface/isfailure.md +58 -0
  76. package/docs/reference/authoring-surface/isready.md +14 -0
  77. package/docs/reference/authoring-surface/jr2setup.md +237 -0
  78. package/docs/reference/authoring-surface/open.md +105 -0
  79. package/docs/reference/authoring-surface/openbody.md +86 -0
  80. package/docs/reference/authoring-surface/paramsof.md +143 -0
  81. package/docs/reference/authoring-surface/pool.md +186 -0
  82. package/docs/reference/authoring-surface/push.md +130 -0
  83. package/docs/reference/authoring-surface/readyof.md +21 -0
  84. package/docs/reference/authoring-surface/secret.md +139 -0
  85. package/docs/reference/authoring-surface/sentby.md +83 -0
  86. package/docs/reference/authoring-surface/source.md +51 -0
  87. package/docs/reference/authoring-surface/text.md +62 -0
  88. package/docs/reference/authoring-surface/type-agentdeclaration.md +27 -0
  89. package/docs/reference/authoring-surface/type-agentdefinition.md +80 -0
  90. package/docs/reference/authoring-surface/type-agentlogic.md +15 -0
  91. package/docs/reference/authoring-surface/type-agentslots.md +15 -0
  92. package/docs/reference/authoring-surface/type-answeraction.md +12 -0
  93. package/docs/reference/authoring-surface/type-anyframepath.md +14 -0
  94. package/docs/reference/authoring-surface/type-anyworktreepath.md +15 -0
  95. package/docs/reference/authoring-surface/type-arg.md +19 -0
  96. package/docs/reference/authoring-surface/type-argnames.md +12 -0
  97. package/docs/reference/authoring-surface/type-bindactors.md +14 -0
  98. package/docs/reference/authoring-surface/type-binding.md +61 -0
  99. package/docs/reference/authoring-surface/type-bindingof.md +12 -0
  100. package/docs/reference/authoring-surface/type-bodyacceptsdoor.md +15 -0
  101. package/docs/reference/authoring-surface/type-bodyacceptsslots.md +17 -0
  102. package/docs/reference/authoring-surface/type-ceilings.md +41 -0
  103. package/docs/reference/authoring-surface/type-chainlink.md +19 -0
  104. package/docs/reference/authoring-surface/type-chartaction.md +46 -0
  105. package/docs/reference/authoring-surface/type-chartagent.md +28 -0
  106. package/docs/reference/authoring-surface/type-chartevent.md +20 -0
  107. package/docs/reference/authoring-surface/type-chartguard.md +20 -0
  108. package/docs/reference/authoring-surface/type-chartinvoke.md +28 -0
  109. package/docs/reference/authoring-surface/type-chartjson.md +14 -0
  110. package/docs/reference/authoring-surface/type-chartstate.md +46 -0
  111. package/docs/reference/authoring-surface/type-charttext.md +40 -0
  112. package/docs/reference/authoring-surface/type-charttransition.md +22 -0
  113. package/docs/reference/authoring-surface/type-charttransitions.md +14 -0
  114. package/docs/reference/authoring-surface/type-chartvalue.md +15 -0
  115. package/docs/reference/authoring-surface/type-childat.md +12 -0
  116. package/docs/reference/authoring-surface/type-childslots.md +12 -0
  117. package/docs/reference/authoring-surface/type-commandenv.md +15 -0
  118. package/docs/reference/authoring-surface/type-commandinput.md +20 -0
  119. package/docs/reference/authoring-surface/type-commandoptions.md +56 -0
  120. package/docs/reference/authoring-surface/type-commandoutput.md +122 -0
  121. package/docs/reference/authoring-surface/type-customize.md +111 -0
  122. package/docs/reference/authoring-surface/type-dispatchdefinition.md +39 -0
  123. package/docs/reference/authoring-surface/type-dispatchinput.md +14 -0
  124. package/docs/reference/authoring-surface/type-dispatchopenparts.md +14 -0
  125. package/docs/reference/authoring-surface/type-dispatchoutput.md +72 -0
  126. package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
  127. package/docs/reference/authoring-surface/type-duration.md +15 -0
  128. package/docs/reference/authoring-surface/type-effectbinding.md +93 -0
  129. package/docs/reference/authoring-surface/type-effectlogic.md +12 -0
  130. package/docs/reference/authoring-surface/type-envopenparts.md +14 -0
  131. package/docs/reference/authoring-surface/type-eventaudience.md +27 -0
  132. package/docs/reference/authoring-surface/type-eventdef.md +45 -0
  133. package/docs/reference/authoring-surface/type-eventfrom.md +16 -0
  134. package/docs/reference/authoring-surface/type-eventsemantics.md +26 -0
  135. package/docs/reference/authoring-surface/type-execoutcome.md +34 -0
  136. package/docs/reference/authoring-surface/type-failslot.md +14 -0
  137. package/docs/reference/authoring-surface/type-failure.md +19 -0
  138. package/docs/reference/authoring-surface/type-failureinput.md +20 -0
  139. package/docs/reference/authoring-surface/type-filebody.md +37 -0
  140. package/docs/reference/authoring-surface/type-framepath.md +14 -0
  141. package/docs/reference/authoring-surface/type-frominput.md +14 -0
  142. package/docs/reference/authoring-surface/type-hostinjectedinput.md +56 -0
  143. package/docs/reference/authoring-surface/type-httpdefinition.md +104 -0
  144. package/docs/reference/authoring-surface/type-httpfileinput.md +20 -0
  145. package/docs/reference/authoring-surface/type-httpinput.md +12 -0
  146. package/docs/reference/authoring-surface/type-httpmethod.md +14 -0
  147. package/docs/reference/authoring-surface/type-httpopenparts.md +12 -0
  148. package/docs/reference/authoring-surface/type-httpoutput.md +95 -0
  149. package/docs/reference/authoring-surface/type-httprequestparts.md +33 -0
  150. package/docs/reference/authoring-surface/type-jr2config.md +126 -0
  151. package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
  152. package/docs/reference/authoring-surface/type-lends.md +38 -0
  153. package/docs/reference/authoring-surface/type-logicat.md +12 -0
  154. package/docs/reference/authoring-surface/type-markedif.md +12 -0
  155. package/docs/reference/authoring-surface/type-mergedactors.md +12 -0
  156. package/docs/reference/authoring-surface/type-namedsecret.md +25 -0
  157. package/docs/reference/authoring-surface/type-namestart.md +77 -0
  158. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +27 -0
  159. package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -0
  160. package/docs/reference/authoring-surface/type-openbodydeclaration.md +40 -0
  161. package/docs/reference/authoring-surface/type-openbodylogic.md +14 -0
  162. package/docs/reference/authoring-surface/type-openbodyoutput.md +42 -0
  163. package/docs/reference/authoring-surface/type-openbodyslot.md +18 -0
  164. package/docs/reference/authoring-surface/type-opencommandinput.md +22 -0
  165. package/docs/reference/authoring-surface/type-openeffect.md +12 -0
  166. package/docs/reference/authoring-surface/type-openeffectslots.md +12 -0
  167. package/docs/reference/authoring-surface/type-openof.md +12 -0
  168. package/docs/reference/authoring-surface/type-opensecret.md +23 -0
  169. package/docs/reference/authoring-surface/type-opensecretmark.md +12 -0
  170. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +26 -0
  171. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +27 -0
  172. package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
  173. package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
  174. package/docs/reference/authoring-surface/type-poolspec.md +60 -0
  175. package/docs/reference/authoring-surface/type-pushinput.md +34 -0
  176. package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
  177. package/docs/reference/authoring-surface/type-pushoutput.md +149 -0
  178. package/docs/reference/authoring-surface/type-pushwireoutput.md +12 -0
  179. package/docs/reference/authoring-surface/type-quantity.md +14 -0
  180. package/docs/reference/authoring-surface/type-reached.md +12 -0
  181. package/docs/reference/authoring-surface/type-reposlot.md +70 -0
  182. package/docs/reference/authoring-surface/type-reposlotsof.md +16 -0
  183. package/docs/reference/authoring-surface/type-sandboxoptions.md +84 -0
  184. package/docs/reference/authoring-surface/type-secret.md +25 -0
  185. package/docs/reference/authoring-surface/type-sender.md +19 -0
  186. package/docs/reference/authoring-surface/type-sentbyguards.md +12 -0
  187. package/docs/reference/authoring-surface/type-size.md +85 -0
  188. package/docs/reference/authoring-surface/type-slothandles.md +24 -0
  189. package/docs/reference/authoring-surface/type-sourcenextresult.md +17 -0
  190. package/docs/reference/authoring-surface/type-sourcespec.md +44 -0
  191. package/docs/reference/authoring-surface/type-syncstate.md +12 -0
  192. package/docs/reference/authoring-surface/type-text.md +19 -0
  193. package/docs/reference/authoring-surface/type-textpart.md +24 -0
  194. package/docs/reference/authoring-surface/type-thinkinglevel.md +39 -0
  195. package/docs/reference/authoring-surface/type-toparameterizedobject.md +14 -0
  196. package/docs/reference/authoring-surface/type-toprovidedactor.md +14 -0
  197. package/docs/reference/authoring-surface/type-unknown.md +21 -0
  198. package/docs/reference/authoring-surface/type-usercontainer.md +77 -0
  199. package/docs/reference/authoring-surface/type-withoutemptyindex.md +20 -0
  200. package/docs/reference/authoring-surface/type-workflowevent.md +46 -0
  201. package/docs/reference/authoring-surface/type-workspaceaccess.md +16 -0
  202. package/docs/reference/authoring-surface/type-workspaced.md +115 -0
  203. package/docs/reference/authoring-surface/type-workspacehandles.md +22 -0
  204. package/docs/reference/authoring-surface/type-workspacemachine.md +29 -0
  205. package/docs/reference/authoring-surface/type-workspaceof.md +13 -0
  206. package/docs/reference/authoring-surface/type-workspaceoptions.md +27 -0
  207. package/docs/reference/authoring-surface/type-worktreefound.md +16 -0
  208. package/docs/reference/authoring-surface/type-worktreeinput.md +56 -0
  209. package/docs/reference/authoring-surface/type-worktreekey.md +14 -0
  210. package/docs/reference/authoring-surface/type-worktreeoptions.md +29 -0
  211. package/docs/reference/authoring-surface/type-worktreeoutput.md +45 -0
  212. package/docs/reference/authoring-surface/type-worktreepath.md +16 -0
  213. package/docs/reference/authoring-surface/type-worktreeready.md +37 -0
  214. package/docs/reference/authoring-surface/type-worktreerefused.md +21 -0
  215. package/docs/reference/authoring-surface/workspace.md +287 -0
  216. package/docs/reference/authoring-surface/worktree.md +71 -0
  217. package/docs/reference/authoring-surface.md +217 -0
  218. package/docs/reference/cli.md +433 -0
  219. package/docs/reference/config.md +765 -0
  220. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +114 -0
  221. package/docs/reference/decisions/0002-duplex-streaming-actor.md +117 -0
  222. package/docs/reference/decisions/0004-worktree-provisioning-model.md +130 -0
  223. package/docs/reference/decisions/0005-sandbox-pod-composition.md +240 -0
  224. package/docs/reference/decisions/0006-agent-control-surface.md +50 -0
  225. package/docs/reference/decisions/0007-durable-machine-state.md +109 -0
  226. package/docs/reference/decisions/0008-orchestrator-as-deployed-app.md +60 -0
  227. package/docs/reference/decisions/0009-cli-and-instance-interface.md +275 -0
  228. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +160 -0
  229. package/docs/reference/decisions/0011-workflow-defined-events.md +141 -0
  230. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +103 -0
  231. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +175 -0
  232. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +95 -0
  233. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +271 -0
  234. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +126 -0
  235. package/docs/reference/decisions/0017-pool-over-a-source-port.md +71 -0
  236. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +111 -0
  237. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +287 -0
  238. package/docs/reference/decisions/0020-private-ca-trust-is-a-named-concept.md +60 -0
  239. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +101 -0
  240. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +103 -0
  241. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +175 -0
  242. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +436 -0
  243. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +65 -0
  244. package/docs/reference/decisions/0026-a-turn-that-is-over-is-never-prompted.md +62 -0
  245. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +245 -0
  246. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +90 -0
  247. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +154 -0
  248. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +100 -0
  249. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +193 -0
  250. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +61 -0
  251. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +126 -0
  252. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +57 -0
  253. package/docs/reference/decisions/0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md +92 -0
  254. package/docs/reference/decisions/0036-a-turn-compacts-its-own-context-mid-flight.md +138 -0
  255. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +196 -0
  256. package/docs/reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md +170 -0
  257. package/docs/reference/decisions/0039-image-garbage-collects-by-reachability.md +115 -0
  258. package/docs/reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md +64 -0
  259. package/docs/reference/decisions/0042-ready-is-not-routable-an-admission-retries-its-connection.md +142 -0
  260. package/docs/reference/decisions/0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md +170 -0
  261. package/docs/reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md +85 -0
  262. package/docs/reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md +82 -0
  263. package/docs/reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md +34 -0
  264. package/docs/reference/decisions/0047-the-git-ssh-key-source-is-the-users-choice.md +47 -0
  265. package/docs/reference/decisions/0048-the-orchestrator-boots-without-its-repos.md +36 -0
  266. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +126 -0
  267. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +73 -0
  268. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +238 -0
  269. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +86 -0
  270. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +123 -0
  271. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +126 -0
  272. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +107 -0
  273. package/docs/reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md +62 -0
  274. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +152 -0
  275. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +108 -0
  276. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +427 -0
  277. package/docs/reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md +111 -0
  278. package/docs/reference/decisions/0061-a-memory-kill-ends-the-agents-processes-not-the-harness.md +83 -0
  279. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +239 -0
  280. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +82 -0
  281. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +79 -0
  282. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +65 -0
  283. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +552 -0
  284. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +458 -0
  285. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +475 -0
  286. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +180 -0
  287. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +334 -0
  288. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +183 -0
  289. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +144 -0
  290. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +122 -0
  291. package/docs/reference/decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md +76 -0
  292. package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +295 -0
  293. package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +320 -0
  294. package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
  295. package/docs/reference/decisions.md +86 -0
  296. package/docs/reference/glossary.md +687 -0
  297. package/docs/reference/http-api.md +1386 -0
  298. package/docs/reference/machines.md +189 -0
  299. package/docs/reference/mechanism-events.md +72 -0
  300. package/docs/why.md +207 -0
  301. package/package.json +5 -3
  302. package/src/actor.ts +694 -106
  303. package/src/agent.ts +114 -38
  304. package/src/ambient.ts +3 -4
  305. package/src/assembly-checks.ts +189 -7
  306. package/src/cause.ts +234 -0
  307. package/src/chart.ts +345 -46
  308. package/src/charts.ts +43 -8
  309. package/src/config.ts +440 -178
  310. package/src/custodian.lua +17 -5
  311. package/src/custodian.ts +325 -99
  312. package/src/customize.ts +199 -49
  313. package/src/dispatch-effect.ts +945 -0
  314. package/src/door-compat.ts +217 -0
  315. package/src/durability.ts +11 -4
  316. package/src/effect-checks.ts +50 -3
  317. package/src/effect.ts +641 -128
  318. package/src/effector-wire.ts +165 -13
  319. package/src/fail.ts +222 -0
  320. package/src/harness-client.ts +587 -50
  321. package/src/harness-env.ts +33 -0
  322. package/src/held-secrets.ts +125 -16
  323. package/src/holds.ts +3 -0
  324. package/src/http-effect.ts +556 -58
  325. package/src/http.ts +480 -30
  326. package/src/index.ts +74 -82
  327. package/src/instance.ts +68 -8
  328. package/src/internal.ts +85 -0
  329. package/src/kube-client.ts +6 -3
  330. package/src/machine-doc.ts +24 -7
  331. package/src/mechanism-routes.ts +168 -0
  332. package/src/model-keys.generated.ts +53 -0
  333. package/src/model-keys.ts +42 -58
  334. package/src/names.ts +19 -6
  335. package/src/notices.ts +483 -41
  336. package/src/open-body.ts +242 -40
  337. package/src/open-effect.ts +81 -8
  338. package/src/open.ts +90 -6
  339. package/src/parts.ts +213 -30
  340. package/src/party.ts +101 -2
  341. package/src/pick.ts +497 -11
  342. package/src/pool.ts +282 -50
  343. package/src/registration.ts +133 -12
  344. package/src/repo-identity.ts +7 -3
  345. package/src/repos.ts +22 -3
  346. package/src/run-host.ts +665 -45
  347. package/src/run-output.ts +38 -0
  348. package/src/sandbox-effect.ts +457 -71
  349. package/src/sandbox-kube.ts +66 -17
  350. package/src/secret-placement.ts +59 -17
  351. package/src/server.ts +3 -0
  352. package/src/setup.ts +325 -62
  353. package/src/size.ts +121 -9
  354. package/src/snapshot-store.ts +54 -10
  355. package/src/stub-harness.ts +44 -16
  356. package/src/timers.ts +14 -1
  357. package/src/tokens.ts +107 -32
  358. package/src/vocabulary.ts +88 -14
  359. package/src/wire.ts +100 -26
  360. package/src/workspace.ts +771 -238
  361. package/src/worktree.ts +249 -0
package/src/config.ts CHANGED
@@ -34,34 +34,96 @@ import { assertSize, type Size } from "./size.ts";
34
34
  // credential against an arbitrary host. A bound url is code the Instance typechecked and deployed
35
35
  // — admitted without a match, cloned anonymously.
36
36
 
37
- /** One credentials entry. An entry may carry neither `token` nor `sshKey`: it then admits its
38
- * prefix through the fence and the clone is anonymous. */
37
+ // One credentials entry. An entry may carry neither `token` nor `sshKey`: it then admits its
38
+ // prefix through the fence and the clone is anonymous.
39
+ /** One entry of `git.credentials`: the credential that the cluster uses for the Repos whose
40
+ * identity starts with `match`. An entry with neither `token` nor `sshKey` admits its Repos
41
+ * through the fence, and they clone with no credential. */
39
42
  export type GitCredential = {
40
- /** A prefix on the identity (`github.com/ourorg/`), or `*` for everything. */
43
+ /** The Repos this entry is for: a prefix of a Repo's identity, such as `github.com/ourorg/`, or
44
+ * `*` for every Repo. The identity of a Repo is its host and path with no scheme, user or `.git`.
45
+ * When more than one entry matches, the longest `match` wins; on a tie, the first entry in the
46
+ * list wins. */
41
47
  match: string;
42
- /** Env var name holding an HTTPS token; `jr2 up` writes its value into the entry's clone Secret
43
- * (`jr2-git-<hash>`), which the cache agent spends. The Orchestrator never holds it (ADR-0068). */
48
+ // The cache agent spends the clone Secret; the Orchestrator never holds it (ADR-0068).
49
+ /** For an `https://` url: the name of the env var that holds a token, read from the shell or
50
+ * `.env` when `jr2 up` runs. `jr2 up` writes the value into a Secret (`jr2-git-<hash>`) that only
51
+ * the Repo cache agent reads. The Agent never sees it. */
44
52
  token?: string;
45
- /** A Secret (Flux key names: `identity`, `identity.pub`, `known_hosts`) holding a deploy key. */
53
+ /** For an ssh url: the name of a Secret that you create. It holds a deploy key under the Flux
54
+ * key names `identity`, `identity.pub` and `known_hosts`. */
46
55
  sshKey?: string;
47
- /** The credential a `push()` spends (ADR-0051, ADR-0067), when it is not the clone credential: an
48
- * ssh key (a Secret of `sshKey`'s shape) or a token (an env var, as `token`). Absent, the clone
49
- * credential pushes; an entry with neither admits a clone, never a push. A token goes Basic
50
- * through a credential helper only. */
51
- push?: { sshKey: string } | { token: string };
52
- /** The key a `push({ sign: true })` signs with, and the committer it signs as (ADR-0067): an ssh
53
- * key (a Secret with `identity`) by default, or a GPG key (a Secret with `private.asc`, no
54
- * passphrase). Explicit: an auth key is never reused as a signing key. The committer's email must
55
- * belong to the forge account that holds the key, or the forge shows the signature unverified. */
56
- sign?: { sshKey: string; name: string; email: string } | { gpgKey: string; name: string; email: string };
57
- /** Who an unsigned `push({ squash: true })` is committed by (ADR-0067): the squashed commit is
58
- * new, and without `sign` no signer names its committer. Its date is the Effector's clock; the
59
- * author line stays the Agent's. A signed push's committer is the signer's. */
60
- committer?: { name: string; email: string };
56
+ // The credential a `push()` spends (ADR-0051, ADR-0067). A token goes Basic through a credential
57
+ // helper only.
58
+ /** The credential that a `push()` Effect uses, when it is not the clone credential. Write
59
+ * `{ sshKey }` or `{ token }`. Default: the clone credential pushes. An entry with no credential
60
+ * admits a clone and never a push. When the push finds no usable credential, it is refused, for
61
+ * example: `no git.credentials entry matches "<identity>" — a push spends the entry its url
62
+ * matches`, or `git.credentials entry "<match>" holds no credential a push over <scheme> can
63
+ * spend`. */
64
+ push?:
65
+ | {
66
+ /** For an ssh url: the name of a Secret that you create, with the same keys as the
67
+ * entry's `sshKey`. */
68
+ sshKey: string;
69
+ }
70
+ | {
71
+ /** For an `https://` url: the name of the env var that holds the push token, read as for
72
+ * the entry's `token`. */
73
+ token: string;
74
+ };
75
+ // Explicit (ADR-0067): an auth key is never reused as a signing key.
76
+ /** The key that a `push({ sign: true })` signs with, and the committer it signs as:
77
+ * `{ sshKey, name, email }` or `{ gpgKey, name, email }`. `sshKey` names a Secret with
78
+ * `identity`. `gpgKey` names a Secret with `private.asc` and no passphrase. jr2 never signs with
79
+ * the clone or push key, so name the signing key here. The email must belong to the forge account
80
+ * that holds the key, or the forge shows the signature as unverified. A signed push without it is
81
+ * refused: `signed, and git.credentials entry "<match>" holds no sign field`. */
82
+ sign?:
83
+ | {
84
+ /** The name of a Secret that you create, which holds the ssh signing key under `identity`. */
85
+ sshKey: string;
86
+ /** The committer's name on the signed commit. */
87
+ name: string;
88
+ /** The committer's email. It must belong to the forge account that holds the key. */
89
+ email: string;
90
+ }
91
+ | {
92
+ /** The name of a Secret that you create, which holds the GPG signing key under
93
+ * `private.asc`, with no passphrase. */
94
+ gpgKey: string;
95
+ /** The committer's name on the signed commit. */
96
+ name: string;
97
+ /** The committer's email. It must belong to the forge account that holds the key. */
98
+ email: string;
99
+ };
100
+ // The squashed commit is new, and without `sign` no signer names its committer (ADR-0067).
101
+ /** Who commits an unsigned `push({ squash: true })`: `{ name, email }`. The squashed commit is
102
+ * new, so it needs a committer. Its date comes from the Effector's clock, and its author stays
103
+ * the Agent. A signed push is committed by the signer, so it does not read this field. A squashed
104
+ * push with neither field is refused: `squashed without sign, and git.credentials entry "<match>"
105
+ * holds neither sign nor committer`. */
106
+ committer?: {
107
+ /** The committer's name on the squashed commit. */
108
+ name: string;
109
+ /** The committer's email on the squashed commit. */
110
+ email: string;
111
+ };
61
112
  };
62
113
 
114
+ /** How the cluster authenticates to Repos. */
63
115
  export type GitConfig = {
64
- /** Matched by prefix on the Repo identity; the longest `match` wins, ties → first in the list. */
116
+ /** The credential entries, matched by prefix on the Repo identity. The longest `match` wins; on a
117
+ * tie, the first entry in the list wins.
118
+ *
119
+ * The list is also a fence. A per-run url, which comes from the run input, must match an entry,
120
+ * or the Sandbox refuses it when it attaches the Repo:
121
+ * `Sandbox "<name>" refuses the per-run repo <url> for slot "<slot>": no git.credentials entry matches "<identity>"`.
122
+ * Thus a run cannot spend this cluster's credentials against any host. A url that a Machine
123
+ * binds needs no entry. When no entry matches it, it clones with no credential.
124
+ *
125
+ * Each entry is checked when the config loads:
126
+ * `<file> at git.credentials[<i>]<path>: <problem>`, followed by the shape of an entry. */
65
127
  credentials?: readonly GitCredential[];
66
128
  };
67
129
 
@@ -110,21 +172,41 @@ export function isSshUrl(url: string): boolean {
110
172
  }
111
173
  }
112
174
 
113
- /** An env var on the Harness container, in the CR's (corev1.EnvVar) shape — `value` or a
114
- * `valueFrom` secret/configmap reference, passed through to the operator verbatim. */
175
+ // An env var on the Harness container, in the CR's (corev1.EnvVar) shape — `value` or a
176
+ // `valueFrom` secret/configmap reference, passed through to the operator verbatim.
177
+ /** One env var of the Harness container, in the Kubernetes `EnvVar` shape. The Agent can read it,
178
+ * so it must not hold a model key: put a model key in `secrets`. */
115
179
  export type HarnessEnvVar = {
180
+ /** The env var's name. A known model key name, such as `ANTHROPIC_API_KEY`, is refused:
181
+ * `harness.env[<i>] sets <NAME>, a model key the Agent could read and take out of the pod — hold it instead`. */
116
182
  name: string;
183
+ /** The value, usually read from `process.env`. `jr2 up` writes it into a Secret of the Instance.
184
+ * Write `value` or `valueFrom`. */
117
185
  value?: string;
186
+ /** Or a Kubernetes `valueFrom` reference, such as `{ secretKeyRef: { name, key } }`, given to the
187
+ * pod as it is. A reference to a Secret of the kit is refused:
188
+ * `harness.env[<i>] (<NAME>) reads Secret "<name>", which is the kit's and never the Harness's`. */
118
189
  valueFrom?: Record<string, unknown>;
119
190
  };
120
191
 
121
- /** A whole-Secret/ConfigMap env injection (corev1.EnvFromSource) for the Harness container —
122
- * e.g. `{ secretRef: { name: "anthropic" } }` to hand a real Harness its model API key. */
192
+ // A whole-Secret/ConfigMap env injection (corev1.EnvFromSource) for the Harness container.
193
+ /** A Secret or a ConfigMap whose keys all become env vars of the Harness container, in the
194
+ * Kubernetes `EnvFromSource` shape. Write `secretRef` or `configMapRef`. The Agent can read every
195
+ * key, so `jr2 up` refuses one that holds a known model key:
196
+ * `harness.envFrom[<i>] loads <where>, which carries <KEY> — a model key the Agent could read`. */
123
197
  export type HarnessEnvFromSource = {
124
- /** Put before every key's name, as corev1 does. */
198
+ /** Text put before each key's name, as Kubernetes does. Default: none. */
125
199
  prefix?: string;
126
- secretRef?: { name: string };
127
- configMapRef?: { name: string };
200
+ /** A Secret that you manage, by name: `{ name }`. `jr2 up` checks that it exists. */
201
+ secretRef?: {
202
+ /** The name of the Secret. */
203
+ name: string;
204
+ };
205
+ /** A ConfigMap that you manage, by name: `{ name }`. */
206
+ configMapRef?: {
207
+ /** The name of the ConfigMap. */
208
+ name: string;
209
+ };
128
210
  };
129
211
 
130
212
  /** This kit's version — npm version == published image tag, one release train (ADR-0019). */
@@ -134,235 +216,415 @@ export const KIT_VERSION = (
134
216
  }
135
217
  ).version;
136
218
 
137
- /** Token limits for one model — the Harness's provider-registration options, keyed per model because limits are
138
- * properties of the MODEL, not the endpoint (agents pick models per definition, ADR-0018). */
219
+ // Token limits for one model — the Harness's provider-registration options, keyed per model
220
+ // because limits are properties of the MODEL, not the endpoint (agents pick models per definition,
221
+ // ADR-0018).
222
+ /** The token limits of one model of a custom provider. */
139
223
  export type HarnessProviderModel = {
140
- /** The model's context window, in tokens (vLLM: `max_model_len`). */
224
+ /** The model's context window, in tokens (vLLM: `max_model_len`). Default: the provider's
225
+ * `contextWindow`. */
141
226
  contextWindow?: number;
142
- /** The model's max output tokens per completion. */
227
+ /** The most output tokens in one completion. Default: the provider's `maxTokens`. */
143
228
  maxTokens?: number;
144
229
  };
145
230
 
146
- /** A custom model provider (ADR-0018) — what the stock Harness registers with pi's
147
- * `registerProvider(id, { api, baseUrl, … })` (ADR-0027). The vLLM/Ollama path: an OpenAI-compatible
148
- * endpoint under an instance-chosen provider id. */
231
+ /** The wire protocols the Harness speaks for a custom provider (`harness.provider.api`, ADR-0018) —
232
+ * `CUSTOM_APIS` in `@jr2/harness`, whose test holds the two lists equal. `jr2 up` refuses any other
233
+ * value before it starts anything, as the Harness would refuse it at boot. */
234
+ export const CUSTOM_PROVIDER_APIS: readonly string[] = ["openai-completions"];
235
+
236
+ // A custom model provider (ADR-0018) — what the stock Harness registers with pi's
237
+ // `registerProvider(id, { api, baseUrl, … })` (ADR-0027). The vLLM/Ollama path: an
238
+ // OpenAI-compatible endpoint under an instance-chosen provider id.
239
+ /** A custom model provider: an endpoint that is not in the model catalog, such as vLLM or Ollama,
240
+ * under a provider id that you choose. An Agent names one of its models as `<id>/<model>`. */
149
241
  export type HarnessProvider = {
150
- /** The provider id model specifiers use (`<id>/<model>`), e.g. `vllm`. */
242
+ /** The provider id that model names start with: `vllm` in `vllm/Qwen/Qwen3-32B`. It must not
243
+ * also be a key of `harness.catalog`:
244
+ * `harness.catalog.<id>: "<id>" is also harness.provider.id — a custom provider owns its own baseUrl`. */
151
245
  id: string;
152
- /** The wire protocol, e.g. `openai-completions` (most OpenAI-compatible endpoints). */
246
+ /** The wire protocol. The only value is `"openai-completions"`, which most OpenAI-compatible
247
+ * endpoints speak, such as vLLM and Ollama. `jr2 up` refuses any other value before it builds or
248
+ * applies anything:
249
+ * `refusing: harness.provider.api is "<api>" — the Harness speaks: openai-completions (ADR-0018)`. */
153
250
  api: string;
154
- /** The endpoint — reachable FROM PODS (`localhost` never is; a LAN address works on kind).
155
- * Deployment-varying → resolve from env (`.env`), never hardcode (ADR-0019). */
251
+ // Deployment-varying → resolve from env (`.env`), never hardcode (ADR-0019).
252
+ /** The endpoint's URL. The pods must reach it: `localhost` never works, and a LAN address works
253
+ * on kind. It changes between deployments, so read it from `process.env` (`.env`). `jr2 up`
254
+ * checks from inside the cluster that the endpoint answers. */
156
255
  baseUrl: string;
157
- /** API key, when the endpoint wants one. May read `process.env`. It is a held secret
158
- * (ADR-0059): `jr2 up` gives the value to the Custodian alone, bound to `baseUrl`'s host, and the
159
- * Harness container holds only its Stand-in. So `baseUrl` must be `https://` when this is set.
160
- * Genuinely optional: an unauthenticated endpoint needs none (the Harness sends the wire
161
- * library's placeholder, which vLLM/Ollama ignore). */
256
+ // A held secret (ADR-0059). Genuinely optional: an unauthenticated endpoint needs none (the
257
+ // Harness sends the wire library's placeholder, which vLLM/Ollama ignore).
258
+ /** The API key, when the endpoint needs one. Read it from `process.env`. It is a Held secret,
259
+ * `JR2_PROVIDER_API_KEY`: `jr2 up` gives the value to the Custodian only, bound to the host of
260
+ * `baseUrl`, and the Harness process holds only its secret Stand-in. Thus `baseUrl` must be
261
+ * `https://` when you set it:
262
+ * `harness.provider.apiKey is set, and harness.provider.baseUrl is not https://`. Default: none,
263
+ * for an endpoint that needs no key. */
162
264
  apiKey?: string;
163
- /** Endpoint-wide default token limits, for any model this provider serves. Flue resolves
164
- * per-model → provider-level → catalog → 0, and a CUSTOM provider id has no catalog entry —
165
- * unset limits resolve to 0, which leaves auto-compaction no context budget to reason about
166
- * (the wire request itself is fine: a 0 `maxTokens` is omitted, never sent). */
265
+ // Limits resolve per-model → provider-level → 0, and a CUSTOM provider id has no catalog entry —
266
+ // unset limits resolve to 0, which leaves auto-compaction no context budget to reason about. The
267
+ // wire request sends no limit for a 0 `maxTokens`: pi-ai omits it when the context window is
268
+ // known, and the Harness removes the 1 that pi-ai would send when neither is (`unlimitedPayload`).
269
+ /** The context window, in tokens, of each model of this provider that `models` does not set.
270
+ * Set it here or in `models`: a limit that nothing sets is 0, and then Compaction has no context
271
+ * budget. */
167
272
  contextWindow?: number;
273
+ /** The most output tokens in one completion, for each model of this provider that `models` does
274
+ * not set. Default: no limit is sent. */
168
275
  maxTokens?: number;
169
- /** Per-model limits, keyed by the model id AFTER the provider prefix — the map entry for
170
- * `vllm/Qwen/Qwen3-32B` is `"Qwen/Qwen3-32B"`. Committable model properties, not
171
- * deployment-varying: whichever model an Agent definition names resolves its own entry. */
276
+ // Committable model properties, not deployment-varying: whichever model an Agent definition names
277
+ // resolves its own entry.
278
+ /** Token limits for each model, keyed by the model id after the provider prefix: the key for
279
+ * `vllm/Qwen/Qwen3-32B` is `"Qwen/Qwen3-32B"`. A limit set here wins over the provider's. */
172
280
  models?: Record<string, HarnessProviderModel>;
173
281
  };
174
282
 
175
- /** Where one of pi's catalog providers sends its calls (ADR-0059) — reach, not a model choice
176
- * (ADR-0018). The catalog keeps every model fact (limits, cost, thinking levels); this changes only
177
- * the endpoint, for a gateway that serves the provider's own API (LiteLLM, for example). */
283
+ // Where one of pi's catalog providers sends its calls (ADR-0059) — reach, not a model choice
284
+ // (ADR-0018). The catalog keeps every model fact (limits, cost, thinking levels).
285
+ /** Where the calls of one catalog provider go: a gateway that serves that provider's own API, such
286
+ * as LiteLLM. Only the endpoint changes. The catalog keeps every fact about the models. */
178
287
  export type CatalogReach = {
179
- /** Replaces `model.baseUrl` for every model of this provider — the WHOLE base the provider's API
180
- * appends to (`https://litellm.corp.example` for `anthropic`, which appends `/v1/messages`;
181
- * `https://litellm.corp.example/v1` for `openai`). HTTPS only. Deployment-varying → `.env`. */
288
+ // Replaces `model.baseUrl` for every model of this provider.
289
+ /** The whole base URL that the provider's API adds its paths to: `https://litellm.corp.example`
290
+ * for `anthropic`, which adds `/v1/messages`; `https://litellm.corp.example/v1` for `openai`. It
291
+ * must be `https://`: `harness.catalog.<id>.baseUrl must be an https:// URL`. It changes between
292
+ * deployments, so read it from `process.env` (`.env`). */
182
293
  baseUrl: string;
183
294
  };
184
295
 
296
+ // One secret of the Instance (ADR-0059's entry shape, ADR-0068's list). Its placement is READ,
297
+ // never declared, and naming is placement: a pod gets the secrets something in it names. Both is
298
+ // both, and `jr2 up` reports it per pod. `hosts`, `headers` and `paths` are the binding, never the
299
+ // placement.
185
300
  /**
186
- * One secret of the Instance (ADR-0059's entry shape, ADR-0068's list). Its placement is READ, never
187
- * declared, and naming is placement: a pod gets the secrets something in it names. A Held secret is
188
- * one a Harness pod names — a `workspace()` lists it under `secrets: [...]`, or it is a known model
189
- * key the provider of an Agent whose Turns run there reads — and there the Harness container's env
190
- * carries its Stand-in, `jr2-held-<name>`, and that pod's Custodian puts the value into requests
191
- * toward `hosts` alone. An entry an Effect names with `secret("name")` is an Effect secret, a file in
192
- * that Effect's Effector. Both is both, and `jr2 up` reports it per pod; an entry nothing names goes
193
- * nowhere, and `jr2 up` warns. `hosts`, `headers` and `paths` are the binding, never the placement.
301
+ * One secret of the Instance. You do not say where it goes: it goes where something names it.
302
+ *
303
+ * - It is a Held secret in each Harness pod that names it. A pod names it when its `workspace()`
304
+ * lists it under `secrets`, or when it is the model key of a provider that an Agent there uses. The Harness
305
+ * process then holds only its Stand-in, a secret value that starts `jr2-held-<name>.`, and the
306
+ * pod's Custodian puts the value into requests toward `hosts` only. The Agent's programs get the
307
+ * Stand-in, in their env under this name, only when the `workspace()` lists it under `secrets`.
308
+ * - It is an Effect secret where an Effect names it with `secret("name")`: a file in that
309
+ * Effect's Effector.
310
+ *
311
+ * One secret can be both. Its `hosts`, `headers` and `paths` are its binding, never its placement:
312
+ * they say where the value may go, not which pod gets it. `jr2 up` warns about an entry that nothing names:
313
+ * `secrets[<i>] (<NAME>) is named by nothing; goes nowhere`.
194
314
  */
195
315
  export type SecretEntry = {
196
- /** As a Held secret, the env var the Harness container sees — `[A-Z_][A-Z0-9_]*`, holding the
197
- * Stand-in. As an Effect secret, the name `secret("name")` names and the Effector's file. */
316
+ /** As a Held secret, the name of its Stand-in: the file the Harness reads it from, the env var
317
+ * that the engine reads a model key from, and the env var of the Agent's programs when the
318
+ * `workspace()` lists it under `secrets`. As an Effect secret, the name that `secret("<name>")` names, and the name of the
319
+ * Effector's file. For a Held secret, it must match `[A-Z_][A-Z0-9_]*`:
320
+ * `secrets[<i>].name "<name>" is not an env var name ([A-Z_][A-Z0-9_]*)`. It must not start with
321
+ * `JR2_` and must not be a proxy or trust variable of the kit:
322
+ * `secrets[<i>].name <NAME> is the kit's — JR2_ names and the proxy and trust variables belong to the mechanism`.
323
+ * One name, one entry: `secrets[<i>].name <NAME> is held twice`. */
198
324
  name: string;
199
- /** The value as a literal, usually read from `process.env`/`.env`. `jr2 up` writes it into the
200
- * Secret of each placement that names it — `jr2-held-secrets-<pod>` (that pod's Custodian's),
201
- * `jr2-instance-effector` — and nowhere else. Exactly one of `value`/`valueFrom`. */
325
+ // `jr2 up` writes it into the Secret of each placement that names it —
326
+ // `jr2-held-secrets-<pod>` (that pod's Custodian's), `jr2-instance-effector` — and nowhere else.
327
+ /** The value, usually read from `process.env` (`.env`). `jr2 up` writes it only into the Secrets
328
+ * of the containers that hold it. Write exactly one of `value` and `valueFrom`:
329
+ * `secrets[<i>] needs exactly one of value or valueFrom`. A `.env` variable that is not set reads
330
+ * as no value, and an empty one is refused: `secrets[<i>].value is empty — set <NAME> in .env`. */
202
331
  value?: string;
203
- /** Or a key of a Secret you manage (Sealed Secrets, External Secrets). `jr2 up` checks that the
204
- * Secret and key exist; each placement mounts that one key as a file, and no other container does. */
205
- valueFrom?: { secretKeyRef: { name: string; key: string } };
206
- /** Where the value may go as a Held secret — its binding, not its placement. Each entry is `host`,
207
- * `host:port`, or an `https://`
208
- * URL (its host and port are taken; the path is ignored). Exact DNS names or IP literals; no
209
- * wildcards. Port default 443. A known model-key name (`MODEL_KEYS`) may leave it out: it then
210
- * goes to the one host that provider's calls go to. */
332
+ /** Or a key of a Secret that you manage (Sealed Secrets, External Secrets):
333
+ * `{ secretKeyRef: { name, key } }`. `jr2 up` checks that the Secret and the key exist. Each
334
+ * container that holds the secret mounts that one key as a file, and no other container does. */
335
+ valueFrom?: {
336
+ /** The Secret and the key in it that hold the value. */
337
+ secretKeyRef: {
338
+ /** The name of the Secret. */
339
+ name: string;
340
+ /** The key in the Secret that holds the value. */
341
+ key: string;
342
+ };
343
+ };
344
+ // A known model-key name is a key of `MODEL_KEYS` (model-keys.ts), which holds its default host.
345
+ /** Where the value may go as a Held secret. Each entry is `host`, `host:port`, or an `https://`
346
+ * URL, of which only the host and port count. Write exact DNS names or IP addresses, with no
347
+ * wildcards. The port default is 443. A known model key, such as `ANTHROPIC_API_KEY`, may leave
348
+ * it out: it then goes to the one host that its provider calls. Any other Held secret must name
349
+ * its hosts: `secrets[<i>].hosts is absent, and <NAME> has no single default host`. */
211
350
  hosts?: readonly string[];
212
- /** Request headers the Stand-in is swapped in, lowercase. Default
213
- * `["authorization", "x-api-key", "x-goog-api-key", "api-key"]`. */
351
+ /** The request headers in which the Custodian puts the value in place of the Stand-in,
352
+ * lowercase. Default: `["authorization", "x-api-key", "x-goog-api-key", "api-key"]`. A header
353
+ * that frames or routes the request is refused:
354
+ * `secrets[<i>].headers[<j>] "<header>" cannot carry a held secret`. */
214
355
  headers?: readonly string[];
215
- /** Optional path prefixes the value may be sent to (`/v1/messages`). Absent → every path. A
216
- * gateway key that can reach admin routes (a LiteLLM key that can mint keys) should narrow this:
217
- * a key minted through the Custodian is a key the Agent can take away. */
356
+ // A key minted through the Custodian is a key the Agent can take away.
357
+ /** The path prefixes to which the value may be sent, such as `/v1/messages`. Each starts with
358
+ * `/`. Default: every path. Narrow it for a gateway key that can reach admin routes, such as a
359
+ * LiteLLM key that can make new keys. The Agent could take away a key made that way. */
218
360
  paths?: readonly string[];
219
361
  };
220
362
 
221
- /** The agent-runtime section (ADR-0018): what the stock Harness image consumes alongside the
222
- * Agent definition each Turn hands it (ADR-0049). Moved out of `sandbox` deliberately — `sandbox`
223
- * is pod transport (it still CARRIES this env to the Harness container), but model concerns are
224
- * Harness semantics.
225
- *
226
- * It declares what this instance can REACH — endpoints, credentials, trust — and never WHICH
227
- * model to use (ADR-0018). This config holds no instance-wide `model` default and no Agent
228
- * roster: an Agent is an actor slot on ONE Machine, whose definition rides the Turn (ADR-0049).
229
- * One definition value may still be carried by several Machines, so the variation that matters is
230
- * per-definition and per-invocation, which one global default serves not at all. */
363
+ // The agent-runtime section (ADR-0018): what the stock Harness image consumes alongside the Agent
364
+ // definition each Turn hands it (ADR-0049). Moved out of `sandbox` deliberately — `sandbox` is pod
365
+ // transport (it still CARRIES this env to the Harness container), but model concerns are Harness
366
+ // semantics.
367
+ //
368
+ // It declares what this instance can REACH — endpoints, credentials, trust — and never WHICH
369
+ // model to use (ADR-0018). This config holds no instance-wide `model` default and no Agent
370
+ // roster: an Agent is an actor slot on ONE Machine, whose definition rides the Turn (ADR-0049).
371
+ // One definition value may still be carried by several Machines, so the variation that matters is
372
+ // per-definition and per-invocation, which one global default serves not at all.
373
+ /** What the Harness can reach: the model endpoints, their credentials, and the CAs it trusts. It
374
+ * never says which model an Agent uses: each Agent's definition names its model. */
231
375
  export type HarnessConfig = {
232
- /** Custom model provider, preflighted from inside the cluster by `jr2 up` (ADR-0019). Its
233
- * `apiKey` is a held secret, `JR2_PROVIDER_API_KEY`, bound to `baseUrl`'s host (ADR-0059). */
376
+ // Preflighted from inside the cluster by `jr2 up` (ADR-0019); its `apiKey` is held (ADR-0059).
377
+ /** A custom model provider, such as a vLLM or Ollama endpoint. See {@link HarnessProvider}.
378
+ * Default: none; the Agents use catalog providers only. */
234
379
  provider?: HarnessProvider;
235
- /** pi catalog providers whose calls go somewhere else, keyed by the catalog provider id
236
- * (`anthropic`, `openai`, ...) — a gateway that serves the provider's own API (ADR-0059). */
380
+ // A gateway that serves the provider's own API (ADR-0059).
381
+ /** Catalog providers whose calls go to another endpoint, such as a gateway, keyed by the catalog
382
+ * provider id: `anthropic`, `openai`, and so on. See {@link CatalogReach}. A key that is not a
383
+ * catalog provider with a known model key is refused:
384
+ * `harness.catalog.<id>: "<id>" is not a pi catalog provider jr2 knows a key for`. Default:
385
+ * each provider's own endpoint. */
237
386
  catalog?: Readonly<Record<string, CatalogReach>>;
238
- /** Env vars for the Harness container. Values read from `process.env`/`.env` are materialized
239
- * into the instance-owned Secret by `jr2 up`. The Agent can read every one of them, so a known
240
- * model key here is refused — it goes in the top-level `secrets` list (ADR-0059). */
387
+ // Values read from `process.env`/`.env` are materialized into the instance-owned Secret by
388
+ // `jr2 up`. A known model key here is refused (ADR-0059).
389
+ /** Env vars for the Harness container. The Agent can read every one of them, so do not put a
390
+ * credential here. A known model key is refused here. Put it in the top-level `secrets` list.
391
+ * The Harness process gets each one, except a name that loads code from a path, such as
392
+ * `NODE_OPTIONS` or `LD_PRELOAD`, and `HOME`, which is its own; the Agent's programs get each
393
+ * one. A name that starts with `JR2_` is jr2's own, and is refused. A credential file that the
394
+ * Harness process reads, such as `AWS_CONFIG_FILE`, is refused in a folder that the Agent's code
395
+ * writes (`/work`, `/tmp`, `/home/jr2`), and gives a warning anywhere else.
396
+ * See {@link HarnessEnvVar}. */
241
397
  env?: readonly HarnessEnvVar[];
242
- /** Whole-Secret/ConfigMap env for the Harness container — `envFrom` refs to Secrets YOU manage
243
- * (Sealed Secrets etc.); `jr2 up` preflights that each referenced Secret exists (ADR-0019), and
244
- * refuses one that carries a known model key (ADR-0059). */
398
+ // `jr2 up` preflights that each referenced Secret exists (ADR-0019), and refuses one that
399
+ // carries a known model key (ADR-0059).
400
+ /** Secrets and ConfigMaps that you manage, whose keys all become env vars of the Harness
401
+ * process and of the Agent's programs, as `env` does. In the container's spec each key has the
402
+ * prefix `JR2_ENVFROM_`, so that the Harness can tell jr2's names from the image's; the Harness
403
+ * removes it. A key that starts with `JR2_` is refused: `jr2 up` refuses it, and the Harness does
404
+ * not start with one. See {@link HarnessEnvFromSource}. */
245
405
  envFrom?: readonly HarnessEnvFromSource[];
246
- /** Path to a PEM CA bundle, RELATIVE to the instance folder — commit the file (CA certs are
247
- * public; e.g. an internal CA in front of a LAN vLLM). Only `jr2 up` reads it (host-side): it
248
- * materializes the `jr2-ca` ConfigMap and runs the provider preflight with the same trust. The
249
- * Harness container trusts it, and so does the Custodian when it verifies a bound host
250
- * (ADR-0020, ADR-0059). */
406
+ // Only `jr2 up` reads it (host-side): it materializes the `jr2-ca` ConfigMap and runs the
407
+ // provider preflight with the same trust (ADR-0020, ADR-0059).
408
+ /** A path to a PEM CA bundle, relative to the Instance folder. An example is the CA of an
409
+ * internal endpoint in front of a LAN vLLM. Commit the file: CA certificates are public. `jr2 up`
410
+ * puts it in the `jr2-ca` ConfigMap and uses it when it checks the provider. The Harness
411
+ * container trusts it, and so does the Custodian when it verifies a host that a Held secret is
412
+ * bound to. */
251
413
  caBundle?: string;
252
414
  };
253
415
 
254
- /** One pod-spec toleration, verbatim (ADR-0052): `nodeSelector` and `tolerations` are the raw
255
- * Kubernetes shapes, the stance `HarnessEnvVar` takes for `EnvVar`. */
416
+ // One pod-spec toleration, verbatim (ADR-0052): `nodeSelector` and `tolerations` are the raw
417
+ // Kubernetes shapes, the stance `HarnessEnvVar` takes for `EnvVar`.
418
+ /** One toleration of a Sandbox pod, in the Kubernetes `Toleration` shape: a node taint that the
419
+ * pod tolerates. */
256
420
  export type Toleration = {
421
+ /** The taint key. Leave it out, with `operator: "Exists"`, to tolerate every taint. */
257
422
  key?: string;
423
+ /** `"Equal"` matches a taint whose value is `value`; `"Exists"` matches any value. Default:
424
+ * `"Equal"`. */
258
425
  operator?: "Exists" | "Equal";
426
+ /** The taint value, for `operator: "Equal"`. */
259
427
  value?: string;
428
+ /** The taint effect to tolerate: `"NoSchedule"`, `"PreferNoSchedule"` or `"NoExecute"`. Default:
429
+ * every effect. */
260
430
  effect?: "NoSchedule" | "PreferNoSchedule" | "NoExecute";
431
+ /** For `"NoExecute"`: how many seconds the pod stays on the node after the taint is added.
432
+ * Default: the pod stays. */
261
433
  tolerationSeconds?: number;
262
434
  };
263
435
 
264
- /**
265
- * Where an Instance's Sandboxes may land (ADR-0052). Absent, a Sandbox node is wherever an
266
- * ordinary pod lands — not cordoned, no taint — and no jr2 label is ever required. Both keys ride
267
- * the Sandbox pod verbatim, the Repo cache agent's DaemonSet takes the same two so a cache is only
268
- * ever where a Sandbox can reach it, and `jr2 up` reads the same predicate off the nodes to report
269
- * the set. A Machine says nothing about placement: a node label is a deployment fact (ADR-0050).
270
- * These two are the default class; a per-Machine `classes` map is the deferred extension.
271
- */
436
+ // Where an Instance's Sandboxes may land (ADR-0052). Both keys ride the Sandbox pod verbatim, the
437
+ // Repo cache agent's DaemonSet takes the same two so a cache is only ever where a Sandbox can reach
438
+ // it, and `jr2 up` reads the same predicate off the nodes to report the set. A Machine says nothing
439
+ // about placement: a node label is a deployment fact (ADR-0050). These two are the default class;
440
+ // a per-Machine `classes` map is the deferred extension.
441
+ /** Which nodes are Sandbox nodes: the nodes on which this Instance's Sandboxes may run. Absent, a
442
+ * Sandbox runs wherever an ordinary pod runs: on a node that is not cordoned and has no taint. No
443
+ * jr2 label is ever needed. The Repo cache agent runs on the same nodes, and `jr2 up` reports the
444
+ * set and warns when it is empty. */
272
445
  export type SandboxPlacement = {
273
- /** Pod `spec.nodeSelector`: a node must carry every label. */
446
+ /** The pod's `nodeSelector`: a Sandbox node must have every one of these labels. Default: none. */
274
447
  nodeSelector?: Readonly<Record<string, string>>;
275
- /** Pod `spec.tolerations`, verbatim — no `tolerationSeconds` is added, so a tolerated `NoExecute`
276
- * taint keeps a Sandbox through it for as long as its Lease is renewed. */
448
+ // No `tolerationSeconds` is added.
449
+ /** The pod's `tolerations`, as you write them. Default: none. jr2 adds no `tolerationSeconds`.
450
+ * Thus a tolerated `NoExecute` taint keeps a Sandbox on its node while its Lease is renewed. */
277
451
  tolerations?: readonly Toleration[];
278
452
  };
279
453
 
280
- /**
281
- * The Instance's Sandbox defaults (ADR-0052, ADR-0060): placement, and the Size for a Workspace
282
- * that states none.
283
- */
454
+ // The Instance's Sandbox defaults (ADR-0052, ADR-0060).
455
+ /** The Instance's Sandbox defaults: the Sandbox nodes (see {@link SandboxPlacement}), and the Size
456
+ * of a Workspace that states none. The config load refuses a `sandbox` that is not an object:
457
+ * `<file> at sandbox: expected an object — { nodeSelector?, tolerations?, resources? }`. A key that
458
+ * this type does not have fails the typecheck that `jr2 up` runs. */
284
459
  export type SandboxConfig = SandboxPlacement & {
460
+ // NEVER an override of a Size a Machine states: a Size is a fact of the Machine, and its one
461
+ // override path is `customize(machine, { resources })` (ADR-0060).
285
462
  /**
286
- * The Instance's default Size (ADR-0060) — `{ limits: { memory, cpu } }` and nothing else — for
287
- * a Workspace that states no Size at all; the kit fills what it leaves out. NEVER an override of a Size a Machine states: a
288
- * Size is a fact of the Machine, and its one override path is `customize(machine, { resources })`.
289
- * Absent → the kit default, memory 2Gi and cpu 1. jr2 reserves the whole Size (every request
290
- * equals its limit) and enforces the CPU limit.
463
+ * The Size of a Workspace that states no Size at all: `{ limits: { memory, cpu } }` and nothing
464
+ * else. The kit default fills a field that it leaves out. It never changes a Size that a Machine
465
+ * states: to change that, use `customize(machine, { resources })`. Default: the kit default,
466
+ * memory `2Gi` and cpu `1`. jr2 reserves the whole Size and enforces the cpu limit. See
467
+ * {@link Size} for the refusals, such as
468
+ * `<file> at sandbox.resources.requests is not accepted`.
291
469
  */
292
470
  resources?: Size;
293
471
  };
294
472
 
473
+ // The PriorityClasses jr2's pods run under (ADR-0060). Absent, `jr2 up` creates two,
474
+ // cluster-scoped, beside the CRDs.
295
475
  /**
296
- * The PriorityClasses jr2's pods run under (ADR-0060). Absent, `jr2 up` creates two, cluster-scoped,
297
- * beside the CRDs: `jr2-control` (100000, PreemptLowerPriority) for the Orchestrator, the operator
298
- * and the Repo cache agent, and `jr2-sandbox` (1000, preemptionPolicy Never) for Sandboxes and the
299
- * Instance Harness — so an ordinary pod cannot preempt a live Workspace, and a waiting Sandbox
300
- * evicts nobody. A cluster with its own scheme names existing classes here, and `jr2 up` then
301
- * creates none for that key.
476
+ * The PriorityClasses that jr2's pods run under. Absent, `jr2 up` creates two: `jr2-control`
477
+ * (value 100000, `PreemptLowerPriority`) and `jr2-sandbox` (value 1000, `preemptionPolicy: Never`).
478
+ * Thus an ordinary pod cannot preempt a live Workspace, and a Sandbox that waits for a node evicts
479
+ * no pod. A cluster with its own scheme names its existing classes here, and `jr2 up` then creates
480
+ * none for that key. The config load refuses any other key:
481
+ * `<file> at priorityClasses<path>: <problem> — { control?, sandbox? }, each the name of an existing PriorityClass`.
302
482
  */
303
483
  export type PriorityClasses = {
304
- /** An existing PriorityClass for the Orchestrator, the operator and the Repo cache agent. */
484
+ /** An existing PriorityClass for the Orchestrator, the operator and the Repo cache agent.
485
+ * Default: `jr2-control`, which `jr2 up` creates. */
305
486
  control?: string;
306
- /** An existing PriorityClass for Sandboxes and the Instance Harness. */
487
+ /** An existing PriorityClass for the Sandboxes and the Instance Harness. Default: `jr2-sandbox`,
488
+ * which `jr2 up` creates. */
307
489
  sandbox?: string;
308
490
  };
309
491
 
492
+ /**
493
+ * The shape of `jr2.config.ts`: the deployment facts of an Instance, which a Machine cannot carry.
494
+ * It holds the Instance's name, its secrets, how the cluster authenticates to Repos, what the
495
+ * Harness can reach, where Sandboxes run, and where images go. It names no Repo, no model and no
496
+ * Sandbox Image: a Machine names those itself.
497
+ *
498
+ * Every field is optional, and an Instance can run with no `jr2.config.ts`. Write the file as
499
+ * `export default defineConfig({ … })`. When the config loads, a file with no default export is
500
+ * refused: `` <file> has no default export (use `export default defineConfig({…})`) ``. The load also
501
+ * checks the shape of `git.credentials`, `secrets`, `harness.catalog` and `priorityClasses`. It
502
+ * checks that `sandbox` is an object, and checks its `resources`. Each refusal names the field.
503
+ * `jr2 up` checks the rest.
504
+ *
505
+ * @example
506
+ * ```ts
507
+ * import { defineConfig, type JR2Config } from "@jr2/orchestrator";
508
+ *
509
+ * const harness: JR2Config["harness"] = {
510
+ * provider: {
511
+ * id: "vllm",
512
+ * api: "openai-completions",
513
+ * baseUrl: process.env.VLLM_URL ?? "",
514
+ * models: { "Qwen/Qwen3.6-35B-A3B": { contextWindow: 131072, maxTokens: 16384 } },
515
+ * },
516
+ * };
517
+ *
518
+ * export default defineConfig({ name: "my-instance", harness });
519
+ * ```
520
+ *
521
+ * @see docs/reference/config.md
522
+ * @see docs/get-started/connect-a-model.md
523
+ * @see docs/concepts/secrets-and-trust.md
524
+ * @see docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md
525
+ */
310
526
  export type JR2Config = {
311
- /** The instance's identity (ADR-0019): its kube namespace defaults to this (`-n` overrides),
312
- * and `jr2 up` labels every object it owns with it. Default: the instance folder's name. */
527
+ // The instance's identity (ADR-0019).
528
+ /** The Instance's name. Its Kubernetes namespace is this name (`-n` overrides it), and
529
+ * `jr2 up` labels every object that it owns with it. Default: the name of the Instance folder. */
313
530
  name?: string;
314
- /** Every secret of the Instance, one list for both sides (ADR-0068). Naming is the placement: a
315
- * Held secret where a `workspace()` lists it under `secrets: [...]` or an Agent's provider reads
316
- * it — a model API key belongs here, never in `harness.env` (ADR-0059) — and an Effect secret
317
- * where an Effect names it with `secret("name")`. `hosts` is a Held secret's binding, never its
318
- * placement: an entry nothing names goes nowhere. See `SecretEntry`. */
531
+ // One list for both sides (ADR-0068). Naming is the placement; `hosts` is a Held secret's
532
+ // binding, never its placement: an entry nothing names goes nowhere.
533
+ /** Every secret of the Instance, in one list. A secret goes where something names it. A Held
534
+ * secret goes where a `workspace()` lists it under `secrets` or an Agent's provider reads it. An
535
+ * Effect secret goes where an Effect names it with `secret("name")`. Its `hosts` is a Held
536
+ * secret's binding, never its placement: an entry that nothing names goes nowhere. A model API
537
+ * key goes here, never in `harness.env`. See {@link SecretEntry}. Each entry is checked when the
538
+ * config loads: `<file> at secrets[<i>]<path>: <problem>`, followed by the shape of an entry. */
319
539
  secrets?: readonly SecretEntry[];
320
- /** How the cluster authenticates to Repos, and the fence a per-run url must pass (ADR-0051).
321
- * NOT a list of Repos: a Machine names those itself, through its `workspace()`'s Repo Slots, and
322
- * whether the instance has a data plane at all is read off the registered Machines. */
540
+ // NOT a list of Repos (ADR-0051): a Machine names those itself, through its `workspace()`'s Repo
541
+ // Slots, and whether the instance has a data plane at all is read off the registered Machines.
542
+ /** How the cluster authenticates to Repos, and the fence that a per-run url must pass. It is not
543
+ * a list of Repos: a Machine names its Repos in the Repo Slots of its `workspace()`. See
544
+ * {@link GitConfig}. */
323
545
  git?: GitConfig;
324
- /** Agent-runtime config for the stock Harness (see `HarnessConfig`). */
546
+ /** What the Harness can reach: a custom model provider, gateways for catalog providers, env
547
+ * vars, and CAs. See {@link HarnessConfig}. */
325
548
  harness?: HarnessConfig;
326
- /** Which nodes are this Instance's Sandbox nodes (see `SandboxPlacement`, ADR-0052), and the
327
- * Size of a Workspace that states none (see `SandboxConfig`, ADR-0060). Absent → wherever an
328
- * ordinary pod lands, at the kit's Size. */
549
+ // See `SandboxPlacement` (ADR-0052) and `SandboxConfig` (ADR-0060).
550
+ /** Which nodes are Sandbox nodes, and the Size of a Workspace that states none. Default: a
551
+ * Sandbox runs wherever an ordinary pod runs, at the kit's Size. See {@link SandboxConfig}. */
329
552
  sandbox?: SandboxConfig;
330
- /** Existing PriorityClasses to use instead of the two `jr2 up` creates (see `PriorityClasses`,
331
- * ADR-0060). */
553
+ // See `PriorityClasses` (ADR-0060).
554
+ /** Existing PriorityClasses to use in place of the two that `jr2 up` creates. See
555
+ * {@link PriorityClasses}. */
332
556
  priorityClasses?: PriorityClasses;
333
- /** Image registry prefix (deployment-varying — resolve from env). Absent → images are
334
- * `kind load`-ed; present → pushed. A non-kind cluster without one fails loudly (ADR-0019). */
557
+ // Deployment-varying — resolve from env (ADR-0019).
558
+ /** The image registry prefix to which `jr2 up` pushes the images that it builds, such as
559
+ * `registry.example.com/team`. It changes between deployments, so read it from `process.env`
560
+ * (`.env`). Default: none; `jr2 up` loads the images into the kind cluster. On a cluster that is
561
+ * not kind, `jr2 up` then refuses:
562
+ * `` context <context> is not a kind cluster and no `registry` is configured ``. */
335
563
  registry?: string;
336
- /** Where this cluster pulls the PUBLISHED Kit images from (deployment-varying — resolve from
337
- * env). Absent → the canonical home, `ghcr.io/snapwich/jr2-harness:<kitversion>` and friends;
338
- * present → the same tags re-homed to a self-hosted mirror, `<kitRegistry>/jr2-harness:<ver>`,
339
- * for a self-hosted, air-gapped, or mirror-only cluster (ADR-0044). Seeding that mirror is a
340
- * deliberate, instance-less act (`jr2 kit push`), never a side effect of `jr2 up`.
341
- *
342
- * Separate from `registry` on purpose: `registry` addresses images THIS converge builds,
343
- * `kitRegistry` addresses artifacts the kit already published. One key for both would make every
344
- * private-registry user mirror the Kit images they could have pulled from the home. */
564
+ // Deployment-varying — resolve from env. Seeding that mirror is a deliberate, instance-less act
565
+ // (`jr2 kit push`), never a side effect of `jr2 up` (ADR-0044).
566
+ //
567
+ // Separate from `registry` on purpose: `registry` addresses images THIS converge builds,
568
+ // `kitRegistry` addresses artifacts the kit already published. One key for both would make every
569
+ // private-registry user mirror the Kit images they could have pulled from the home.
570
+ /** The registry from which this cluster pulls the published Kit images: a mirror for a cluster
571
+ * that is self-hosted, air-gapped, or mirror-only. Default: none; the cluster pulls from the
572
+ * canonical home, `ghcr.io/snapwich/jr2-harness:<version>` and the others. Set, the cluster pulls
573
+ * the same tags from `<kitRegistry>/jr2-harness:<version>`. Fill the mirror with
574
+ * `jr2 kit push <registry>`; `jr2 up` never pushes to it. Read it from `process.env` (`.env`). */
345
575
  kitRegistry?: string;
346
- /** What `jr2 up` builds its images FOR — docker platform strings, e.g. `["linux/arm64"]`
347
- * (deployment-varying — resolve from env). Absent → derived from the cluster's schedulable nodes
348
- * and intersected with the platforms the kit releases for, which is the answer for every ordinary
349
- * cluster (ADR-0045). Present → ABSOLUTE: derivation is skipped and this is the build set (still
350
- * intersected, so an unpublished platform is a named error, never a silent build).
576
+ // Deployment-varying — resolve from env (ADR-0045). Present → ABSOLUTE: derivation is skipped
577
+ // (still intersected, so an unpublished platform is a named error, never a silent build).
578
+ //
579
+ // The escape hatch for the two cases derivation cannot see: a pool that autoscales from zero (no
580
+ // nodes to read yet), and a polluted set (an amd64 GPU pool beside arm64 workers, where the
581
+ // derived pair would cost a needless qemu cross-build). Not additive or subtractive.
582
+ /** The platforms for which `jr2 up` builds its images, as docker platform strings, such as
583
+ * `["linux/arm64"]`. Default: the platforms of the cluster's schedulable nodes that the kit
584
+ * releases images for. `jr2 up` reports a node platform that it skips.
351
585
  *
352
- * The escape hatch for the two cases derivation cannot see: a pool that autoscales from zero (no
353
- * nodes to read yet), and a polluted set (an amd64 GPU pool beside arm64 workers, where the
354
- * derived pair would cost a needless qemu cross-build). Not additive or subtractive. */
586
+ * Set it when the nodes cannot tell. One case is a node group that scales up from zero. Another
587
+ * is a set of nodes that need no image, such as an amd64 GPU group beside arm64 nodes. When you
588
+ * set it, it replaces the default. It adds to nothing and removes from nothing. The kit releases
589
+ * images for `linux/amd64` and `linux/arm64` only, so `jr2 up` refuses any other:
590
+ * `` `platforms` names <platforms> — the kit releases images for linux/amd64, linux/arm64 and nothing else ``. */
355
591
  platforms?: readonly string[];
356
- /** Operator-layer overrides — kit development territory (ADR-0019). */
592
+ // Kit development territory (ADR-0019).
593
+ /** Settings of the operator layer, for kit development. */
357
594
  operator?: {
358
- /** `false` = `jr2 up` skips the operator layer (run the controller loop yourself). */
595
+ /** `false`: `jr2 up` does not converge the operator, and you run its controller yourself.
596
+ * Default: `true`. */
359
597
  manage?: boolean;
360
598
  };
361
599
  };
362
600
 
363
- /** Identity passthrough that checks a config object against `JR2Config` where it is written —
364
- * excess keys included, at every depth, which is why the parameter is the type and not a generic
365
- * bound by it. */
601
+ // Identity passthrough that checks a config object against `JR2Config` where it is written —
602
+ // excess keys included, at every depth, which is why the parameter is the type and not a generic
603
+ // bound by it.
604
+ /**
605
+ * Declare the config of an Instance: the default export of `jr2.config.ts`. It gives back the
606
+ * object that you pass. Its only work is to check that object against {@link JR2Config} where you
607
+ * write it, at every depth, a misspelled key included. `jr2 up` typechecks the file before it
608
+ * converges.
609
+ *
610
+ * @param c - The Instance's config. See {@link JR2Config} for every field.
611
+ * @returns The same object.
612
+ *
613
+ * @example
614
+ * ```ts
615
+ * import { defineConfig } from "@jr2/orchestrator";
616
+ *
617
+ * export default defineConfig({
618
+ * name: "my-instance",
619
+ * secrets: [{ name: "ANTHROPIC_API_KEY", value: process.env.ANTHROPIC_API_KEY }],
620
+ * git: { credentials: [{ match: "github.com/you/", token: "GITHUB_TOKEN" }] },
621
+ * });
622
+ * ```
623
+ *
624
+ * @see docs/reference/config.md
625
+ * @see docs/get-started/create-an-instance.md
626
+ * @see docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md
627
+ */
366
628
  export function defineConfig(c: JR2Config): JR2Config {
367
629
  return c;
368
630
  }