@jr2/orchestrator 0.5.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 (185) hide show
  1. package/docs/agents-block.md +2 -2
  2. package/docs/concepts/agents-and-turns.md +56 -20
  3. package/docs/concepts/architecture.md +30 -17
  4. package/docs/concepts/charts.md +6 -3
  5. package/docs/concepts/converge.md +3 -0
  6. package/docs/concepts/effects.md +86 -31
  7. package/docs/concepts/machines-and-workflows.md +9 -0
  8. package/docs/concepts/pools-and-sources.md +6 -6
  9. package/docs/concepts/secrets-and-trust.md +126 -42
  10. package/docs/concepts/the-menu.md +3 -0
  11. package/docs/concepts/workspaces.md +129 -51
  12. package/docs/get-started/an-agent-in-a-sandbox.md +21 -19
  13. package/docs/get-started/have-your-assistant-write-a-machine.md +111 -40
  14. package/docs/get-started/install.md +4 -0
  15. package/docs/guides/add-tools-to-the-sandbox.md +17 -4
  16. package/docs/guides/bind-open-parts.md +5 -4
  17. package/docs/guides/cut-a-worktree.md +560 -0
  18. package/docs/guides/fix-a-failed-converge.md +1 -1
  19. package/docs/guides/let-an-agent-write-a-chart.md +5 -1
  20. package/docs/guides/look-inside-a-sandbox.md +18 -15
  21. package/docs/guides/move-from-kind-to-a-shared-cluster.md +16 -0
  22. package/docs/guides/push-and-sign-a-branch.md +67 -21
  23. package/docs/guides/run-a-command-or-call-a-url.md +84 -41
  24. package/docs/guides/run-one-worker-per-item.md +19 -18
  25. package/docs/guides/set-an-agents-access-and-dials.md +67 -22
  26. package/docs/guides/size-a-workspace.md +19 -6
  27. package/docs/guides/use-a-hosted-provider.md +6 -0
  28. package/docs/guides/watch-a-run.md +47 -24
  29. package/docs/guides/write-a-workflow.md +1 -1
  30. package/docs/llms.txt +33 -12
  31. package/docs/reference/authoring-surface/agent.md +34 -11
  32. package/docs/reference/authoring-surface/answer.md +19 -3
  33. package/docs/reference/authoring-surface/arg.md +27 -5
  34. package/docs/reference/authoring-surface/branchname.md +15 -0
  35. package/docs/reference/authoring-surface/command.md +36 -21
  36. package/docs/reference/authoring-surface/fail.md +17 -4
  37. package/docs/reference/authoring-surface/file.md +36 -16
  38. package/docs/reference/authoring-surface/http.md +108 -8
  39. package/docs/reference/authoring-surface/isready.md +14 -0
  40. package/docs/reference/authoring-surface/jr2setup.md +1 -1
  41. package/docs/reference/authoring-surface/open.md +36 -5
  42. package/docs/reference/authoring-surface/paramsof.md +22 -4
  43. package/docs/reference/authoring-surface/pool.md +4 -4
  44. package/docs/reference/authoring-surface/push.md +30 -12
  45. package/docs/reference/authoring-surface/readyof.md +21 -0
  46. package/docs/reference/authoring-surface/secret.md +25 -4
  47. package/docs/reference/authoring-surface/source.md +3 -3
  48. package/docs/reference/authoring-surface/type-agentdefinition.md +2 -2
  49. package/docs/reference/authoring-surface/type-agentslots.md +4 -1
  50. package/docs/reference/authoring-surface/type-anyframepath.md +14 -0
  51. package/docs/reference/authoring-surface/type-anyworktreepath.md +15 -0
  52. package/docs/reference/authoring-surface/type-bindactors.md +14 -0
  53. package/docs/reference/authoring-surface/type-binding.md +5 -6
  54. package/docs/reference/authoring-surface/type-commandinput.md +4 -3
  55. package/docs/reference/authoring-surface/type-commandoptions.md +0 -22
  56. package/docs/reference/authoring-surface/type-commandoutput.md +22 -4
  57. package/docs/reference/authoring-surface/type-customize.md +4 -2
  58. package/docs/reference/authoring-surface/type-dispatchoutput.md +8 -1
  59. package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
  60. package/docs/reference/authoring-surface/type-filebody.md +4 -8
  61. package/docs/reference/authoring-surface/type-framepath.md +14 -0
  62. package/docs/reference/authoring-surface/type-hostinjectedinput.md +2 -4
  63. package/docs/reference/authoring-surface/type-httpdefinition.md +6 -3
  64. package/docs/reference/authoring-surface/type-httpfileinput.md +20 -0
  65. package/docs/reference/authoring-surface/type-httpoutput.md +7 -1
  66. package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
  67. package/docs/reference/authoring-surface/type-namestart.md +77 -0
  68. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +1 -6
  69. package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -0
  70. package/docs/reference/authoring-surface/type-opencommandinput.md +7 -2
  71. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +1 -6
  72. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +5 -10
  73. package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
  74. package/docs/reference/authoring-surface/type-poolspec.md +2 -2
  75. package/docs/reference/authoring-surface/type-pushinput.md +22 -3
  76. package/docs/reference/authoring-surface/type-pushoutput.md +32 -7
  77. package/docs/reference/authoring-surface/type-reposlot.md +5 -5
  78. package/docs/reference/authoring-surface/type-sandboxoptions.md +11 -4
  79. package/docs/reference/authoring-surface/type-slothandles.md +5 -13
  80. package/docs/reference/authoring-surface/type-sourcespec.md +10 -10
  81. package/docs/reference/authoring-surface/type-workspaced.md +29 -8
  82. package/docs/reference/authoring-surface/type-workspacehandles.md +5 -6
  83. package/docs/reference/authoring-surface/type-workspaceoptions.md +1 -13
  84. package/docs/reference/authoring-surface/type-worktreefound.md +16 -0
  85. package/docs/reference/authoring-surface/type-worktreeinput.md +56 -0
  86. package/docs/reference/authoring-surface/type-worktreekey.md +14 -0
  87. package/docs/reference/authoring-surface/type-worktreeoptions.md +29 -0
  88. package/docs/reference/authoring-surface/type-worktreeoutput.md +45 -0
  89. package/docs/reference/authoring-surface/type-worktreepath.md +16 -0
  90. package/docs/reference/authoring-surface/type-worktreeready.md +37 -0
  91. package/docs/reference/authoring-surface/type-worktreerefused.md +21 -0
  92. package/docs/reference/authoring-surface/workspace.md +65 -37
  93. package/docs/reference/authoring-surface/worktree.md +71 -0
  94. package/docs/reference/authoring-surface.md +30 -10
  95. package/docs/reference/cli.md +1 -1
  96. package/docs/reference/config.md +13 -4
  97. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +10 -9
  98. package/docs/reference/decisions/0002-duplex-streaming-actor.md +17 -10
  99. package/docs/reference/decisions/0004-worktree-provisioning-model.md +27 -21
  100. package/docs/reference/decisions/0005-sandbox-pod-composition.md +29 -19
  101. package/docs/reference/decisions/0006-agent-control-surface.md +6 -3
  102. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +30 -30
  103. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +35 -17
  104. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +8 -2
  105. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +9 -7
  106. package/docs/reference/decisions/0017-pool-over-a-source-port.md +6 -4
  107. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +21 -9
  108. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +2 -1
  109. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +8 -9
  110. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +62 -65
  111. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +9 -1
  112. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +13 -12
  113. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +4 -1
  114. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +18 -19
  115. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +8 -8
  116. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +4 -4
  117. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +57 -46
  118. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +5 -0
  119. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +20 -8
  120. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +24 -24
  121. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +5 -3
  122. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +110 -67
  123. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +101 -74
  124. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +5 -6
  125. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +2 -0
  126. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +181 -153
  127. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +74 -63
  128. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +31 -20
  129. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +1 -1
  130. package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +295 -0
  131. package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +320 -0
  132. package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
  133. package/docs/reference/decisions.md +3 -0
  134. package/docs/reference/glossary.md +51 -12
  135. package/docs/reference/http-api.md +45 -7
  136. package/docs/reference/machines.md +0 -1
  137. package/docs/why.md +5 -4
  138. package/package.json +3 -3
  139. package/src/actor.ts +161 -35
  140. package/src/agent.ts +6 -6
  141. package/src/ambient.ts +3 -5
  142. package/src/chart.ts +186 -19
  143. package/src/charts.ts +9 -2
  144. package/src/config.ts +16 -6
  145. package/src/custodian.lua +17 -5
  146. package/src/custodian.ts +251 -77
  147. package/src/customize.ts +11 -3
  148. package/src/effect-checks.ts +50 -3
  149. package/src/effect.ts +343 -127
  150. package/src/effector-wire.ts +6 -3
  151. package/src/fail.ts +17 -4
  152. package/src/harness-client.ts +74 -31
  153. package/src/harness-env.ts +33 -0
  154. package/src/held-secrets.ts +125 -16
  155. package/src/holds.ts +3 -0
  156. package/src/http-effect.ts +88 -32
  157. package/src/http.ts +56 -17
  158. package/src/index.ts +15 -1
  159. package/src/internal.ts +1 -0
  160. package/src/kube-client.ts +6 -3
  161. package/src/machine-doc.ts +3 -4
  162. package/src/model-keys.ts +24 -0
  163. package/src/names.ts +19 -6
  164. package/src/notices.ts +443 -54
  165. package/src/open-body.ts +85 -19
  166. package/src/open.ts +36 -5
  167. package/src/parts.ts +40 -28
  168. package/src/party.ts +33 -0
  169. package/src/pick.ts +41 -7
  170. package/src/pool.ts +44 -23
  171. package/src/registration.ts +53 -11
  172. package/src/repo-identity.ts +7 -3
  173. package/src/repos.ts +20 -2
  174. package/src/run-host.ts +106 -20
  175. package/src/sandbox-effect.ts +217 -66
  176. package/src/sandbox-kube.ts +63 -16
  177. package/src/secret-placement.ts +9 -2
  178. package/src/setup.ts +86 -6
  179. package/src/tokens.ts +107 -32
  180. package/src/vocabulary.ts +23 -4
  181. package/src/wire.ts +10 -10
  182. package/src/workspace.ts +264 -253
  183. package/src/worktree.ts +249 -0
  184. package/docs/reference/authoring-surface/type-noslotspec.md +0 -26
  185. package/docs/reference/authoring-surface/type-workspacespec.md +0 -37
@@ -18,13 +18,15 @@ credential that an [Effect](../reference/glossary.md#effect) uses, and only an
18
18
  ## Why it exists
19
19
 
20
20
  An Agent's [Working tools](../reference/glossary.md#working-tools) give it a shell in the Harness container. Thus the
21
- Agent can read each variable and each file of that container, and it can send them out of the pod. If a model API key
22
- were in that container, the Agent could take it away. If the credential of the Orchestrator's API were there, the Agent
23
- could answer its own review [Gate](../reference/glossary.md#gate).
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
24
 
25
- jr2 does not try to hide the API from the Agent. Instead, it keeps the credentials out of that container. The Instance
26
- token enters no Harness pod. Each credential of the `secrets` list, and the key of `harness.provider.apiKey`, goes to a
27
- container in which the Agent executes nothing. [Architecture](architecture.md) tells where those containers run.
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.
28
30
 
29
31
  ## One list, and the name decides
30
32
 
@@ -65,12 +67,14 @@ containers that hold it. An entry can also name a key of a Secret that you manag
65
67
 
66
68
  ## Held secrets
67
69
 
68
- The Harness container of a pod gets one variable for each Held secret of the pod. Its value is the Stand-in,
69
- `jr2-held-<NAME>`. The Stand-in is the same in each pod, and it is not secret. It has a meaning only to the Custodian of
70
- that pod.
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).
71
74
 
72
- When a pod holds a Held secret other than the Sandbox token, the Harness container sends its HTTPS traffic through the
73
- Custodian (`HTTPS_PROXY`). For each request to a host that a Held secret is bound to, the Custodian does these things:
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:
74
78
 
75
79
  1. It ends the TLS connection with a certificate that `jr2 up` issued from the CA of the
76
80
  [Instance](../reference/glossary.md#instance).
@@ -87,25 +91,96 @@ These fields of an entry bind a Held secret. They do not place it:
87
91
  - [`headers`](../reference/config.md#secrets-headers): the headers in which the Custodian replaces the Stand-in.
88
92
  - [`paths`](../reference/config.md#secrets-paths): the path prefixes that the value can go to.
89
93
 
90
- The Custodian also holds the Sandbox token of its pod, and the Harness container holds only its Stand-in. Each
91
- [Sandbox](../reference/glossary.md#sandbox) and the Instance Harness has a Sandbox token. The Harness reads the
92
- [Menu](../reference/glossary.md#menu) and sends each pick with the Stand-in of that token, and the Custodian replaces
93
- it.
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.
94
105
 
95
106
  ### What a Held secret does not stop
96
107
 
97
- - **The Agent can spend the key.** While the pod lives, the Agent can send requests through the Custodian to a bound
98
- host. A key that can make new keys gives the Agent a key that it can take away. Use a key with no admin rights. Set
99
- narrow `paths`.
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`.
100
111
  - **A response passes with no change.** If a host puts the full key in a response, the Agent reads it.
101
- - **jr2 does not control egress.** A client that does not use the Custodian sends only the Stand-in, which is worth
102
- nothing outside the pod. The cluster decides which hosts a pod can reach.
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).
103
119
  - **A key that signs the request cannot be a Held secret.** A SigV4 or ADC credential signs the request, and the
104
120
  Custodian cannot put a value into a signature. Use an Effect secret for such a credential. For a model provider, use a
105
121
  gateway that holds the credential, and make the key of the gateway a Held secret.
106
- - **A tool must trust the CA of the Instance.** When a pod holds a Held secret other than the Sandbox token, jr2 sets
107
- the trust variables of Node.js, OpenSSL, Python and git in its Harness container. A client that does not trust the CA,
108
- such as a Java client, cannot connect to a bound host. It sends nothing.
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).
109
184
 
110
185
  ## Effect secrets
111
186
 
@@ -125,8 +200,7 @@ encodings. It does not find a changed form of an Effect secret, such as a hash o
125
200
  ## Git credentials
126
201
 
127
202
  The [`git.credentials`](../reference/config.md#git-credentials) entries give the cluster its git credentials. The
128
- Orchestrator matches a [Repo](../reference/glossary.md#repo) to an entry, and names the Secret of that entry. It never
129
- reads a git credential:
203
+ Orchestrator matches a Repo to an entry, and names the Secret of that entry. It never reads a git credential:
130
204
 
131
205
  - The clone credential goes into a Secret that only the Repo cache reads.
132
206
  - The push credential and the signing key go to the [Sandbox Effector](../reference/glossary.md#sandbox-effector) of the
@@ -140,17 +214,19 @@ make the Sandbox. [Give the cluster git credentials](../guides/give-the-cluster-
140
214
  jr2 makes its own tokens. `jr2 up` makes the Instance token and the signing key the first time, and keeps them in the
141
215
  `jr2-instance` Secret. Each other token comes from the signing key, so the Orchestrator stores no token for each pod.
142
216
 
143
- | Token | Who holds it | What it lets the holder do |
144
- | -------------- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
145
- | 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. |
146
- | Sandbox token | The Custodian of one pod, as a file | Read the Menu and send the picks of the Agents in that pod. Ask for a fetch of a Repo that the Sandbox mounts. |
147
- | Harness bearer | The Orchestrator. The Harness holds only its SHA-256 digest. | Call the Harness in one pod: start a [Turn](../reference/glossary.md#turn), read it, stop it, set up a Worktree, and run a `command` Effect. |
148
- | Effector key | The Orchestrator, and the Effector of one placement | Sign a request to that Effector, and sign its answer. |
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. |
149
224
 
150
225
  - The Instance token enters no Harness pod. The CLI reads it from the `jr2-instance` Secret over the Kubernetes API.
151
226
  Thus Kubernetes RBAC decides who holds it.
152
- - A Sandbox token cannot answer a Gate and cannot speak for a different pod. Thus an Agent cannot approve its own work,
153
- and a Sandbox cannot send a pick for an Agent in a different Sandbox.
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.
154
230
  - A Harness can check its bearer, but it cannot make a bearer. Thus an Agent cannot drive a conversation in a different
155
231
  pod.
156
232
 
@@ -161,8 +237,8 @@ Each route of the [HTTP API](../reference/http-api.md) is in one of three access
161
237
  - [**Open**](../reference/http-api.md#band-open), with no token: the [Workflows](../reference/glossary.md#workflow), the
162
238
  structure of each Machine, and the live runs of a Workflow with their states. An open route shows no context of a run,
163
239
  and an [Emit](../reference/glossary.md#emit) shows only its type.
164
- - [**Authenticated**](../reference/http-api.md#band-authenticated), with the Instance token or a Sandbox token: the
165
- routes of the Agent's Menu and the fetch. A Sandbox token reaches only what its own pod owns.
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.
166
242
  - [**Instance token only**](../reference/http-api.md#band-instanceonly): the start of a run, the full state of a run,
167
243
  the Gates, and cancel.
168
244
 
@@ -185,6 +261,9 @@ who can read the pod logs of the namespace can read these conversations.
185
261
  not `https://`. A local model over plain `http://` has no key.
186
262
  - [`harness.env`](../reference/config.md#harness-env) and `harness.envFrom` put each value into the Harness container,
187
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.
188
267
  - A Held secret must name its `hosts`, unless it is a known model key with one default host.
189
268
  - A name that a `workspace()` lists must be in the `secrets` list. A name that an Effect gives to `secret()` must be in
190
269
  it too.
@@ -193,11 +272,14 @@ who can read the pod logs of the namespace can read these conversations.
193
272
  - The Orchestrator can make and change a Secret, but it cannot get or list one.
194
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
195
274
  `jr2 up` restarts the Instance Harness with it.
196
- - A `command` that names an Effect secret needs Landlock in the kernel of the node. On a node without it, the Effector
197
- does not start, and the provision of the Sandbox fails.
198
- - When a pod holds a Held secret other than the Sandbox token, `SSL_CERT_FILE` and the other trust variables replace the
199
- trust store of the image. They hold the public roots, `harness.caBundle` and the CA of the Instance. Thus a private CA
200
- goes in [`harness.caBundle`](../reference/config.md#harness-cabundle), not in the image.
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.
201
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
202
284
  Secret. When you delete it, the next `jr2 up` makes a new CA.
203
285
 
@@ -218,5 +300,7 @@ who can read the pod logs of the namespace can read these conversations.
218
300
  [ADR-0032](../reference/decisions/0032-the-console-unlocks-with-the-instance-token.md),
219
301
  [ADR-0051](../reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md),
220
302
  [ADR-0058](../reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md),
221
- [ADR-0059](../reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md) and
222
- [ADR-0068](../reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.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)
@@ -352,6 +352,9 @@ transition refuse other senders. It never gives an event to a party. The positio
352
352
  body that has one.
353
353
  - `createMachine` refuses an `on` key that no listed event has, and a listed event that no state takes.
354
354
  - `createMachine` refuses a parallel state at or below a state that invokes an Agent.
355
+ - `createMachine` refuses one Agent slot in two states that can be active together: two parallel regions, one state
356
+ inside the other, or one state that invokes the slot twice. The two Turns would have one sender and one conversation.
357
+ For two Agents at once, use two slots (one definition can fill both), a child Machine in each region, or a Pool.
355
358
  - `createMachine` refuses a `sentBy` that names no Agent slot and no Gate invoke id of the Machine.
356
359
  - `jr2 up` warns about a mode that no transition enters. Its picks are on the Menu, but never in the Allowed picks.
357
360
  - `jr2 up` warns about a Gate below an Agent's state whose event has no target. The Gate stays open after that event, so