@jr2/orchestrator 0.5.1 → 0.7.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 (350) hide show
  1. package/console/components/app.ts +55 -15
  2. package/console/components/bottom-drawer.ts +259 -0
  3. package/console/components/drawer.ts +14 -6
  4. package/console/components/fleet.ts +36 -13
  5. package/console/components/machine-pane.ts +61 -5
  6. package/console/components/nav.ts +54 -8
  7. package/console/conversation.ts +161 -0
  8. package/console/login.ts +123 -0
  9. package/console/main.ts +600 -60
  10. package/console/store.ts +611 -64
  11. package/console/style.css +231 -0
  12. package/docs/agents-block.md +2 -2
  13. package/docs/concepts/agents-and-turns.md +142 -29
  14. package/docs/concepts/architecture.md +60 -34
  15. package/docs/concepts/callers-and-hooks.md +205 -0
  16. package/docs/concepts/charts.md +13 -8
  17. package/docs/concepts/composition.md +10 -1
  18. package/docs/concepts/converge.md +7 -2
  19. package/docs/concepts/durable-runs.md +43 -16
  20. package/docs/concepts/effects.md +133 -50
  21. package/docs/concepts/gates.md +95 -25
  22. package/docs/concepts/machines-and-workflows.md +14 -4
  23. package/docs/concepts/pools-and-sources.md +193 -17
  24. package/docs/concepts/secrets-and-trust.md +186 -62
  25. package/docs/concepts/the-menu.md +14 -0
  26. package/docs/concepts/workspaces.md +135 -56
  27. package/docs/get-started/a-model-decides.md +14 -13
  28. package/docs/get-started/an-agent-in-a-sandbox.md +46 -35
  29. package/docs/get-started/connect-a-model.md +1 -1
  30. package/docs/get-started/create-an-instance.md +17 -14
  31. package/docs/get-started/have-your-assistant-write-a-machine.md +111 -40
  32. package/docs/get-started/install.md +4 -0
  33. package/docs/guides/add-tools-to-the-sandbox.md +72 -15
  34. package/docs/guides/answer-a-gate-from-a-forge.md +304 -0
  35. package/docs/guides/answer-a-gate.md +74 -37
  36. package/docs/guides/bind-open-parts.md +29 -12
  37. package/docs/guides/call-an-mcp-tool-from-a-machine.md +572 -0
  38. package/docs/guides/cancel-a-run.md +26 -16
  39. package/docs/guides/cut-a-worktree.md +560 -0
  40. package/docs/guides/fix-a-failed-converge.md +19 -7
  41. package/docs/guides/give-a-caller-a-scope.md +203 -0
  42. package/docs/guides/give-an-agent-mcp-tools.md +422 -0
  43. package/docs/guides/iterate-on-a-machine-on-kind.md +10 -9
  44. package/docs/guides/let-an-agent-write-a-chart.md +14 -7
  45. package/docs/guides/look-inside-a-sandbox.md +115 -42
  46. package/docs/guides/move-from-kind-to-a-shared-cluster.md +16 -0
  47. package/docs/guides/open-the-console.md +63 -14
  48. package/docs/guides/push-and-sign-a-branch.md +67 -21
  49. package/docs/guides/read-a-conversation.md +225 -0
  50. package/docs/guides/run-a-chart-by-name.md +17 -16
  51. package/docs/guides/run-a-command-or-call-a-url.md +84 -41
  52. package/docs/guides/run-one-worker-per-item.md +34 -27
  53. package/docs/guides/set-an-agents-access-and-dials.md +69 -22
  54. package/docs/guides/size-a-workspace.md +19 -6
  55. package/docs/guides/start-a-run-for-each-alert.md +290 -0
  56. package/docs/guides/start-a-run-from-a-run.md +18 -16
  57. package/docs/guides/start-a-run.md +24 -18
  58. package/docs/guides/steer-an-agent.md +332 -0
  59. package/docs/guides/use-a-hosted-provider.md +7 -1
  60. package/docs/guides/watch-a-run.md +146 -117
  61. package/docs/guides/write-a-hook-kind.md +238 -0
  62. package/docs/guides/write-a-tool-as-an-mcp-server.md +351 -0
  63. package/docs/guides/write-a-workflow.md +18 -8
  64. package/docs/llms.txt +110 -23
  65. package/docs/reference/authoring-surface/agent.md +35 -12
  66. package/docs/reference/authoring-surface/answer.md +20 -4
  67. package/docs/reference/authoring-surface/arg.md +28 -6
  68. package/docs/reference/authoring-surface/basic.md +1 -1
  69. package/docs/reference/authoring-surface/bearer.md +1 -1
  70. package/docs/reference/authoring-surface/branchname.md +15 -0
  71. package/docs/reference/authoring-surface/carry.md +23 -0
  72. package/docs/reference/authoring-surface/chartschema.md +1 -1
  73. package/docs/reference/authoring-surface/chartschemafor.md +1 -1
  74. package/docs/reference/authoring-surface/command.md +37 -22
  75. package/docs/reference/authoring-surface/customize.md +4 -1
  76. package/docs/reference/authoring-surface/defineconfig.md +1 -1
  77. package/docs/reference/authoring-surface/defineevent.md +1 -1
  78. package/docs/reference/authoring-surface/dispatch.md +5 -4
  79. package/docs/reference/authoring-surface/fail.md +18 -5
  80. package/docs/reference/authoring-surface/file.md +37 -17
  81. package/docs/reference/authoring-surface/frompick.md +1 -1
  82. package/docs/reference/authoring-surface/gate.md +118 -0
  83. package/docs/reference/authoring-surface/group-agents.md +15 -0
  84. package/docs/reference/authoring-surface/group-charts.md +16 -0
  85. package/docs/reference/authoring-surface/group-composition.md +25 -0
  86. package/docs/reference/authoring-surface/group-config.md +17 -0
  87. package/docs/reference/authoring-surface/group-effects.md +48 -0
  88. package/docs/reference/authoring-surface/group-failure.md +13 -0
  89. package/docs/reference/authoring-surface/group-gates.md +16 -0
  90. package/docs/reference/authoring-surface/group-machine-and-events.md +15 -0
  91. package/docs/reference/authoring-surface/group-steering.md +12 -0
  92. package/docs/reference/authoring-surface/group-types-these-signatures-name.md +137 -0
  93. package/docs/reference/authoring-surface/group-workspaces-and-repos.md +15 -0
  94. package/docs/reference/authoring-surface/http.md +109 -9
  95. package/docs/reference/authoring-surface/isfailure.md +1 -1
  96. package/docs/reference/authoring-surface/isready.md +14 -0
  97. package/docs/reference/authoring-surface/jr2setup.md +14 -4
  98. package/docs/reference/authoring-surface/mcp.md +107 -0
  99. package/docs/reference/authoring-surface/open.md +41 -8
  100. package/docs/reference/authoring-surface/openbody.md +1 -1
  101. package/docs/reference/authoring-surface/paramsof.md +23 -5
  102. package/docs/reference/authoring-surface/pool.md +79 -11
  103. package/docs/reference/authoring-surface/push.md +31 -13
  104. package/docs/reference/authoring-surface/readyof.md +21 -0
  105. package/docs/reference/authoring-surface/reread.md +19 -0
  106. package/docs/reference/authoring-surface/secret.md +26 -5
  107. package/docs/reference/authoring-surface/sentby.md +1 -1
  108. package/docs/reference/authoring-surface/skip.md +22 -0
  109. package/docs/reference/authoring-surface/source.md +59 -4
  110. package/docs/reference/authoring-surface/steer.md +93 -0
  111. package/docs/reference/authoring-surface/text.md +1 -1
  112. package/docs/reference/authoring-surface/type-agentdefinition.md +16 -3
  113. package/docs/reference/authoring-surface/type-agentmcpserver.md +54 -0
  114. package/docs/reference/authoring-surface/type-agentslots.md +4 -1
  115. package/docs/reference/authoring-surface/type-anyframepath.md +14 -0
  116. package/docs/reference/authoring-surface/type-anyworktreepath.md +15 -0
  117. package/docs/reference/authoring-surface/type-bindactors.md +14 -0
  118. package/docs/reference/authoring-surface/type-binding.md +6 -7
  119. package/docs/reference/authoring-surface/type-caller.md +40 -0
  120. package/docs/reference/authoring-surface/type-callerrecord.md +32 -0
  121. package/docs/reference/authoring-surface/type-carry.md +19 -0
  122. package/docs/reference/authoring-surface/type-chainlink.md +3 -3
  123. package/docs/reference/authoring-surface/type-commandinput.md +4 -3
  124. package/docs/reference/authoring-surface/type-commandoptions.md +0 -22
  125. package/docs/reference/authoring-surface/type-commandoutput.md +23 -5
  126. package/docs/reference/authoring-surface/type-customize.md +20 -3
  127. package/docs/reference/authoring-surface/type-delivered.md +17 -0
  128. package/docs/reference/authoring-surface/type-deliveredevent.md +18 -0
  129. package/docs/reference/authoring-surface/type-dispatchdefinition.md +4 -3
  130. package/docs/reference/authoring-surface/type-dispatchoutput.md +9 -2
  131. package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
  132. package/docs/reference/authoring-surface/type-effectbinding.md +12 -2
  133. package/docs/reference/authoring-surface/type-filebody.md +4 -8
  134. package/docs/reference/authoring-surface/type-framepath.md +14 -0
  135. package/docs/reference/authoring-surface/type-gatebinding.md +15 -0
  136. package/docs/reference/authoring-surface/type-gatehooks.md +12 -0
  137. package/docs/reference/authoring-surface/type-gatelook.md +37 -0
  138. package/docs/reference/authoring-surface/type-gateoutcome.md +32 -0
  139. package/docs/reference/authoring-surface/type-gatereply.md +19 -0
  140. package/docs/reference/authoring-surface/type-gateslot.md +21 -0
  141. package/docs/reference/authoring-surface/type-gateslotinput.md +13 -0
  142. package/docs/reference/authoring-surface/type-gateslotparts.md +18 -0
  143. package/docs/reference/authoring-surface/type-gateslots.md +12 -0
  144. package/docs/reference/authoring-surface/type-gatetypesat.md +13 -0
  145. package/docs/reference/authoring-surface/type-hostinjectedinput.md +3 -5
  146. package/docs/reference/authoring-surface/type-httpdefinition.md +6 -3
  147. package/docs/reference/authoring-surface/type-httpfileinput.md +20 -0
  148. package/docs/reference/authoring-surface/type-httpoutput.md +8 -2
  149. package/docs/reference/authoring-surface/type-jr2config.md +37 -2
  150. package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
  151. package/docs/reference/authoring-surface/type-lends.md +5 -1
  152. package/docs/reference/authoring-surface/type-lookoutput.md +14 -0
  153. package/docs/reference/authoring-surface/type-mcpcalloutput.md +78 -0
  154. package/docs/reference/authoring-surface/type-mcpnotreturned.md +42 -0
  155. package/docs/reference/authoring-surface/type-mcpoptions.md +47 -0
  156. package/docs/reference/authoring-surface/type-mcpoutput.md +50 -0
  157. package/docs/reference/authoring-surface/type-mcpreadoutput.md +53 -0
  158. package/docs/reference/authoring-surface/type-mcpremoteserver.md +50 -0
  159. package/docs/reference/authoring-surface/type-mcpserver.md +41 -0
  160. package/docs/reference/authoring-surface/type-mcpstdioserver.md +42 -0
  161. package/docs/reference/authoring-surface/type-namedsecret.md +4 -4
  162. package/docs/reference/authoring-surface/type-namestart.md +77 -0
  163. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +1 -6
  164. package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -0
  165. package/docs/reference/authoring-surface/type-openbodydeclaration.md +1 -1
  166. package/docs/reference/authoring-surface/type-openbodyoutput.md +1 -1
  167. package/docs/reference/authoring-surface/type-opencommandinput.md +7 -2
  168. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +1 -6
  169. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +5 -10
  170. package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
  171. package/docs/reference/authoring-surface/type-poolspec.md +11 -6
  172. package/docs/reference/authoring-surface/type-pushinput.md +22 -3
  173. package/docs/reference/authoring-surface/type-pushoutput.md +33 -8
  174. package/docs/reference/authoring-surface/type-pushsourcespec.md +38 -0
  175. package/docs/reference/authoring-surface/type-readsourcespec.md +47 -0
  176. package/docs/reference/authoring-surface/type-reposlot.md +6 -6
  177. package/docs/reference/authoring-surface/type-reread.md +18 -0
  178. package/docs/reference/authoring-surface/type-runsconfig.md +41 -0
  179. package/docs/reference/authoring-surface/type-sandboxoptions.md +11 -4
  180. package/docs/reference/authoring-surface/type-sender.md +26 -1
  181. package/docs/reference/authoring-surface/type-sendercallerfield.md +14 -0
  182. package/docs/reference/authoring-surface/type-size.md +1 -1
  183. package/docs/reference/authoring-surface/type-skip.md +19 -0
  184. package/docs/reference/authoring-surface/type-slothandles.md +5 -13
  185. package/docs/reference/authoring-surface/type-sourcehooks.md +23 -0
  186. package/docs/reference/authoring-surface/type-sourcein.md +25 -0
  187. package/docs/reference/authoring-surface/type-sourcereply.md +18 -0
  188. package/docs/reference/authoring-surface/type-sourcespec.md +26 -17
  189. package/docs/reference/authoring-surface/type-sourceverdict.md +49 -0
  190. package/docs/reference/authoring-surface/type-standingpoolmachine.md +12 -0
  191. package/docs/reference/authoring-surface/type-standingpoolspec.md +51 -0
  192. package/docs/reference/authoring-surface/type-steerargs.md +20 -0
  193. package/docs/reference/authoring-surface/type-thinkinglevel.md +1 -1
  194. package/docs/reference/authoring-surface/type-usercontainer.md +1 -1
  195. package/docs/reference/authoring-surface/type-workspaced.md +30 -9
  196. package/docs/reference/authoring-surface/type-workspacehandles.md +5 -6
  197. package/docs/reference/authoring-surface/type-workspaceoptions.md +1 -13
  198. package/docs/reference/authoring-surface/type-worktreefound.md +16 -0
  199. package/docs/reference/authoring-surface/type-worktreeinput.md +56 -0
  200. package/docs/reference/authoring-surface/type-worktreekey.md +14 -0
  201. package/docs/reference/authoring-surface/type-worktreeoptions.md +29 -0
  202. package/docs/reference/authoring-surface/type-worktreeoutput.md +45 -0
  203. package/docs/reference/authoring-surface/type-worktreepath.md +16 -0
  204. package/docs/reference/authoring-surface/type-worktreeready.md +37 -0
  205. package/docs/reference/authoring-surface/type-worktreerefused.md +21 -0
  206. package/docs/reference/authoring-surface/workspace.md +66 -38
  207. package/docs/reference/authoring-surface/worktree.md +71 -0
  208. package/docs/reference/authoring-surface.md +88 -147
  209. package/docs/reference/cli.md +413 -74
  210. package/docs/reference/config.md +248 -6
  211. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +10 -9
  212. package/docs/reference/decisions/0002-duplex-streaming-actor.md +81 -14
  213. package/docs/reference/decisions/0004-worktree-provisioning-model.md +27 -21
  214. package/docs/reference/decisions/0005-sandbox-pod-composition.md +39 -25
  215. package/docs/reference/decisions/0006-agent-control-surface.md +6 -3
  216. package/docs/reference/decisions/0007-durable-machine-state.md +1 -1
  217. package/docs/reference/decisions/0009-cli-and-instance-interface.md +150 -49
  218. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +5 -3
  219. package/docs/reference/decisions/0011-workflow-defined-events.md +256 -12
  220. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +39 -32
  221. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +54 -30
  222. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +53 -27
  223. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +26 -16
  224. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +9 -7
  225. package/docs/reference/decisions/0017-pool-over-a-source-port.md +226 -10
  226. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +15 -7
  227. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +53 -27
  228. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +16 -2
  229. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +4 -4
  230. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +124 -43
  231. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +36 -38
  232. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +10 -5
  233. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +16 -7
  234. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +36 -19
  235. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +18 -10
  236. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +3 -2
  237. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +23 -15
  238. package/docs/reference/decisions/0032-the-console-unlocks-with-a-callers-credential.md +101 -0
  239. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +22 -22
  240. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +6 -6
  241. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +13 -12
  242. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +23 -23
  243. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +6 -1
  244. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +57 -46
  245. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +5 -0
  246. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +20 -8
  247. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +55 -40
  248. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +14 -13
  249. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +31 -24
  250. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +18 -10
  251. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +137 -86
  252. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +165 -91
  253. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +7 -7
  254. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +2 -2
  255. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md +109 -0
  256. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +53 -23
  257. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +221 -153
  258. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +161 -96
  259. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +32 -9
  260. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +48 -33
  261. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +56 -30
  262. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +35 -25
  263. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +1 -1
  264. package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +296 -0
  265. package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +347 -0
  266. package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
  267. package/docs/reference/decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md +285 -0
  268. package/docs/reference/decisions/0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md +198 -0
  269. package/docs/reference/decisions.md +9 -4
  270. package/docs/reference/glossary.md +138 -45
  271. package/docs/reference/hooks.md +1077 -0
  272. package/docs/reference/http-api.md +988 -123
  273. package/docs/reference/machines.md +0 -1
  274. package/docs/why.md +7 -5
  275. package/package.json +10 -3
  276. package/src/actor.ts +194 -42
  277. package/src/agent.ts +56 -6
  278. package/src/ambient.ts +3 -5
  279. package/src/assembly-checks.ts +530 -4
  280. package/src/callers.ts +245 -0
  281. package/src/chart.ts +238 -26
  282. package/src/charts.ts +9 -2
  283. package/src/config.ts +566 -7
  284. package/src/conversations.ts +453 -0
  285. package/src/custodian.lua +17 -5
  286. package/src/custodian.ts +257 -77
  287. package/src/customize.ts +154 -7
  288. package/src/dispatch-effect.ts +142 -26
  289. package/src/durability.ts +2 -0
  290. package/src/duration.ts +20 -0
  291. package/src/effect-checks.ts +52 -4
  292. package/src/effect.ts +401 -132
  293. package/src/effector-wire.ts +278 -30
  294. package/src/fail.ts +17 -4
  295. package/src/gate-brand.ts +16 -0
  296. package/src/gate.ts +634 -48
  297. package/src/harness-client.ts +84 -35
  298. package/src/harness-env.ts +33 -0
  299. package/src/held-secrets.ts +197 -39
  300. package/src/holds.ts +5 -2
  301. package/src/hook-kinds/discord.ts +92 -0
  302. package/src/hook-kinds/door.ts +107 -0
  303. package/src/hook-kinds/github.ts +89 -0
  304. package/src/hook-kinds/gitlab.ts +72 -0
  305. package/src/hook-kinds/hook.ts +7 -0
  306. package/src/hook-kinds/index.ts +465 -0
  307. package/src/hook-kinds/slack.ts +151 -0
  308. package/src/hook-kinds/webhook.ts +113 -0
  309. package/src/hooks.ts +506 -0
  310. package/src/http-effect.ts +164 -70
  311. package/src/http.ts +933 -99
  312. package/src/index.ts +30 -5
  313. package/src/instance.ts +196 -26
  314. package/src/internal.ts +23 -3
  315. package/src/kube-client.ts +6 -3
  316. package/src/machine-doc.ts +29 -5
  317. package/src/mcp-effect.ts +424 -0
  318. package/src/model-keys.ts +24 -0
  319. package/src/mount-digest.ts +41 -7
  320. package/src/names.ts +82 -7
  321. package/src/notices.ts +576 -59
  322. package/src/open-body.ts +110 -30
  323. package/src/open-effect.ts +17 -4
  324. package/src/open.ts +40 -7
  325. package/src/parts.ts +125 -35
  326. package/src/party.ts +35 -1
  327. package/src/pick.ts +43 -9
  328. package/src/pool.ts +1172 -172
  329. package/src/registration.ts +249 -17
  330. package/src/repo-identity.ts +7 -3
  331. package/src/repos.ts +20 -2
  332. package/src/run-host.ts +1614 -190
  333. package/src/run-list.ts +191 -0
  334. package/src/sandbox-effect.ts +217 -66
  335. package/src/sandbox-kube.ts +116 -21
  336. package/src/secret-placement.ts +248 -35
  337. package/src/server.ts +53 -4
  338. package/src/setup.ts +158 -38
  339. package/src/snapshot-store.ts +565 -48
  340. package/src/steer.ts +111 -0
  341. package/src/stub-harness.ts +6 -2
  342. package/src/tokens.ts +362 -46
  343. package/src/vocabulary.ts +23 -4
  344. package/src/wire.ts +27 -10
  345. package/src/workspace.ts +314 -259
  346. package/src/worktree.ts +249 -0
  347. package/docs/reference/authoring-surface/type-noslotspec.md +0 -26
  348. package/docs/reference/authoring-surface/type-workspacespec.md +0 -37
  349. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +0 -61
  350. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +0 -65
@@ -19,8 +19,8 @@ runs and what it does.
19
19
  ## Why the parts are separate
20
20
 
21
21
  An Agent that works on a Repo runs code. Its [Working tools](../reference/glossary.md#working-tools) give it a shell.
22
- With the shell, it can read each file and each variable in its container. It can also connect to each address that its
23
- pod can reach. Thus jr2 puts each thing that the Agent must not touch in a different place:
22
+ With the shell, it can run each program of its image on the files in `/work`. It can also connect to each address that
23
+ its pod can reach. Thus jr2 puts each thing that the Agent must not touch in a different place:
24
24
 
25
25
  - The control of a run is in the Orchestrator. An Agent cannot change its own Machine. It can only pick from its
26
26
  [Menu](../reference/glossary.md#menu), and the Orchestrator decides what each pick does.
@@ -28,6 +28,8 @@ pod can reach. Thus jr2 puts each thing that the Agent must not touch in a diffe
28
28
  processes. Agents in two Workspaces share no files and no ports. [Workspaces](workspaces.md) tells more.
29
29
  - The credentials are in containers in which the Agent executes nothing: the Custodian, the Effector and the Repo cache.
30
30
  [Secrets and trust](secrets-and-trust.md) tells how.
31
+ - The Harness holds one secret value, with which it sends the picks. Each program that it starts runs confined with
32
+ Landlock, so the code of an Agent cannot read that value.
31
33
 
32
34
  ## What runs where
33
35
 
@@ -44,7 +46,7 @@ pod can reach. Thus jr2 puts each thing that the Agent must not touch in a diffe
44
46
  - **Instance Harness**: runs the Turns of the Menu-only Agents
45
47
  - Harness
46
48
  - Custodian
47
- - **Instance Effector**: runs the Effects that read no Worktree file
49
+ - **Instance Effector**: runs the Effects that read no Worktree file, and checks a Hook's signature
48
50
  - **Repo cache**: one pod on each Sandbox node
49
51
  - ↓ a Turn, an Effect · ↑ a pick, through the Custodian · ↓ the Repo cache gives each Repo
50
52
  - **Sandbox**: one pod for each Workspace, in the same namespace
@@ -66,7 +68,7 @@ The [Instance Harness](../reference/glossary.md#instance-harness), the
66
68
  sections below tell when.
67
69
 
68
70
  A Sandbox pod can also have a [User Container](../reference/glossary.md#user-container), when its `workspace()` names
69
- one. It runs its own image, and it mounts the [Worktree](../reference/glossary.md#worktree) read-write.
71
+ one. It runs its own image, and it mounts `/work`, with each [Worktree](../reference/glossary.md#worktree), read-write.
70
72
  [Workspaces](workspaces.md) tells more.
71
73
 
72
74
  ## On your computer
@@ -82,23 +84,30 @@ to the Orchestrator's Service, for the life of the command. The [global options]
82
84
  tell how to give a URL and a token instead. The CLI keeps no local state about the cluster.
83
85
 
84
86
  The [Console](../reference/glossary.md#console) is a page that the Orchestrator serves. You open it in a browser,
85
- through a port forward that you start. [Open the Console](../guides/open-the-console.md) gives the commands.
87
+ through a port forward that you start. It draws the Machine of each live run, and marks a run that started on an older
88
+ [Assembly](../reference/glossary.md#assembly). With the credential of a [Caller](../reference/glossary.md#caller), it
89
+ shows the open [Gates](../reference/glossary.md#gate) that you can answer. With the credential of a Caller that has
90
+ `read`, it also lists the ended runs, and shows the [Conversations](../reference/glossary.md#conversation) of the Agents
91
+ in a drawer at the bottom. [Open the Console](../guides/open-the-console.md) gives the commands.
92
+
93
+ The `jr2 workspace` commands do not talk to the Orchestrator. They read the Sandbox resources with your kubectl
94
+ credentials, and open a shell or a port forward into a Sandbox pod. Thus they work when the Orchestrator is down.
86
95
 
87
96
  ## The Orchestrator
88
97
 
89
98
  The Orchestrator is one pod for each Instance (`replicas: 1`), with the Service `jr2-orchestrator` on port 4000. It
90
99
  hosts all the [Workflows](../reference/glossary.md#workflow) of the Instance and many runs of each Workflow. It keeps
91
- the snapshot of each run on its own volume, so a run continues after a restart. [Durable runs](durable-runs.md) tells
92
- how.
100
+ the snapshot of each run on its own volume, so a run continues after a restart. It also keeps there the conversation of
101
+ each Agent, which each Harness sends during the Turn. [Durable runs](durable-runs.md) tells how, and for how long.
93
102
 
94
103
  The pod runs the `jr2-orchestrator` [Kit image](../reference/glossary.md#kit-image), which holds no code of yours. The
95
- Orchestrator loads your Workflows and your config from the [Assembly](../reference/glossary.md#assembly) that `jr2 up`
96
- publishes to it, and it keeps each Assembly on its volume. [Converge](converge.md#the-assembly) tells how.
104
+ Orchestrator loads your Workflows and your config from the Assembly that `jr2 up` publishes to it, and it keeps each
105
+ Assembly on its volume. [Converge](converge.md#the-assembly) tells how.
97
106
 
98
107
  The Orchestrator does these things:
99
108
 
100
109
  - It runs each Machine as an [xstate](https://xstate.js.org) actor, and holds the state of each run.
101
- - It serves the HTTP API that the CLI, the Console, a webhook and the Custodians call. See the
110
+ - It serves the HTTP API that the CLI, the Console, the other Callers, the Hooks and the Custodians call. See the
102
111
  [HTTP API](../reference/http-api.md).
103
112
  - It sends each Turn to a Harness, and judges each pick that comes back.
104
113
  - It writes a Sandbox resource when a run enters a Workspace. Then it watches the Sandbox resource until the Workspace
@@ -114,34 +123,41 @@ To isolate two sets of Workflows, use two Instances. Each Instance has its own n
114
123
  ## The Harness
115
124
 
116
125
  The Harness is jr2's own server for the Turns of Agents. It speaks to the model provider. It gives the model the Agent's
117
- Menu and its Working tools. It executes each Working tool that the model calls, and sends each pick through the
118
- Custodian. It holds no Agents of its own. Each Turn brings the Agent's definition with it.
119
- [Agents and Turns](agents-and-turns.md) tells what happens in a Turn.
126
+ Working tools, its [MCP tools](../reference/glossary.md#mcp-tools) and its Menu. It executes each Working tool that the
127
+ model calls, and sends each pick through the Custodian. It is the MCP client of the Agent: it calls each MCP tool on its
128
+ server, a remote one through the Custodian, or a stdio one that it starts. Each program that a Working tool starts runs
129
+ confined with Landlock, and so does each stdio MCP server. The Harness holds no Agents of its own. Each Turn brings the
130
+ Agent's definition with it. [Agents and Turns](agents-and-turns.md) tells what happens in a Turn.
120
131
 
121
132
  The Harness has two placements:
122
133
 
123
134
  - **In each Sandbox.** The Harness is the primary container of the Sandbox pod, named `harness`. It runs in the
124
135
  [Sandbox Image](../reference/glossary.md#sandbox-image), and jr2 mounts the Harness into the pod at `/opt/jr2`. Thus
125
- the Working tools of the Agent reach the tools of that image. An Agent that works on a Repo has its Turns here.
136
+ the Working tools of the Agent reach the tools of that image. Each Agent that works on a Repo has its Turns here: one
137
+ Harness serves all the Agents of the Sandbox.
126
138
  - **As the Instance Harness.** The Instance Harness is one StatefulSet for each Instance, `jr2-instance-harness`. Each
127
139
  Turn of a Menu-only Agent (`workspace: "none"`) runs here. Its pod has a Harness and a Custodian, and no Worktree. It
128
- keeps each conversation on its volume while a run can continue it. `jr2 up` converges it when a Machine of the
129
- Instance has a Menu-only Agent.
140
+ runs no program of an Agent, so its Agents can use only remote MCP servers. It keeps each conversation on its volume
141
+ while a run can continue it. `jr2 up` converges it when a Machine of the Instance has a Menu-only Agent.
130
142
 
131
143
  A Harness answers only the Orchestrator. The Orchestrator sends a bearer token on each call, and the Harness accepts no
132
144
  call without it, except its health check. `jr2 up` also converges NetworkPolicies that let only the Orchestrator's pods
133
- connect to a Harness pod.
145
+ connect to a Harness pod, and that stop each Harness pod at the cloud metadata addresses
146
+ ([Secrets and trust](secrets-and-trust.md#what-a-held-secret-does-not-stop)).
134
147
 
135
148
  ## The Custodian
136
149
 
137
150
  Each Harness pod has a Custodian: each Sandbox, and the Instance Harness. The Custodian is a container beside the
138
151
  Harness. The Agent executes no code in it. It does two things:
139
152
 
140
- - It carries four kinds of call from the pod to the Orchestrator: the Menu reads, the picks, the polls for the answer to
141
- a pick, and the fetch asks. It sends each one with the pod's Sandbox token. These four routes are all that it
142
- forwards. It refuses all other routes.
143
- - It holds the [Held secrets](../reference/glossary.md#held-secret) of its pod, such as a model API key. It puts the
144
- value into the requests that go to the hosts of each Held secret.
153
+ - It carries six kinds of call from the pod to the Orchestrator: the Menu reads, the picks, the polls for the answer to
154
+ a pick, the reports that a step boundary gave its notices to the model, the batches of the conversation's entries, and
155
+ the fetch asks. It sends the first five with the control token of the Sandbox, and the fetch asks with its fetch
156
+ token. These six routes are all that it forwards. It refuses all other routes. The Harness sends the first five on a
157
+ Unix socket in `/run/jr2`. A confined program cannot make a socket there, and the Custodian refuses a request on it
158
+ that does not have the control Stand-in. The fetch asks go to port `8081` on loopback.
159
+ - It holds the [Held secrets](../reference/glossary.md#held-secret) of its pod, such as a model API key or the token of
160
+ an MCP server. It puts the value into the requests that go to the hosts of each Held secret.
145
161
 
146
162
  The Custodian runs Envoy, from a [Pinned image](../reference/glossary.md#pinned-image). The Orchestrator writes its
147
163
  config for a Sandbox, and `jr2 up` writes it for the Instance Harness. [Secrets and trust](secrets-and-trust.md) tells
@@ -149,20 +165,25 @@ how the Custodian holds a credential.
149
165
 
150
166
  ## The Effector
151
167
 
152
- The Effector runs each `http` and `push` Effect, each `command` that names an Effect secret, and each `dispatch` that
153
- names a target. Thus a credential that an Effect uses stays out of the Harness container. The Effector is jr2's own
168
+ The Effector runs each `http`, `mcp` and `push` Effect, each `command` that names an Effect secret, and each `dispatch`
169
+ that names a target. Thus a credential that an Effect uses stays out of the Harness container. The Effector is jr2's own
154
170
  program, and it runs in a container in which the Agent executes nothing. [Effects](effects.md) tells which Effect runs
155
171
  where. It has two placements:
156
172
 
157
- - **The Sandbox Effector** is a container in a Sandbox pod, named `effector`. It mounts the Worktree read-only. It runs
158
- a `push`, an `http` Effect whose body is a Worktree file, and a `command` that names an Effect secret. Its image is
159
- the Sandbox Image, unless the `workspace()` names a different image.
173
+ - **The Sandbox Effector** is a container in a Sandbox pod, named `effector`. It mounts `/work` read-only. It runs a
174
+ `push`, an `http` Effect whose body is a Worktree file, and a `command` that names an Effect secret. Its image is the
175
+ Sandbox Image, unless the `workspace()` names a different image.
160
176
  - **The Instance Effector** is one pod for each Instance, `jr2-instance-effector`. It runs each `http` Effect that reads
161
- no Worktree file, and each `dispatch` that starts a run on a different Orchestrator. It holds the Instance token of
162
- that Orchestrator as an Effect secret. It runs in the `jr2-harness` Kit image.
177
+ no Worktree file, each `mcp` Effect, and each `dispatch` that starts a run on a different Orchestrator. It holds the
178
+ token of each MCP server that an `mcp` Effect calls, and the Caller token for that Orchestrator, as Effect secrets. It
179
+ also serves the [Hooks](../reference/glossary.md#hook): it holds the secret of each Hook that checks a signature with
180
+ a shared secret (`mac`) and checks the signature, and it sends the follow-up of a kind that posts one, such as the
181
+ reply to a Slack click. It runs in the `jr2-harness` Kit image.
163
182
 
164
- Each placement exists only when a Machine of the Instance puts an Effect there. A `command` that names no Effect secret
165
- does not use an Effector. It runs in the Harness container of its Sandbox, with the same tools and access as the Agent.
183
+ A Sandbox Effector exists only when a Machine of the Instance puts an Effect there. The Instance Effector exists only
184
+ when a Machine puts an Effect there or a Hook needs it. A `command` that names no Effect secret does not use an
185
+ Effector. It runs in the Harness container of its Sandbox, with the same tools and access as the Agent, and confined in
186
+ the same way.
166
187
 
167
188
  The Orchestrator signs each request that it sends to an Effector, and the Effector signs each answer. The Orchestrator
168
189
  only asks, and the Effector holds the credentials.
@@ -188,7 +209,8 @@ the Orchestrator stopped for longer than the Lease allows. [Workspaces](workspac
188
209
 
189
210
  The Repo cache keeps one bare clone of each Repo on each node that needs it. It is a DaemonSet, `jr2-repo-cache`, in the
190
211
  namespace of the Instance. It has one pod on each [Sandbox node](../reference/glossary.md#sandbox-node). `jr2 up`
191
- converges it when a Machine of the Instance uses a Sandbox.
212
+ converges it when a Machine of the Instance uses a Sandbox. Each pod also checks that the kernel of its node has
213
+ Landlock. On a node without Landlock, the pod is not ready, and `jr2 up` names that node and refuses to converge.
192
214
 
193
215
  The operator mounts the cache of each Repo into a Sandbox, read-only, at `/repos`. The clone in the Sandbox gets its
194
216
  objects from that cache. A `git fetch origin` in the Sandbox asks the Repo cache for a fetch. Then the Repo cache
@@ -204,6 +226,8 @@ credential to fetch.
204
226
  - A Harness accepts a call only from the Orchestrator, with the bearer of its own pod. Only its health check needs no
205
227
  bearer.
206
228
  - The Agent executes code only in the Harness container. It executes nothing in the Custodian or in the Effector.
229
+ - In the Harness container, each program that the Harness starts runs confined. It cannot read what the Harness holds.
230
+ - Each Sandbox node needs Landlock in its kernel. `jr2 up` refuses to converge while a Sandbox node has none.
207
231
  - The Orchestrator watches the Sandbox resources and never reads a pod. The operator puts the facts of the pod on the
208
232
  status of the Sandbox resource.
209
233
  - A Sandbox has one pod for its life. A lost pod is never made again.
@@ -226,5 +250,7 @@ credential to fetch.
226
250
  [ADR-0053](../reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md),
227
251
  [ADR-0058](../reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md),
228
252
  [ADR-0059](../reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md),
229
- [ADR-0063](../reference/decisions/0063-the-orchestrator-watches-the-cluster.md) and
230
- [ADR-0068](../reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md)
253
+ [ADR-0063](../reference/decisions/0063-the-orchestrator-watches-the-cluster.md),
254
+ [ADR-0068](../reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md),
255
+ [ADR-0076](../reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md) and
256
+ [ADR-0077](../reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md)
@@ -0,0 +1,205 @@
1
+ ---
2
+ title: Callers and Hooks
3
+ description: Who acts on an Instance from outside its Machines, what each one may do, and what a Hook does.
4
+ ---
5
+
6
+ # Callers and Hooks
7
+
8
+ A [Caller](../reference/glossary.md#caller) is the holder of a credential that acts on an
9
+ [Instance](../reference/glossary.md#instance) from outside its [Machines](../reference/glossary.md#machine). A person, a
10
+ CI job, a bridge to a chat service and an outside service are Callers. A Caller can start a run, read it, answer a
11
+ [Gate](../reference/glossary.md#gate), send a [Steering message](../reference/glossary.md#steering-message) or cancel a
12
+ run, for the [Workflows](../reference/glossary.md#workflow) that its scopes name. A
13
+ [Hook](../reference/glossary.md#hook) is the door of an Instance for one outside service, such as a forge. It is a
14
+ Caller that can only answer a Gate or bring items to a Source. Its [Hook kind](../reference/glossary.md#hook-kind) is
15
+ the protocol of the service.
16
+
17
+ ## Why it exists
18
+
19
+ One Instance token can do everything on each run. A CI job that starts runs, a bridge that answers Gates, and a teammate
20
+ with a browser need less than that. If each one holds the Instance token, each one can do each act on each run, and the
21
+ run cannot say who acted. Thus jr2 knows each Caller by its credential, gives it only its scopes, and records it on each
22
+ act.
23
+
24
+ An outside service sends a delivery, such as a review on a forge. It does not send a bearer token, and each service has
25
+ its own signature, its own body and its own handshake. A Hook checks that a delivery comes from its service, and decides
26
+ nothing. The Gate or the Source that hears it decides what the delivery means for its run.
27
+
28
+ ## How a Caller is known
29
+
30
+ The Orchestrator has an ordered list of authenticators. Each one looks at the credential of a request. It gives a
31
+ Caller, or it says that the credential is not its credential. The first one that claims the credential decides. When it
32
+ finds the credential bad, the answer is 401, and the next authenticator does not look at it. A credential that no
33
+ authenticator claims also gets 401.
34
+
35
+ | Authenticator | The credential | The Caller |
36
+ | ----------------- | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
37
+ | Instance token | The token in the `jr2-instance` Secret. The CLI reads it over the Kubernetes API. | `token/instance`, with each scope for each Workflow |
38
+ | Named token | A value that starts with `jr2c_`, for a name in [`callers.tokens`](../reference/config.md#callers-tokens) | `token/<name>`, with the scopes of its entry |
39
+ | JWT | A JWT of an issuer in [`callers.issuers`](../reference/config.md#callers-issuers) | `jwt/<sub>`, with the scopes that its groups give |
40
+ | A Hook's verifier | A delivery at `POST /hooks/<name>`, with no bearer token | `hook/<name>`, with `answer` only |
41
+
42
+ The tokens of a Sandbox and the publish token are not Callers, and no authenticator makes a Caller from them. Thus an
43
+ Agent cannot answer a Gate. [Secrets and trust](secrets-and-trust.md#the-tokens) tells where each token is kept.
44
+
45
+ For a JWT, jr2 checks the signature with the published keys of the issuer, and the issuer, the audience and the expiry.
46
+ The name of the Caller is the `sub` claim, and the name that jr2 shows is the `email` claim. The `groups` map of the
47
+ issuer entry gives scopes for the values of a claim, such as the groups of a person. A JWT that gets no scope from the
48
+ map can do nothing. An issuer with `login: true` also lets a person log in to the
49
+ [Console](../reference/glossary.md#console) ([Open the Console](../guides/open-the-console.md#log-in)).
50
+
51
+ [`jr2 whoami`](../reference/cli.md#whoami) and [`GET /whoami`](../reference/http-api.md#get-whoami) show the Caller of a
52
+ credential, with its scopes. Use them to see what a credential can do before a request fails.
53
+
54
+ ## Scopes
55
+
56
+ A scope is one act of a Caller. Its value is `"all"`, or a list of the names of the Workflows that it opens:
57
+
58
+ | Scope | The act |
59
+ | -------- | ----------------------------------------------------------------------------------------- |
60
+ | `start` | Start a run. |
61
+ | `read` | Read the full state of a run, follow its feed, list the runs, and read its conversations. |
62
+ | `answer` | Answer a Gate, and read the open Gates and their meta. |
63
+ | `cancel` | Cancel a run. |
64
+ | `steer` | Send a Steering message to an Agent of a run. |
65
+
66
+ For a route that addresses a run, jr2 checks the scope against the Workflow of that run. The name of a Chart wrapper
67
+ also opens each named Chart of that wrapper. A request without the scope gets 403, which names the scope and the
68
+ Workflow. [`GET /runs`](../reference/http-api.md#get-runs) shows only the runs of the Workflows that the Caller can
69
+ read. A few routes, such as a runtime Chart and the log of a Hook, need the Instance token: no scope opens them. The
70
+ [HTTP API](../reference/http-api.md#band-guarded) gives the scope of each route.
71
+
72
+ The `answer` scope can also name single Gates of a Workflow, with an item `{ workflow, gates }` beside the names of
73
+ Workflows. The name of a Gate is its invoke id. A Caller with such an item answers only those Gates of that Workflow,
74
+ and it sees only those Gates, in [`GET /gates`](../reference/http-api.md#get-gates) and in `GET /runs/:runId`. A gate
75
+ POST to another open Gate of that Workflow gets 403, which names the scope, the Workflow and the Gate. `jr2 up` refuses
76
+ a Gate name that the Machines of the Workflow do not have. The other scopes, and the `may.answer` of a Hook, name only
77
+ Workflows.
78
+
79
+ A scope does not limit what a run does. A run that a Caller starts can start a run of a different Workflow with a
80
+ [`dispatch`](../reference/authoring-surface/dispatch.md) Effect. The Machine is the limit of what its run does.
81
+
82
+ ## On behalf of
83
+
84
+ A Caller can say that it acts on behalf of a different party, in the header `jr2-on-behalf-of`. The CLI sends the name
85
+ of your Kubernetes user. A bridge can send the name of the person in the chat service. jr2 records this name and shows
86
+ it, but it does not check it. The one exception is a Hook: the name comes from the body of the delivery, and the
87
+ signature of the service covers the body.
88
+
89
+ ## The record of each act
90
+
91
+ The run records the Caller of each act on a guarded route: its `via`, its `name`, the name that jr2 shows, and
92
+ `onBehalfOf`. It does not record the scopes.
93
+
94
+ - `startedBy`: the Caller that started the run. A run that a local `dispatch` started records that run and the slot. A
95
+ run that jr2 started, such as the run of a standing Pool, records `jr2` and why: `boot`, `publish`, `hand-over` or
96
+ `resume`.
97
+ - `cancelledBy`: the Caller that cancelled the run.
98
+ - `answers`: the last 100 answers to the Gates of the run, each with the Caller that answered.
99
+
100
+ The [Sender](../reference/glossary.md#sender) of an event that a Gate delivers also holds the Caller, and `checked`
101
+ names the fields of it that jr2 checked. [`jr2 status`](../reference/cli.md#status) follows `startedBy` from run to run,
102
+ to the first Caller, and prints it as `started by:`. The open routes do not show these records.
103
+
104
+ ## How a Hook behaves
105
+
106
+ The `hooks` block of `jr2.config.ts` declares each Hook by its name, with its kind. The address of the Hook is
107
+ `POST /hooks/<name>`. The kind makes the Hook from two things: the credential, and `may.answer`, the Workflows whose
108
+ Gates the Hook can answer. The default is every Workflow. A Hook never starts a run. To bring items to a
109
+ [Pool](../reference/glossary.md#pool), it answers the `source` Gate of the Pool, so `may.answer` must name the Workflow
110
+ of the Pool.
111
+
112
+ **One delivery at a Hook**
113
+
114
+ - **Service**: a forge or a chat service
115
+ - **Door**: the Hook's address in the Orchestrator
116
+ - **Instance Effector**: holds a mac secret
117
+ - **Kind**: the protocol of the service
118
+ - **Handlers**: of the open Gates and Sources
119
+ - Service → Door: a delivery
120
+ - Door → Instance Effector: the signed bytes, for mac only
121
+ - Instance Effector → Door: match, or no match
122
+ - Door → Kind: the request, without a token header
123
+ - Kind → Door: a handshake, or the event key, the id and the payload
124
+ - Door → Handlers: the parsed payload
125
+ - Door → Service: the outcome, or a text for a person
126
+
127
+ The door does the same steps for each kind:
128
+
129
+ 1. **It verifies the delivery.** The kind tells what the service signed. The door runs one of three verifiers:
130
+ - `mac`: a shared secret signs the bytes, such as on GitHub and Slack. The
131
+ [Instance Effector](../reference/glossary.md#instance-effector) holds the secret, and it answers only "match" or
132
+ "no match". The Orchestrator never holds the secret, because a secret that verifies a delivery can also make one.
133
+ - `token`: the service sends a shared token in a header, such as on GitLab. The Orchestrator keeps only the SHA-256
134
+ digest of the token, and removes the header before the kind reads the request.
135
+ - `ed25519`: a key pair signs the delivery, such as on Discord. The config holds the public key, which cannot sign.
136
+
137
+ When the service signs a time, the door refuses a time that is more than 300 seconds from its clock. A delivery that
138
+ fails gets 401, and nothing more occurs.
139
+
140
+ Before the verifier, the door refuses a body that is larger than 2 MiB with 413. It reads no more of the body than
141
+ this limit.
142
+
143
+ 2. **The kind reads the delivery.** A handshake, such as a GitHub `ping`, gets the answer of the kind, and goes to no
144
+ Gate. Otherwise the kind gives the event key, the id of the delivery, `onBehalfOf` and the payload.
145
+ 3. **The schema of the event key parses the payload.** A payload that the schema refuses goes to no Gate.
146
+ 4. **The door looks for a repeat.** A delivery whose id or body the Hook got before goes to no Gate. The door does this
147
+ check after the parse, so a payload that the schema refused is parsed again when the service sends it again.
148
+ 5. **The delivery goes to each handler that hears it**: each open Gate and each Source, in each live run of a Workflow
149
+ that the Hook can answer, whose handlers name the kind and the event key. [Gates](gates.md#a-gate-that-hears-a-hook)
150
+ and [Pools and Sources](pools-and-sources.md#a-hook-reaches-a-source) tell what a handler gives.
151
+
152
+ The answer is the outcome of the delivery, as JSON: where it went, and what each Gate or Source did with it. For a chat
153
+ command or a click, the person who acted reads one text in the envelope of the kind
154
+ ([The answer that a person reads](gates.md#the-answer-that-a-person-reads)).
155
+
156
+ The log of the Hook keeps each delivery and its outcome for `runs.retain`, 30 days by default. The repeat check reads
157
+ it. Print it with [`jr2 hooks <name>`](../reference/cli.md#hooks).
158
+
159
+ ## Hook kinds
160
+
161
+ A Hook kind is code that `jr2.config.ts` imports. It tells what the service signed, how to read a delivery and its
162
+ payloads, the handshake of the service, and the envelope of a text that a person reads. A workflow module imports the
163
+ same kind for the types of its handlers. Thus a handler for an event key that the kind does not give does not compile.
164
+
165
+ - `@jr2/orchestrator/hooks` ships the kinds `github`, `gitlab`, `slack` and `discord`.
166
+ - [`webhook()`](../reference/hooks.md#webhook) makes a kind from data, for a service that sends JSON, signs only the
167
+ body, and has no handshake.
168
+ - [`defineHookKind()`](../reference/hooks.md#definehookkind) makes a kind for any other service, in a module of the
169
+ Instance or in a package of its own.
170
+
171
+ A kind never gets the credential: jr2 makes the constructor of the kind, so the credential goes from the config to the
172
+ door. A kind is pure and synchronous. It does no I/O, and it does not read a clock. The door does the verify, the time
173
+ window, the repeat check and the follow-up post. When a kind gives a follow-up post, such as the reply to a Slack click,
174
+ it declares the hosts of its posts, and `jr2 up` adds them to the targets of the Instance Effector.
175
+
176
+ ## Rules
177
+
178
+ - The first authenticator that claims a credential decides. A credential that it finds bad gets 401.
179
+ - A Sandbox token, a control token and the publish token are not Callers.
180
+ - A request without the scope for the Workflow gets 403. A JWT that gets no scope gets 403 on each route that needs a
181
+ scope.
182
+ - `onBehalfOf` is a claim. jr2 checks it only for a Hook, where the signature covers it.
183
+ - A named token's value is in a Secret of its own. The Orchestrator keeps only its SHA-256 digest.
184
+ - `cors` lists the origins of the browser apps on a different origin that can call the Orchestrator. The Console needs
185
+ no entry.
186
+ - A Hook can only answer a Gate or bring items to a Source, of the Workflows in its `may.answer`. It never starts a run.
187
+ - The door refuses a signed time that is more than 300 seconds from its clock. No config changes the window.
188
+ - Two kinds with one id fail the typecheck and `jr2 up`.
189
+
190
+ ## Read next
191
+
192
+ - [Give a Caller a scope](../guides/give-a-caller-a-scope.md)
193
+ - [Answer a Gate from a forge](../guides/answer-a-gate-from-a-forge.md)
194
+ - [Start a run for each alert](../guides/start-a-run-for-each-alert.md)
195
+ - [Write a Hook kind](../guides/write-a-hook-kind.md)
196
+ - [Open the Console](../guides/open-the-console.md)
197
+ - [Secrets and trust](secrets-and-trust.md), [Gates](gates.md) and [Pools and Sources](pools-and-sources.md)
198
+ - Reference: [`callers`](../reference/config.md#callers), [`hooks`](../reference/config.md#hooks),
199
+ [`cors`](../reference/config.md#cors), [`@jr2/orchestrator/hooks`](../reference/hooks.md),
200
+ [`jr2 whoami`](../reference/cli.md#whoami), [`jr2 hooks`](../reference/cli.md#hooks), and the
201
+ [HTTP API](../reference/http-api.md)
202
+ - [ADR-0078](../reference/decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md): the
203
+ Callers, the scopes, the Hooks and the Hook kinds
204
+ - [ADR-0014](../reference/decisions/0014-observation-is-open-run-state-is-not.md): which scope opens which route
205
+ - [ADR-0011](../reference/decisions/0011-workflow-defined-events.md): a Gate hears a Hook
@@ -30,17 +30,21 @@ it bad text.
30
30
  [Open](../reference/glossary.md#open) part that a run binds, not a composer. Its declaration has three parts:
31
31
 
32
32
  - **`lends`**: the parts of the Chart wrapper that a Chart can use, by their slot keys. These are Agents, Effects,
33
- Machines, guards, actions and delays. `gate: true` lends the [Gate](../reference/glossary.md#gate), so that a Chart
34
- can wait for a person. `fail: true` lends [`fail()`](../reference/authoring-surface/fail.md), so that a Chart can end
35
- its run as a [Failure](../reference/glossary.md#failure) with `invoke: { src: "fail", input: { reason } }`.
33
+ Machines, Gate slots (`gates`), guards, actions and delays. `gate: true` lends the
34
+ [Gate](../reference/glossary.md#gate), so that a Chart can wait for a person. A lent Gate slot hears a
35
+ [Hook](../reference/glossary.md#hook), and the door refuses a Chart whose `events` do not hold each event of the
36
+ slot's `delivers`. `fail: true` lends [`fail()`](../reference/authoring-surface/fail.md), so that a Chart can end its
37
+ run as a [Failure](../reference/glossary.md#failure) with `invoke: { src: "fail", input: { reason } }`.
36
38
  - **`allows`**: what the Agents that a Chart adds can have. `models` lists the models, and `workspace` lists the
37
39
  Workspace accesses. With no model, a Chart cannot add an Agent.
38
40
  - **`ceilings`**: the limits of one Chart: the Agent Turns, the wall clock, the transitions, the children at one time,
39
41
  and the size of the Chart in bytes.
40
42
 
41
43
  A Chart gets nothing else. A lent Effect keeps its url, its argv and its secrets, so a Chart fills only its input. A
42
- Chart has no url, no secret, no image and no Repo of its own. The Open body also decides where each Turn of the Chart
43
- runs: in the Sandbox of its `workspace()`, or on the [Instance Harness](../reference/glossary.md#instance-harness).
44
+ lent [`worktree()`](../reference/authoring-surface/worktree.md) must state `expect`, because a Chart cannot state it. A
45
+ Chart reads the cut with the kit guard `jr2.ready`, then gives the same literal path as `cwd` and `worktree`. A Chart
46
+ has no url, no secret, no image and no Repo of its own. The Open body also decides where each Turn of the Chart runs: in
47
+ the Sandbox of its `workspace()`, or on the [Instance Harness](../reference/glossary.md#instance-harness).
44
48
 
45
49
  ## What a Chart holds
46
50
 
@@ -48,7 +52,8 @@ A Chart is [xstate](https://xstate.js.org)'s own JSON config, with these limits:
48
52
 
49
53
  - States, transitions, `after` delays, and nested and parallel states. A Chart has 200 states at most, 8 levels deep.
50
54
  - Each `invoke.src` is a lent slot key or an Agent of the Chart. Each guard, action and delay is a lent name. Each Chart
51
- also has the guard `jr2.failure`, which is true on an `onError` when the error is a Failure.
55
+ also has the guard `jr2.failure`, which is true on an `onError` when the error is a Failure, and the guard
56
+ `jr2.ready`, which is true on an `onDone` when the cut is `ready`.
52
57
  - A state that invokes an Agent can route [`agent.fault`](../reference/mechanism-events.md#agent-fault) in its `on`, but
53
58
  it does not have to. When no transition of the Chart takes `agent.fault`, the Open body sends it, unchanged, to the
54
59
  Chart wrapper. The wrapper's own `agent.fault` route takes it, as for an Agent of the wrapper, so one route on the
@@ -104,8 +109,8 @@ wrapper only. It comes from one of two places:
104
109
  - A **runtime Chart**: `PUT /workflows/<wrapper>/charts/<name>`, with the Instance token. The Orchestrator keeps it on
105
110
  its volume. `DELETE` on the same path removes it.
106
111
 
107
- [`jr2 runs`](../reference/cli.md#runs) lists each named Chart under its wrapper. When a publish removes a part that a
108
- runtime Chart uses, the Chart stays, and it is unrunnable until a publish gives the part back.
112
+ [`jr2 workflows`](../reference/cli.md#workflows) lists each named Chart under its wrapper. When a publish removes a part
113
+ that a runtime Chart uses, the Chart stays, and it is unrunnable until a publish gives the part back.
109
114
 
110
115
  ## A run keeps its Chart
111
116
 
@@ -250,7 +250,7 @@ show.
250
250
  | Key | What it changes |
251
251
  | ----------- | ------------------------------------------------------------------------------------- |
252
252
  | `agents` | The definition of an Agent, by slot, field by field. `{ model }` keeps the rest. |
253
- | `effects` | The Open url, argv or secret of an Effect, by slot. |
253
+ | `effects` | The Open url, argv, secret, MCP server or tool of an Effect, by slot. |
254
254
  | `actors` | A Machine that this Machine composes, by slot. The value has the same keys, one down. |
255
255
  | `repos` | The Repo Slots of the [`workspace()`](../reference/authoring-surface/workspace.md). |
256
256
  | `image` | The Sandbox Image of the `workspace()`. |
@@ -272,6 +272,7 @@ A Machine marks a part Open with [`open`](../reference/authoring-surface/open.md
272
272
  - a Repo Slot with no url, or the whole Repo Slot map (`repos: open`)
273
273
  - the model of an Agent (`agent({ model: open, … })`)
274
274
  - the url of an `http` Effect, the argv of a `command`, or the name of the secret of an Effect (`secret(open)`)
275
+ - the server or the tool of an `mcp` Effect (`mcp.call(open, open, { input })`)
275
276
 
276
277
  An Open part has no default. You bind it with `customize()`. Each Open part has two checks:
277
278
 
@@ -280,6 +281,14 @@ An Open part has no default. You bind it with `customize()`. Each Open part has
280
281
  - At run time, the part refuses too. An Agent does not start a [Turn](../reference/glossary.md#turn) with an Open model.
281
282
  An Effect does not run while it is Open.
282
283
 
284
+ One Open part only warns: the look of a Gate slot (`look: open`). `jr2 up` prints a warning for a look that nobody
285
+ bound, because a person can still answer the Gate with `jr2 send`.
286
+
287
+ The MCP servers of an Agent are not Open. A packaged Machine's Agent names the servers that it needs, such as
288
+ `slack-acme`. When the `mcp` block of your `jr2.config.ts` does not declare that name, `jr2 up` refuses the Workflow and
289
+ prints the `customize()` line that binds the Agent to a server that you declare:
290
+ `customize(machine, { agents: { <slot>: { mcp: [...] } } })`. The list replaces the Agent's whole `mcp` list.
291
+
283
292
  [Bind a shipped Machine's Open parts](../guides/bind-open-parts.md) shows each binding.
284
293
 
285
294
  ## @jr2/machines
@@ -40,7 +40,7 @@ The diagram shows what `jr2 up` makes, and what [`jr2 run`](../reference/cli.md#
40
40
  - **Assembly**: your Workflows and your config
41
41
  - **jr2-instance Secret**: holds the Instance token
42
42
  - **Instance Harness**: when an Agent is Menu-only
43
- - **Instance Effector**: when an Effect runs there
43
+ - **Instance Effector**: when an Effect runs there, or a Hook needs it
44
44
  - **Repo cache**: when a Machine has a Sandbox
45
45
  - ↓ the Orchestrator asks for each Sandbox · ↑ the operator makes its pod
46
46
  - **jr2-system**: one for each cluster
@@ -79,10 +79,15 @@ the cluster, see [Architecture](architecture.md).
79
79
  Until each check passes, `jr2 up` writes nothing that the Instance or the operator reads. The pod has up to 5
80
80
  minutes to start, and each model has up to 6 minutes. `jr2 up` writes each model's result as it comes. With
81
81
  `--skip-provider-preflight`, no pod checks the models, and `jr2 up` writes a warning that names each model that it
82
- did not check.
82
+ did not check. The same pod asks each remote MCP server that an Agent names for its tools, through its Custodian.
83
+ `jr2 up` refuses an allowlisted tool that the server does not list, and a server that refuses the token. The flag
84
+ skips this check too.
83
85
  12. It applies the operator. For a Repo with an ssh url, it asks where the deploy key comes from.
84
86
  13. It applies the Secrets and the Orchestrator, and waits for the rollout. When the Machines need them, it also waits
85
87
  for the Repo cache, the Instance Harness and the [Instance Effector](../reference/glossary.md#instance-effector).
88
+ The Repo cache agent on each Sandbox node checks that the kernel of its node has Landlock, which jr2 needs. While a
89
+ Sandbox node has no Landlock, the converge fails. The error names each such node, the reason, and the fix: keep the
90
+ node out with `sandbox.nodeSelector` or a taint, or enable Landlock in its kernel. Then run `jr2 up` again.
86
91
  14. It publishes the Assembly to the Orchestrator. When the Orchestrator already has this Assembly, it publishes
87
92
  nothing.
88
93
  15. It removes the images that nothing uses now.