@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
@@ -0,0 +1,230 @@
1
+ ---
2
+ title: Pools and Sources
3
+ description: How a Pool runs one worker Machine for each work item that a Source gives, and how its run ends.
4
+ ---
5
+
6
+ # Pools and Sources
7
+
8
+ A [Pool](../reference/glossary.md#pool) is a [Machine](../reference/glossary.md#machine) that runs one worker Machine
9
+ for each item of work. It gets the items from a [Source](../reference/glossary.md#source), one at a time, and it runs a
10
+ limited number of workers at once. You make a Pool with [`pool()`](../reference/authoring-surface/pool.md) and a Source
11
+ with [`source()`](../reference/authoring-surface/source.md). A Pool is usually the root of a
12
+ [Workflow](../reference/glossary.md#workflow). For example, a Pool can run one [`task`](../reference/machines.md#task)
13
+ for each ticket that is ready.
14
+
15
+ ## Why it exists
16
+
17
+ A Machine that works through a list of items does the same work each time. It claims an item and starts a worker for it.
18
+ It runs no more workers than a limit. It collects each result, looks for more work, and decides when the run is done. A
19
+ Pool does all of this. The Machine gives only the Source, the worker and the limit.
20
+
21
+ The Pool holds both the Source and the workers. Thus it can tell three conditions apart. All the work is done, the work
22
+ that remains can never start, or the workers wait for a person.
23
+
24
+ ## How a Pool behaves
25
+
26
+ The example is a Pool over a Source of four tickets. T-3 needs T-1 and T-2, and T-4 needs a ticket that does not exist.
27
+ Each worker waits at a [Gate](../reference/glossary.md#gate) until a person approves or rejects its ticket. Thus the
28
+ example runs with no model. A worker is usually a Machine with an [Agent](../reference/glossary.md#agent), such as
29
+ `task`.
30
+
31
+ ```ts
32
+ import { assign, fromPromise } from "xstate";
33
+ import { z } from "zod";
34
+ import { defineEvent, jr2Setup, pool, source, type PoolNextInput } from "@jr2/orchestrator";
35
+
36
+ type Ticket = { id: string; title: string; needs: string[] };
37
+
38
+ // A small backlog with dependencies. T-3 can start only when T-1 and T-2 are done.
39
+ // T-4 needs a ticket that does not exist, so it never becomes ready.
40
+ const backlog: Ticket[] = [
41
+ { id: "T-1", title: "Trim the name.", needs: [] },
42
+ { id: "T-2", title: "Add a --version flag.", needs: [] },
43
+ { id: "T-3", title: "Document the two changes.", needs: ["T-1", "T-2"] },
44
+ { id: "T-4", title: "Remove the old flag.", needs: ["T-9"] },
45
+ ];
46
+
47
+ // The ids that the Pool claimed. This set is in memory, so it serves one run.
48
+ // A real Work Source asks the issue tracker instead.
49
+ const claimed = new Set<string>();
50
+
51
+ // The Source: give the next ready ticket, or say how many tickets are not ready.
52
+ const tickets = source<Ticket>({
53
+ next: fromPromise(async ({ input }: { input: PoolNextInput }) => {
54
+ const done = (id: string) => claimed.has(id) && !input.active.includes(id);
55
+ const waiting = backlog.filter((t) => !claimed.has(t.id));
56
+ const ready = waiting.find((t) => t.needs.every(done));
57
+ if (!ready) return { item: null, open: waiting.length };
58
+ claimed.add(ready.id);
59
+ return { item: ready, open: waiting.length - 1 };
60
+ }),
61
+ // Send this event to the Pool's `source` Gate when the backlog changes.
62
+ wake: defineEvent({
63
+ name: "tickets_changed",
64
+ description: "The backlog changed. Look for ready tickets again.",
65
+ audience: "external",
66
+ input: z.object({}),
67
+ }),
68
+ });
69
+
70
+ const approve = defineEvent({
71
+ name: "approve",
72
+ description: "Accept the ticket.",
73
+ audience: "external",
74
+ input: z.object({}),
75
+ });
76
+
77
+ const reject = defineEvent({
78
+ name: "reject",
79
+ description: "Refuse the ticket.",
80
+ audience: "external",
81
+ input: z.object({ reason: z.string().describe("Why, in one sentence.") }),
82
+ });
83
+
84
+ type Outcome = { id: string; outcome: "approved" } | { id: string; outcome: "rejected"; reason: string };
85
+
86
+ // The worker: one Machine for each ticket. Here a person decides at a Gate.
87
+ // A worker is usually a Machine with an Agent, such as `task`.
88
+ const review = jr2Setup({
89
+ types: {} as { input: Ticket; context: Ticket & { outcome?: Outcome }; output: Outcome },
90
+ events: [approve, reject],
91
+ }).createMachine({
92
+ id: "review",
93
+ context: ({ input }) => input,
94
+ initial: "review",
95
+ states: {
96
+ review: {
97
+ invoke: { src: "gate", input: ({ context }) => ({ meta: { title: context.title } }) },
98
+ on: {
99
+ approve: {
100
+ target: "decided",
101
+ actions: assign({ outcome: ({ context }) => ({ id: context.id, outcome: "approved" as const }) }),
102
+ },
103
+ reject: {
104
+ target: "decided",
105
+ actions: assign({
106
+ outcome: ({ context, event }) => ({ id: context.id, outcome: "rejected" as const, reason: event.reason }),
107
+ }),
108
+ },
109
+ },
110
+ },
111
+ decided: { type: "final" },
112
+ },
113
+ output: ({ context }) => context.outcome!,
114
+ });
115
+
116
+ // The Pool: one `review` for each ready ticket, two at most at once.
117
+ export const machine = pool(review, {
118
+ id: "tickets",
119
+ source: tickets,
120
+ itemId: (ticket: Ticket) => ticket.id,
121
+ cap: 2,
122
+ });
123
+ ```
124
+
125
+ The Pool asks for work, starts workers, and waits:
126
+
127
+ - The Pool calls the `next` of its Source when the run starts. It gives `next` the ids of the items that its workers
128
+ have now.
129
+ - When `next` gives an item, the Pool starts a worker for it, and calls `next` again. The id of the worker is
130
+ `itemId(item)`.
131
+ - When `cap` workers run, the Pool does not call `next`. It calls `next` again when a worker finishes.
132
+ - When no item is ready and workers run, the Pool waits. It calls `next` again when a worker finishes, when the `wake`
133
+ event comes, or after `pollEvery` milliseconds.
134
+ - When no worker runs and `next` gives no item, the run ends.
135
+
136
+ In the example, T-1 and T-2 start at once, because `cap` is 2. The run then has three Gates: `T-1.review`, `T-2.review`
137
+ and `source`. When a person answers both tickets, T-3 starts. When T-3 finishes, only T-4 remains, and it can never
138
+ start. Thus the run ends in the state `deadlocked`, and the `status` of its output is `deadlocked`.
139
+
140
+ ### How the run ends
141
+
142
+ The output of a Pool is `{ status, items, failures }`. `items` holds the output of each worker that got to a final
143
+ state, by item id. `failures` holds the [Failure](../reference/glossary.md#failure) of each worker that failed, by item
144
+ id. The Pool catches a worker's Failure: that worker stops, its Sandbox is deleted, and the other workers go on. A
145
+ defect in a worker is not caught, and ends the run `error`.
146
+
147
+ When the Pool ends and one or more workers failed, the run ends `failed` by default. This is true for `drained` and for
148
+ `deadlocked`. Its output is then the Failure of the Pool: the reason is `"<n> of <m> items failed"`, and the detail is
149
+ `{ status, items, failures }`. `jr2 run` exits 1. To end `done` with the failures in the output, give `pool()` the
150
+ option `failures: "report"`. Use it when a failed item is a correct answer of the run, for example in a run that tries
151
+ each item and reports which ones did not succeed.
152
+
153
+ `status` comes from the last answer of `next`:
154
+
155
+ | The last answer of `next` | `status` | What it means |
156
+ | ----------------------------------------------- | ------------ | ---------------------------------------- |
157
+ | `null`, or `{ item: null, open: 0 }` | `drained` | No item is left. |
158
+ | `{ item: null, open: <n> }`, with `<n>` above 0 | `deadlocked` | Items exist, but none of them can start. |
159
+
160
+ While workers wait at Gates, the run does not end. It stays open, and the Gates of the run show what needs you. This is
161
+ not a status in the output: a run that waits has no output yet.
162
+
163
+ ### Inputs, Gates and the wake event
164
+
165
+ - A worker gets its item as its input, or what `itemInput(item, { input })` makes of it. The Pool does not give the run
166
+ input to a worker. `itemInput` can copy a part of it into the worker's input.
167
+ - The run input of a Pool is its own `input` schema, not the input of its worker. `cap` and `itemInput` read it.
168
+ - The Gates of a worker show in the run. The id of each one starts with the id of its item, such as `T-1.review`.
169
+ - The Pool takes the `wake` event of the Source at a Gate named `source`, for the whole run. A webhook or
170
+ [`jr2 send`](../reference/cli.md#send) sends it. When the Pool is inside a different Machine, the Gate is
171
+ `<path>.source`.
172
+ - The [Vocabulary](../reference/glossary.md#vocabulary) of a Pool is only its `wake` event. Each worker keeps its own.
173
+
174
+ A Pool is a Machine. It can be the root of a Workflow, or a different Machine can compose it.
175
+ [`customize()`](../reference/authoring-surface/customize.md) goes through a Pool to its worker, as
176
+ [Composition](composition.md) tells.
177
+
178
+ ## Sources
179
+
180
+ [`source(spec)`](../reference/authoring-surface/source.md) declares where a Pool gets its items. A Source has three
181
+ parts ([`SourceSpec`](../reference/authoring-surface/type-sourcespec.md)):
182
+
183
+ - `next`: an [xstate](https://xstate.js.org) promise actor that claims the next ready item. Its input is
184
+ `{ active, settled, input }`: the ids of the items that workers have now, the ids of the finished and failed items,
185
+ and the input of the Pool. Thus a Source can fan out over a plan with no state of its own.
186
+ - `wake`: an optional event, made with [`defineEvent`](../reference/authoring-surface/defineevent.md), that tells the
187
+ Pool that new work is ready.
188
+ - `pollEvery`: an optional time in milliseconds. While the Pool waits, it calls `next` again at this interval.
189
+
190
+ `next` resolves to one of three answers ([`SourceNextResult`](../reference/authoring-surface/type-sourcenextresult.md)):
191
+ an item, `null` when no item is ready now, or `{ item, open }`. `open` is the number of items that exist but are not
192
+ ready. Without `open`, the Pool cannot tell a finished run from a blocked one. Thus, when no worker runs, `null` means
193
+ `drained`.
194
+
195
+ A Source can be a queue, a generator, or a set that `next` queries again each time.
196
+
197
+ ## Work Source and the Ready-set
198
+
199
+ A [Work Source](../reference/glossary.md#work-source) is a Source over an issue tracker, such as GitHub issues or Jira.
200
+ The Work Source reads the dependencies between the items from the issue tracker. The
201
+ [Ready-set](../reference/glossary.md#ready-set) is the set of items that nothing blocks now. The `next` of a Work Source
202
+ gives the next item of the Ready-set.
203
+
204
+ The Machine does not hold the dependencies, and it does not copy the Ready-set into its context. It asks the Work Source
205
+ again each time. The Ready-set changes during a run. A person can add a ticket, or open a closed ticket again. Use
206
+ `wake` or `pollEvery`, so that the Pool sees the change while it waits.
207
+
208
+ jr2 does not include a Work Source. You write its `next` for your issue tracker. In the example, the list in memory is
209
+ the issue tracker.
210
+
211
+ ## Rules
212
+
213
+ - `itemId` gives each item an id of its own, and the same id each time. The Pool uses it as the id of the worker and as
214
+ the key in `items`.
215
+ - `next` must not give an item in `active` or in `settled`.
216
+ - `cap` is a number, or a function of the run input. Without `cap`, one worker runs at a time.
217
+ - `onDrained` takes only `"final"`: the run ends.
218
+ - The Pool calls `next` again on `wake` or after `pollEvery` only while it waits: workers run and no item is ready.
219
+ - A `task` in a Pool needs a `branch` in its input. Make it in `itemInput`, for example from the item id.
220
+
221
+ ## Read next
222
+
223
+ - [Run one worker per item](../guides/run-one-worker-per-item.md)
224
+ - [Composition](composition.md)
225
+ - [Gates](gates.md)
226
+ - Reference: [`pool()`](../reference/authoring-surface/pool.md), [`source()`](../reference/authoring-surface/source.md),
227
+ [`PoolSpec`](../reference/authoring-surface/type-poolspec.md),
228
+ [`SourceSpec`](../reference/authoring-surface/type-sourcespec.md),
229
+ [`SourceNextResult`](../reference/authoring-surface/type-sourcenextresult.md)
230
+ - [ADR-0017](../reference/decisions/0017-pool-over-a-source-port.md): the run loop is a Pool over a Source
@@ -0,0 +1,306 @@
1
+ ---
2
+ title: Secrets and trust
3
+ description: Where jr2 keeps each credential, which tokens it makes, and what a caller can see and do without one.
4
+ ---
5
+
6
+ # Secrets and trust
7
+
8
+ jr2 keeps each credential of the `secrets` list, the key of `harness.provider.apiKey`, and each token of its own, out of
9
+ the containers in which an [Agent](../reference/glossary.md#agent) executes code. A
10
+ [Held secret](../reference/glossary.md#held-secret) is a credential that the [Harness](../reference/glossary.md#harness)
11
+ uses through its pod's [Custodian](../reference/glossary.md#custodian), and the Harness container holds only its
12
+ [Stand-in](../reference/glossary.md#stand-in). An [Effect secret](../reference/glossary.md#effect-secret) is a
13
+ credential that an [Effect](../reference/glossary.md#effect) uses, and only an
14
+ [Effector](../reference/glossary.md#effector) holds its value. jr2 also makes its own tokens, for each caller of the
15
+ [Orchestrator](../reference/glossary.md#orchestrator) and of each Harness. Without a token, a caller can see each
16
+ [Machine](../reference/glossary.md#machine) and where each live run is in it, but it cannot control a run.
17
+
18
+ ## Why it exists
19
+
20
+ An Agent's [Working tools](../reference/glossary.md#working-tools) give it a shell in the Harness container. Thus the
21
+ Agent can run each program of the image, and it can send what it reads out of the pod. If a model API key were in its
22
+ reach, the Agent could take it away. If the credential of the Orchestrator's API were in its reach, the Agent could
23
+ answer its own review [Gate](../reference/glossary.md#gate), or send a pick for a different Agent.
24
+
25
+ jr2 does not try to hide the API from the Agent. Instead, it keeps the credentials out of the Agent's reach. The
26
+ Instance token enters no Harness pod. Each credential of the `secrets` list, and the key of `harness.provider.apiKey`,
27
+ goes to a container in which the Agent executes nothing. [Architecture](architecture.md) tells where those containers
28
+ run. The Harness itself holds one secret value, and [Confined programs](#confined-programs) tells how jr2 keeps it from
29
+ the Agent's code.
30
+
31
+ ## One list, and the name decides
32
+
33
+ [`jr2.config.ts`](../reference/config.md) has one [`secrets`](../reference/config.md#secrets) list. An entry goes to
34
+ each place that names it, and to no other place:
35
+
36
+ ```ts
37
+ import { defineConfig } from "@jr2/orchestrator";
38
+
39
+ export default defineConfig({
40
+ secrets: [
41
+ // A model key. Each pod whose Agents use Anthropic claims it, and the Custodian of that pod holds it.
42
+ // It has no hosts, so it goes only to the host that Anthropic's calls go to.
43
+ { name: "ANTHROPIC_API_KEY", value: process.env.ANTHROPIC_API_KEY },
44
+ // A Held secret for a tool that the Agent runs. A workspace() claims it with secrets: ["TRACKER_TOKEN"].
45
+ // The tool sends it as a bearer. The Custodian puts the value only into requests to this host.
46
+ { name: "TRACKER_TOKEN", value: process.env.TRACKER_TOKEN, hosts: ["api.tracker.example.com"] },
47
+ // An Effect secret. An Effect names it with secret("DEPLOY_TOKEN"), and only its Effector holds the value.
48
+ { name: "DEPLOY_TOKEN", value: process.env.DEPLOY_TOKEN },
49
+ ],
50
+ });
51
+ ```
52
+
53
+ - A pod claims a Held secret when its [`workspace()`](../reference/authoring-surface/workspace.md) lists the name under
54
+ `secrets`. A pod also claims the key of each provider that an Agent in that pod uses.
55
+ - The [Instance Harness](../reference/glossary.md#instance-harness) claims only the model keys of the Menu-only Agents
56
+ that it hosts.
57
+ - An Effect claims an Effect secret when it names it with [`secret()`](../reference/authoring-surface/secret.md).
58
+ - One entry can be a Held secret and an Effect secret at the same time. The placement is a fact of each place that names
59
+ it, not of the entry.
60
+ - An entry that nothing names goes nowhere, and [`jr2 up`](../reference/cli.md#up) warns about it.
61
+
62
+ [`harness.provider.apiKey`](../reference/config.md#harness-provider-apikey) is a Held secret too. Each pod whose Agents
63
+ use the custom provider claims it, and it goes only to the host of `harness.provider.baseUrl`.
64
+
65
+ `jr2 up` reads the values on your computer, from your shell or `.env`. It writes each value only into the Secrets of the
66
+ containers that hold it. An entry can also name a key of a Secret that you manage, with `valueFrom`.
67
+
68
+ ## Held secrets
69
+
70
+ The Harness of a pod holds one Stand-in for each Held secret of the pod, in a file that only the Harness reads. jr2
71
+ makes a new Stand-in for each pod, and it is secret. It has a meaning only to the Custodian of that pod. A program of an
72
+ Agent gets the Stand-in of a Held secret only when the `workspace()` lists that secret under `secrets`. The fetch
73
+ Stand-in is the one public Stand-in (see below).
74
+
75
+ When a pod holds a Held secret other than the tokens of its Sandbox, the Harness container sends its HTTPS traffic
76
+ through the Custodian (`HTTPS_PROXY`). For each request to a host that a Held secret is bound to, the Custodian does
77
+ these things:
78
+
79
+ 1. It ends the TLS connection with a certificate that `jr2 up` issued from the CA of the
80
+ [Instance](../reference/glossary.md#instance).
81
+ 2. It replaces the Stand-in with the value, in the headers that the entry names.
82
+ 3. It opens its own TLS connection to the host, and verifies the certificate of the host.
83
+
84
+ The Custodian sends the traffic to each other host with no change. It refuses a loopback, link-local or unspecified
85
+ address. A request to a bound host that has no Stand-in in its headers does not leave the pod.
86
+
87
+ These fields of an entry bind a Held secret. They do not place it:
88
+
89
+ - [`hosts`](../reference/config.md#secrets-hosts): the hosts that the value can go to. A known model key with one
90
+ default host, such as `ANTHROPIC_API_KEY`, can leave it out. It then goes only to the host of its provider.
91
+ - [`headers`](../reference/config.md#secrets-headers): the headers in which the Custodian replaces the Stand-in.
92
+ - [`paths`](../reference/config.md#secrets-paths): the path prefixes that the value can go to.
93
+
94
+ The Custodian also holds the two tokens of its [Sandbox](../reference/glossary.md#sandbox): the control token and the
95
+ fetch token.
96
+
97
+ - The Harness reads the [Menu](../reference/glossary.md#menu) and sends each pick with the control Stand-in, and the
98
+ Custodian replaces it with the control token. The control Stand-in is secret. Only the Harness holds it, in a file
99
+ that no program of an Agent can read. With each request, the Harness also tells which Agent the conversation is for,
100
+ and the Orchestrator checks it.
101
+ - A `git fetch` in the Sandbox sends the fetch Stand-in, which is public. The Custodian replaces it with the fetch token
102
+ only for a fetch. It refuses the fetch Stand-in for the Menu and for a pick.
103
+
104
+ The Instance Harness has a token of its own for the Menu. It runs no program of an Agent.
105
+
106
+ ### What a Held secret does not stop
107
+
108
+ - **The Agent's code can spend a key that the `workspace()` lists.** While the pod lives, a program of the Agent can
109
+ send requests through the Custodian to the bound host of that key. A key that can make new keys gives the Agent a key
110
+ that it can take away. Use a key with no admin rights. Set narrow `paths`.
111
+ - **A response passes with no change.** If a host puts the full key in a response, the Agent reads it.
112
+ - **jr2 controls egress only at the cloud metadata addresses.** A client that does not use the Custodian sends only the
113
+ Stand-in, which is worth nothing outside the pod. The cluster decides which hosts a pod can reach. One exception: a
114
+ NetworkPolicy of jr2 stops each Harness pod at the metadata addresses of the large cloud providers (`169.254.169.254`,
115
+ `fd00:ec2::254`, `169.254.170.2`, `169.254.170.23` and `100.100.100.200`). There, the metadata service of a node can
116
+ give the node's cloud credential to a pod. The list is not complete for every provider, and only a CNI that enforces
117
+ NetworkPolicy applies it. See
118
+ [Move from kind to a shared cluster](../guides/move-from-kind-to-a-shared-cluster.md#check-the-cluster).
119
+ - **A key that signs the request cannot be a Held secret.** A SigV4 or ADC credential signs the request, and the
120
+ Custodian cannot put a value into a signature. Use an Effect secret for such a credential. For a model provider, use a
121
+ gateway that holds the credential, and make the key of the gateway a Held secret.
122
+ - **A tool must trust the CA of the Instance.** When a pod holds a Held secret other than the tokens of its Sandbox, jr2
123
+ sets the trust variables of Node.js, OpenSSL, Python and git in its Harness container. A client that does not trust
124
+ the CA, such as a Java client, cannot connect to a bound host. It sends nothing.
125
+
126
+ ## Confined programs
127
+
128
+ The Harness runs the [Turns](../reference/glossary.md#turn) of each Agent of the Sandbox. The Harness process itself
129
+ runs only jr2's code. Each other process in the Harness container runs confined with Landlock: each command of the
130
+ Agent's shell, and so the tests, the builds, git and its hooks; the search tools; the I/O of the read, write and edit
131
+ tools; jr2's own git; and each `command` Effect that names no Effect secret. Code that one Agent writes in `/work` can
132
+ run in a program of a different Agent, for example when a reviewer runs the tests of a coder. Thus jr2 confines each
133
+ such program, and not only the programs of one Agent.
134
+
135
+ All these programs are in one Landlock domain. They can see, signal and trace each other, as the programs of one user on
136
+ a computer can. Thus a later shell command can find and stop a server that an earlier command started. When the process
137
+ that starts these programs stops, the Harness starts it again, in a new domain. The programs of the old domain continue,
138
+ and a program of the new domain cannot trace them.
139
+
140
+ A confined program can do all that the user of the container can do, with these exceptions:
141
+
142
+ - It cannot read, write, make, remove, move or link a file in `/run/jr2`. That folder holds the control Stand-in, the
143
+ Stand-in of each Held secret, the socket of the Custodian for the picks, and the conversations of the Harness. A
144
+ program can list the names in `/run/jr2`, as it can list each folder: `ls /` and `find /` work. The names are not
145
+ secrets.
146
+ - It cannot make a socket in `/run/jr2`. It can connect to the socket of the Custodian, but the Custodian refuses each
147
+ request on the socket that does not have the control Stand-in, and the program cannot read that Stand-in.
148
+ - It cannot trace the Harness process, or read its environment or its memory.
149
+ - When the kernel has Landlock ABI 6 or later, it cannot send a signal to the Harness process, or connect to an abstract
150
+ Unix socket that the Harness or the Custodian made.
151
+
152
+ Thus a program can write in a folder of the image that its user owns, move a file from one folder to another, and run
153
+ the tools of the image. The kernel keeps the shared parts safe: the caches of each [Repo](../reference/glossary.md#repo)
154
+ at `/repos`, the runtime of jr2 at `/opt/jr2` and the CA bundles are read-only. The pod has no ServiceAccount token. A
155
+ program can connect to each address that the pod can reach.
156
+
157
+ The environment of a confined program is the environment with which the container started: the `ENV` of the image,
158
+ `harness.env`, `harness.envFrom`, and the proxy and trust variables. jr2 removes its own `JR2_` names from it, except
159
+ `JR2_CUSTODIAN_URL`. jr2 adds `HOME`, `TMPDIR` and the Stand-in of each Held secret that the `workspace()` lists, under
160
+ the name of that secret. The environment holds no control Stand-in. It holds the Stand-in of a model key only when the
161
+ `workspace()` lists that key. Thus code in `/work` cannot call the model with your key unless you allow it. jr2 records
162
+ the use of each Turn, and a call that you did not allow cannot go around that record.
163
+
164
+ The read, write and edit tools reach the same paths as the shell. A path in `/run/jr2`, or a link to one, gives the
165
+ error of a file that the user cannot read. In a path, `~` is the `HOME` of the confined programs.
166
+
167
+ The Harness process has its own `HOME`, in `/run/jr2`, from before Node starts. Thus a file that a program writes in its
168
+ `HOME`, such as `~/.aws/config` or a module in `~/.node_modules`, does not change what the Harness process does. The
169
+ Harness does not start when Node would look for a module outside `/opt/jr2` and `/run/jr2`.
170
+
171
+ The Harness process gets only the env that jr2 composes: `harness.env`, `harness.envFrom`, the proxy and trust
172
+ variables, and jr2's own names. It does not get the `ENV` of the image. It also does not get a name that loads code from
173
+ a path, such as `NODE_OPTIONS`, `LD_PRELOAD` or `OPENSSL_CONF`, from any of them. Thus an image with
174
+ `NODE_OPTIONS=--require /app/hook.js` changes the Agent's programs, as you expect, and not the Harness process. In
175
+ `kubectl exec`, each key of `harness.envFrom` has the prefix `JR2_ENVFROM_`. The programs of the Agent see each key
176
+ under its own name. A name that starts with `JR2_` is jr2's own: `jr2 up` refuses one in `harness.env` or in a Secret or
177
+ a ConfigMap that `harness.envFrom` loads, and the Harness does not start with one from `harness.envFrom`.
178
+
179
+ Thus code in `/work` cannot send a pick for an Agent, also when a different Agent runs it. An Agent can still try to
180
+ change what a different Agent does with the text that it writes, such as a comment in a file. The other Agent's model
181
+ decides each of its own picks.
182
+
183
+ jr2 needs Landlock in the kernel of each node. See [Rules](#rules).
184
+
185
+ ## Effect secrets
186
+
187
+ An Effect secret is the real value, in an Effector. The Effector reads the value and puts it into the call, or into the
188
+ environment of a `command`. The Agent executes nothing in the Effector, and the Orchestrator never holds the value.
189
+ [Effects](effects.md) tells which Effects run in which Effector.
190
+
191
+ A `command` that names an Effect secret can run the Agent's code, such as `npm run deploy`. It runs on a copy of one
192
+ commit of the Agent's branch, in its scratch folder. Because the command can run the Agent's code, the Effector confines
193
+ the process of the command with Landlock. The process can read and run the system files of the image. It can use its own
194
+ scratch folder, and read the Effect secrets that the command names. It cannot read the Effector's key, the other Effect
195
+ secrets, the push credentials or the [Worktree](../reference/glossary.md#worktree).
196
+
197
+ The Effector removes each Effect secret that it holds from what it returns, in the forms that it sent and in common
198
+ encodings. It does not find a changed form of an Effect secret, such as a hash or a part of it.
199
+
200
+ ## Git credentials
201
+
202
+ The [`git.credentials`](../reference/config.md#git-credentials) entries give the cluster its git credentials. The
203
+ Orchestrator matches a Repo to an entry, and names the Secret of that entry. It never reads a git credential:
204
+
205
+ - The clone credential goes into a Secret that only the Repo cache reads.
206
+ - The push credential and the signing key go to the [Sandbox Effector](../reference/glossary.md#sandbox-effector) of the
207
+ Sandbox whose `push` Effect uses them.
208
+
209
+ The list is also a fence. A Repo url that comes from the run input must match an entry, or the Orchestrator refuses to
210
+ make the Sandbox. [Give the cluster git credentials](../guides/give-the-cluster-git-credentials.md) gives the fields.
211
+
212
+ ## The tokens
213
+
214
+ jr2 makes its own tokens. `jr2 up` makes the Instance token and the signing key the first time, and keeps them in the
215
+ `jr2-instance` Secret. Each other token comes from the signing key, so the Orchestrator stores no token for each pod.
216
+
217
+ | Token | Who holds it | What it lets the holder do |
218
+ | -------------- | ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
219
+ | Instance token | You, the CLI, the [Console](../reference/glossary.md#console) when you type it, and a webhook client | Everything: start a run, read its full state, answer a Gate, cancel a run. |
220
+ | Control token | The Custodian of one Sandbox, as a file | Read the Menu and send the picks of the Agents in that Sandbox, for the Agent that the Harness names. |
221
+ | Fetch token | The Custodian of one Sandbox, as a file | Ask for a fetch of a Repo that the Sandbox mounts. |
222
+ | Harness bearer | The Orchestrator. The Harness holds only its SHA-256 digest. | Call the Harness in one pod: start a Turn, read it, stop it, set up a Worktree, and run a `command` Effect. |
223
+ | Effector key | The Orchestrator, and the Effector of one placement | Sign a request to that Effector, and sign its answer. |
224
+
225
+ - The Instance token enters no Harness pod. The CLI reads it from the `jr2-instance` Secret over the Kubernetes API.
226
+ Thus Kubernetes RBAC decides who holds it.
227
+ - A control token or a fetch token cannot answer a Gate, and cannot speak for a different pod. The Orchestrator accepts
228
+ each one only on its own routes. Thus an Agent cannot approve its own work, and a Sandbox cannot send a pick for an
229
+ Agent in a different Sandbox.
230
+ - A Harness can check its bearer, but it cannot make a bearer. Thus an Agent cannot drive a conversation in a different
231
+ pod.
232
+
233
+ ## Open observation, guarded control
234
+
235
+ Each route of the [HTTP API](../reference/http-api.md) is in one of three access bands:
236
+
237
+ - [**Open**](../reference/http-api.md#band-open), with no token: the [Workflows](../reference/glossary.md#workflow), the
238
+ structure of each Machine, and the live runs of a Workflow with their states. An open route shows no context of a run,
239
+ and an [Emit](../reference/glossary.md#emit) shows only its type.
240
+ - [**Authenticated**](../reference/http-api.md#band-authenticated), with the Instance token or a token of a Sandbox: the
241
+ routes of the Agent's Menu and the fetch. A token of a Sandbox reaches only its own routes, in its own pod.
242
+ - [**Instance token only**](../reference/http-api.md#band-instanceonly): the start of a run, the full state of a run,
243
+ the Gates, and cancel.
244
+
245
+ Thus the Console shows the Machines and the runs without a token. When you type the Instance token in the Console, it
246
+ can also start runs and answer Gates. The Console keeps the token in the session storage of the browser tab, and forgets
247
+ it when you close the tab.
248
+
249
+ "Open" means open to each caller that can reach the Service of the Orchestrator: a port forward, a pod in the cluster,
250
+ or an ingress. The names of the states and the types of the Emits are open.
251
+
252
+ The Harness writes each Agent's conversation to its container log. The log has the prompts, the model's text and
253
+ thinking, its tool calls and the answers to its picks. It does not write the results of the Working tools. Each person
254
+ who can read the pod logs of the namespace can read these conversations.
255
+
256
+ ## Rules
257
+
258
+ - A model key goes in the `secrets` list. `jr2 up` refuses a known model key in `harness.env`, or in a Secret or a
259
+ ConfigMap that `harness.envFrom` names.
260
+ - A Held secret goes only over `https://`. `jr2 up` refuses `harness.provider.apiKey` when `harness.provider.baseUrl` is
261
+ not `https://`. A local model over plain `http://` has no key.
262
+ - [`harness.env`](../reference/config.md#harness-env) and `harness.envFrom` put each value into the Harness container,
263
+ and the Agent can read it. Put a credential in the `secrets` list.
264
+ - A file that the Harness process reads, such as the one that `AWS_CONFIG_FILE` or `GOOGLE_APPLICATION_CREDENTIALS`
265
+ names in `harness.env`, cannot be in `/work`, `/tmp`, `/dev/shm` or `/home/jr2`: `jr2 up` refuses it. In another
266
+ folder, `jr2 up` gives a warning: make sure that the user of the Sandbox cannot write the file.
267
+ - A Held secret must name its `hosts`, unless it is a known model key with one default host.
268
+ - A name that a `workspace()` lists must be in the `secrets` list. A name that an Effect gives to `secret()` must be in
269
+ it too.
270
+ - An Effect secret has 8 characters or more.
271
+ - The name of a Held secret is an env var name. It cannot start with `JR2_`, and it cannot be a proxy or trust variable.
272
+ - The Orchestrator can make and change a Secret, but it cannot get or list one.
273
+ - A changed value does not reach a live Sandbox. A new Sandbox gets it. When a Menu-only Agent uses the value, the next
274
+ `jr2 up` restarts the Instance Harness with it.
275
+ - jr2 needs Landlock ABI 3 or later in the kernel of each Sandbox node: Linux 6.2 or later, or a kernel with a backport,
276
+ with `landlock` in its list of security modules. `jr2 up` refuses to converge while a Sandbox node has no Landlock, or
277
+ only ABI 1 or 2. On a node without it, a Harness or a Sandbox Effector does not start, and the provision of the
278
+ Sandbox fails.
279
+ - A name in `harness.env`, or a key that `harness.envFrom` loads, cannot start with `JR2_`.
280
+ - When a pod holds a Held secret other than the tokens of its Sandbox, `SSL_CERT_FILE` and the other trust variables
281
+ replace the trust store of the image. They hold the public roots, `harness.caBundle` and the CA of the Instance. Thus
282
+ a private CA goes in [`harness.caBundle`](../reference/config.md#harness-cabundle), not in the image.
283
+ - `jr2 up` makes the CA of the Instance one time, in the `jr2-held-ca` Secret, and never changes it. No pod mounts that
284
+ Secret. When you delete it, the next `jr2 up` makes a new CA.
285
+
286
+ ## Read next
287
+
288
+ - [Use a local model](../guides/use-a-local-model.md)
289
+ - [Use a hosted provider or a gateway](../guides/use-a-hosted-provider.md)
290
+ - [Give the cluster git credentials](../guides/give-the-cluster-git-credentials.md)
291
+ - [Run a command or call a URL with an Effect](../guides/run-a-command-or-call-a-url.md)
292
+ - [Open the Console](../guides/open-the-console.md)
293
+ - [Architecture](architecture.md) and [Effects](effects.md)
294
+ - The [`secrets`](../reference/config.md#secrets), [`git`](../reference/config.md#git) and
295
+ [`harness`](../reference/config.md#harness) fields of `jr2.config.ts`,
296
+ [`secret()`](../reference/authoring-surface/secret.md), and the [HTTP API](../reference/http-api.md)
297
+ - [ADR-0013](../reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md),
298
+ [ADR-0014](../reference/decisions/0014-observation-is-open-run-state-is-not.md),
299
+ [ADR-0023](../reference/decisions/0023-the-harness-prints-the-conversation.md),
300
+ [ADR-0032](../reference/decisions/0032-the-console-unlocks-with-the-instance-token.md),
301
+ [ADR-0051](../reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md),
302
+ [ADR-0058](../reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md),
303
+ [ADR-0059](../reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md),
304
+ [ADR-0068](../reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md),
305
+ [ADR-0076](../reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md) and
306
+ [ADR-0077](../reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md)