@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.
- package/docs/agents-block.md +2 -2
- package/docs/concepts/agents-and-turns.md +56 -20
- package/docs/concepts/architecture.md +30 -17
- package/docs/concepts/charts.md +6 -3
- package/docs/concepts/converge.md +3 -0
- package/docs/concepts/effects.md +86 -31
- package/docs/concepts/machines-and-workflows.md +9 -0
- package/docs/concepts/pools-and-sources.md +6 -6
- package/docs/concepts/secrets-and-trust.md +126 -42
- package/docs/concepts/the-menu.md +3 -0
- package/docs/concepts/workspaces.md +129 -51
- package/docs/get-started/an-agent-in-a-sandbox.md +21 -19
- package/docs/get-started/have-your-assistant-write-a-machine.md +111 -40
- package/docs/get-started/install.md +4 -0
- package/docs/guides/add-tools-to-the-sandbox.md +17 -4
- package/docs/guides/bind-open-parts.md +5 -4
- package/docs/guides/cut-a-worktree.md +560 -0
- package/docs/guides/fix-a-failed-converge.md +1 -1
- package/docs/guides/let-an-agent-write-a-chart.md +5 -1
- package/docs/guides/look-inside-a-sandbox.md +18 -15
- package/docs/guides/move-from-kind-to-a-shared-cluster.md +16 -0
- package/docs/guides/push-and-sign-a-branch.md +67 -21
- package/docs/guides/run-a-command-or-call-a-url.md +84 -41
- package/docs/guides/run-one-worker-per-item.md +19 -18
- package/docs/guides/set-an-agents-access-and-dials.md +67 -22
- package/docs/guides/size-a-workspace.md +19 -6
- package/docs/guides/use-a-hosted-provider.md +6 -0
- package/docs/guides/watch-a-run.md +47 -24
- package/docs/guides/write-a-workflow.md +1 -1
- package/docs/llms.txt +33 -12
- package/docs/reference/authoring-surface/agent.md +34 -11
- package/docs/reference/authoring-surface/answer.md +19 -3
- package/docs/reference/authoring-surface/arg.md +27 -5
- package/docs/reference/authoring-surface/branchname.md +15 -0
- package/docs/reference/authoring-surface/command.md +36 -21
- package/docs/reference/authoring-surface/fail.md +17 -4
- package/docs/reference/authoring-surface/file.md +36 -16
- package/docs/reference/authoring-surface/http.md +108 -8
- package/docs/reference/authoring-surface/isready.md +14 -0
- package/docs/reference/authoring-surface/jr2setup.md +1 -1
- package/docs/reference/authoring-surface/open.md +36 -5
- package/docs/reference/authoring-surface/paramsof.md +22 -4
- package/docs/reference/authoring-surface/pool.md +4 -4
- package/docs/reference/authoring-surface/push.md +30 -12
- package/docs/reference/authoring-surface/readyof.md +21 -0
- package/docs/reference/authoring-surface/secret.md +25 -4
- package/docs/reference/authoring-surface/source.md +3 -3
- package/docs/reference/authoring-surface/type-agentdefinition.md +2 -2
- package/docs/reference/authoring-surface/type-agentslots.md +4 -1
- package/docs/reference/authoring-surface/type-anyframepath.md +14 -0
- package/docs/reference/authoring-surface/type-anyworktreepath.md +15 -0
- package/docs/reference/authoring-surface/type-bindactors.md +14 -0
- package/docs/reference/authoring-surface/type-binding.md +5 -6
- package/docs/reference/authoring-surface/type-commandinput.md +4 -3
- package/docs/reference/authoring-surface/type-commandoptions.md +0 -22
- package/docs/reference/authoring-surface/type-commandoutput.md +22 -4
- package/docs/reference/authoring-surface/type-customize.md +4 -2
- package/docs/reference/authoring-surface/type-dispatchoutput.md +8 -1
- package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
- package/docs/reference/authoring-surface/type-filebody.md +4 -8
- package/docs/reference/authoring-surface/type-framepath.md +14 -0
- package/docs/reference/authoring-surface/type-hostinjectedinput.md +2 -4
- package/docs/reference/authoring-surface/type-httpdefinition.md +6 -3
- package/docs/reference/authoring-surface/type-httpfileinput.md +20 -0
- package/docs/reference/authoring-surface/type-httpoutput.md +7 -1
- package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
- package/docs/reference/authoring-surface/type-namestart.md +77 -0
- package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +1 -6
- package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -0
- package/docs/reference/authoring-surface/type-opencommandinput.md +7 -2
- package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +1 -6
- package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +5 -10
- package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
- package/docs/reference/authoring-surface/type-poolspec.md +2 -2
- package/docs/reference/authoring-surface/type-pushinput.md +22 -3
- package/docs/reference/authoring-surface/type-pushoutput.md +32 -7
- package/docs/reference/authoring-surface/type-reposlot.md +5 -5
- package/docs/reference/authoring-surface/type-sandboxoptions.md +11 -4
- package/docs/reference/authoring-surface/type-slothandles.md +5 -13
- package/docs/reference/authoring-surface/type-sourcespec.md +10 -10
- package/docs/reference/authoring-surface/type-workspaced.md +29 -8
- package/docs/reference/authoring-surface/type-workspacehandles.md +5 -6
- package/docs/reference/authoring-surface/type-workspaceoptions.md +1 -13
- package/docs/reference/authoring-surface/type-worktreefound.md +16 -0
- package/docs/reference/authoring-surface/type-worktreeinput.md +56 -0
- package/docs/reference/authoring-surface/type-worktreekey.md +14 -0
- package/docs/reference/authoring-surface/type-worktreeoptions.md +29 -0
- package/docs/reference/authoring-surface/type-worktreeoutput.md +45 -0
- package/docs/reference/authoring-surface/type-worktreepath.md +16 -0
- package/docs/reference/authoring-surface/type-worktreeready.md +37 -0
- package/docs/reference/authoring-surface/type-worktreerefused.md +21 -0
- package/docs/reference/authoring-surface/workspace.md +65 -37
- package/docs/reference/authoring-surface/worktree.md +71 -0
- package/docs/reference/authoring-surface.md +30 -10
- package/docs/reference/cli.md +1 -1
- package/docs/reference/config.md +13 -4
- package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +10 -9
- package/docs/reference/decisions/0002-duplex-streaming-actor.md +17 -10
- package/docs/reference/decisions/0004-worktree-provisioning-model.md +27 -21
- package/docs/reference/decisions/0005-sandbox-pod-composition.md +29 -19
- package/docs/reference/decisions/0006-agent-control-surface.md +6 -3
- package/docs/reference/decisions/0012-workspace-wrapper-machine.md +30 -30
- package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +35 -17
- package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +8 -2
- package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +9 -7
- package/docs/reference/decisions/0017-pool-over-a-source-port.md +6 -4
- package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +21 -9
- package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +2 -1
- package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +8 -9
- package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +62 -65
- package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +9 -1
- package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +13 -12
- package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +4 -1
- package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +18 -19
- package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +8 -8
- package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +4 -4
- package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +57 -46
- package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +5 -0
- package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +20 -8
- package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +24 -24
- package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +5 -3
- package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +110 -67
- package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +101 -74
- package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +5 -6
- 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
- package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +181 -153
- package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +74 -63
- package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +31 -20
- package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +1 -1
- package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +295 -0
- package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +320 -0
- package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
- package/docs/reference/decisions.md +3 -0
- package/docs/reference/glossary.md +51 -12
- package/docs/reference/http-api.md +45 -7
- package/docs/reference/machines.md +0 -1
- package/docs/why.md +5 -4
- package/package.json +3 -3
- package/src/actor.ts +161 -35
- package/src/agent.ts +6 -6
- package/src/ambient.ts +3 -5
- package/src/chart.ts +186 -19
- package/src/charts.ts +9 -2
- package/src/config.ts +16 -6
- package/src/custodian.lua +17 -5
- package/src/custodian.ts +251 -77
- package/src/customize.ts +11 -3
- package/src/effect-checks.ts +50 -3
- package/src/effect.ts +343 -127
- package/src/effector-wire.ts +6 -3
- package/src/fail.ts +17 -4
- package/src/harness-client.ts +74 -31
- package/src/harness-env.ts +33 -0
- package/src/held-secrets.ts +125 -16
- package/src/holds.ts +3 -0
- package/src/http-effect.ts +88 -32
- package/src/http.ts +56 -17
- package/src/index.ts +15 -1
- package/src/internal.ts +1 -0
- package/src/kube-client.ts +6 -3
- package/src/machine-doc.ts +3 -4
- package/src/model-keys.ts +24 -0
- package/src/names.ts +19 -6
- package/src/notices.ts +443 -54
- package/src/open-body.ts +85 -19
- package/src/open.ts +36 -5
- package/src/parts.ts +40 -28
- package/src/party.ts +33 -0
- package/src/pick.ts +41 -7
- package/src/pool.ts +44 -23
- package/src/registration.ts +53 -11
- package/src/repo-identity.ts +7 -3
- package/src/repos.ts +20 -2
- package/src/run-host.ts +106 -20
- package/src/sandbox-effect.ts +217 -66
- package/src/sandbox-kube.ts +63 -16
- package/src/secret-placement.ts +9 -2
- package/src/setup.ts +86 -6
- package/src/tokens.ts +107 -32
- package/src/vocabulary.ts +23 -4
- package/src/wire.ts +10 -10
- package/src/workspace.ts +264 -253
- package/src/worktree.ts +249 -0
- package/docs/reference/authoring-surface/type-noslotspec.md +0 -26
- package/docs/reference/authoring-surface/type-workspacespec.md +0 -37
package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md
CHANGED
|
@@ -53,16 +53,20 @@ only asks. Its Role reads no Secret either: it may create and patch one, never g
|
|
|
53
53
|
token being an Effect secret it holds. A Sandbox Effector — the binary with `JR2_EFFECTOR_WORK` set, a read-only view
|
|
54
54
|
of the Agent's `/work` — runs an `http` whose body is a Worktree file, `push` and a credentialed `command` (ADR-0067);
|
|
55
55
|
the Instance Effector refuses the last two, and a file body, as a defect, and a Sandbox Effector refuses a `dispatch`.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
56
|
+
Under Repo Slots each of the three needs a cut's settled row in the run's ledger, because a path alone does not name
|
|
57
|
+
the target of a credential: the Orchestrator reads the row before it starts the Effect, and with no row it sends
|
|
58
|
+
nothing and settles the kind's own outcome
|
|
59
|
+
([ADR-0075](0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md)). Its env names where its mount holds the
|
|
60
|
+
rest: `JR2_EFFECTOR_REPOS` (the node's Repo caches, read-only), `JR2_EFFECTOR_GIT` (the push credentials: one
|
|
61
|
+
directory per `git.credentials` entry the Sandbox's pushes spend, the entry's own Secret as the operator projects it —
|
|
62
|
+
`entry.json`, what the entry is; `identity` and `known_hosts` for an ssh key; `password` for a token; `sign.identity`
|
|
63
|
+
or `sign.private.asc` for the signing key; see the Sandbox Effector's objects below), `JR2_EFFECTOR_SCRATCH` (its own
|
|
64
|
+
writable scratch). What jr2 guarantees is stated per kind: for `push` and a file body, git never takes config or hooks
|
|
65
|
+
from the Agent's repository and no program the Agent's files name runs — the clean-repository procedure of ADR-0067
|
|
66
|
+
and the opener below. jr2's own programs (`git`, `ssh`, `ssh-keygen`, `gpg`, `gpg-agent`) run by an absolute path
|
|
67
|
+
found in one fixed PATH, `/usr/local/bin:/usr/bin:/bin`, never by the PATH the image set: an image that puts a
|
|
68
|
+
directory of its own first does not choose which `git` runs. For a credentialed `command` the Agent's files are the
|
|
69
|
+
author's choice to run, and the claims are the next bullet's.
|
|
66
70
|
- **A credentialed `command`'s child is confined with Landlock.** The child runs as the Effector's own uid in the
|
|
67
71
|
Effector's container, where the placement key, every placed secret and the push and signing keys are files that uid
|
|
68
72
|
can read, and its program is often the Agent's code (`npm run deploy`, `make`, `./scripts/release.sh`, a tool's
|
|
@@ -74,28 +78,34 @@ only asks. Its Role reads no Secret either: it may create and patch one, never g
|
|
|
74
78
|
The thread is locked from the probe to the `execve`, so the domain is the one `execve` keeps. Landlock is an
|
|
75
79
|
allow-list the kernel enforces and every descendant inherits, and a confined process cannot ptrace a process outside
|
|
76
80
|
its domain, so `/proc/<effector>/mem` and `/proc/<effector>/environ` are closed too. The ruleset handles every
|
|
77
|
-
filesystem right of ABI 1,
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
`/
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
81
|
+
filesystem right of ABI 1, `REFER` (ABI 2) and `TRUNCATE` (ABI 3). At ABI 6 it also sets the scopes of abstract Unix
|
|
82
|
+
sockets and of signals: the child cannot connect to an abstract socket that a process outside its domain made, or
|
|
83
|
+
signal such a process. It allows: read and execute on the image's system paths — every top-level directory the image
|
|
84
|
+
holds that is not a denied name, whole, so the rules follow the image and break on no layout (the usual `/usr`,
|
|
85
|
+
`/bin`, `/etc`, `/opt`, and a tool tree of the image's own such as `/google-cloud-sdk` or `/nix` alike; a link such as
|
|
86
|
+
`/lib -> usr/lib` gets no rule, and works through the rule on what it names, because the kernel checks the path that a
|
|
87
|
+
link resolves to; the confiner opens each rule's path with no link on it, and refuses to start when one is there);
|
|
88
|
+
read and write on the devices (`/dev/null`, `/dev/zero`, `/dev/full`, `/dev/random`, `/dev/urandom`, `/dev/tty`) and
|
|
89
|
+
the Effect's own scratch, one per Effect (the checkout, `$HOME` and `$TMPDIR`); read on each secret file the command
|
|
90
|
+
names, by `secret("x")` and `.path` alike. The denied names — the kernel and process pseudo-filesystems (`/proc`,
|
|
91
|
+
`/sys`, the rest of `/dev`), the runtime tree where jr2 mounts the Effector's own files (`/run`, the key and config
|
|
92
|
+
under `/run/jr2/effector`), and the volatile and per-user trees that hold no tool an image ships (`/home`, `/root`,
|
|
93
|
+
`/var`, `/srv`, `/mnt`, `/media`, `/lost+found`) — are denied, and so is every path named in the Effector's own deny
|
|
94
|
+
list: the secrets the command does not name, the push credentials (`@git`), the Worktree (`/work`), the node caches
|
|
95
|
+
(`/repos`), the rest of its scratch (another Effect's, and a push's copied keys). Every jr2 mount in the Effector's
|
|
96
|
+
container that holds a secret or the Agent's files is under a denied name (`/run`: the key, the config, the secrets,
|
|
97
|
+
`@git`) or in that deny list (`/work`, `/repos`, the scratch). The two others hold only public data and the child may
|
|
98
|
+
read them: the runtime volume at `/opt/jr2`, which holds the programs jr2 ships, and the instance's caBundle ConfigMap
|
|
99
|
+
at `/etc/jr2/effector-ca` (ADR-0020). So a jr2 mount that holds a secret goes under `/run/jr2`; one under `/etc`,
|
|
100
|
+
`/opt` or a new top-level name is readable by the child. A top-level directory that holds one of the Effector's own
|
|
101
|
+
paths is allowed one level down at a time around it, so a mount placed inside one stays denied. Network is not
|
|
102
|
+
restricted. Any failure before `execve` ends the confiner with exit 126 and its reason on stderr: nothing runs
|
|
103
|
+
unconfined. The design needs ABI 3, the minimum of jr2 (ADR-0077); measured from a hardened non-root pod (every
|
|
97
104
|
capability dropped, `RuntimeDefault` seccomp): kind on WSL2 6.6 — ABI 3; the home cluster's arm64 nodes (6.8) — ABI 4;
|
|
98
|
-
spark (6.11, arm64) — ABI 5.
|
|
105
|
+
spark (6.11, arm64) — ABI 5. Landlock is a requirement of jr2
|
|
106
|
+
([ADR-0077](0077-jr2-requires-landlock-on-every-sandbox-node.md)). The confiner is shared: the Harness runs each
|
|
107
|
+
process it starts in `/work` through the same program, with its own rules
|
|
108
|
+
([ADR-0076](0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md)).
|
|
99
109
|
- **One child cannot reach another.** Three rules, in the Effector:
|
|
100
110
|
1. **The subreaper.** The Effector sets `PR_SET_CHILD_SUBREAPER` when it starts, so a descendant whose parent ends is
|
|
101
111
|
reparented to the Effector, never to the container's init. Every child an Effect starts runs in its own session and
|
|
@@ -137,16 +147,16 @@ only asks. Its Role reads no Secret either: it may create and patch one, never g
|
|
|
137
147
|
3. **A private TMPDIR.** Each credentialed child gets `TMPDIR` set to a directory of its Effect's own scratch, which
|
|
138
148
|
goes when the outcome is read, and `/tmp` is not in the ruleset: a child neither reads nor writes it, so what one
|
|
139
149
|
child writes no other child reads.
|
|
140
|
-
- **Fail-closed at the pod
|
|
141
|
-
|
|
142
|
-
(`
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
- **Fail-closed at `jr2 up` and at the pod.** Landlock is a requirement of jr2 (ADR-0077): `jr2 up` refuses to converge
|
|
151
|
+
while a Sandbox node lacks it, as the Repo cache agent on that node reports, and a Sandbox Effector probes it when it
|
|
152
|
+
starts (`landlock_create_ruleset` with `LANDLOCK_CREATE_RULESET_VERSION`), whatever its Workspace holds. The Instance
|
|
153
|
+
Effector confines nothing and does not probe. When the probe fails, the Sandbox Effector does not become ready: it
|
|
154
|
+
ends, and its termination message — written to `/dev/termination-log`, the container's `FallbackToLogsOnError` the
|
|
155
|
+
fallback — says `landlock: unavailable on node <node> (<why>)`, with the node from the downward API (`NODE_NAME`). The
|
|
156
|
+
operator copies the Sandbox Effector container's restarts and last end onto the Sandbox's status (`status.effector`),
|
|
157
|
+
and the provision fails on an Effector that ended before the pod was Ready, in those words, before any Turn. The pod's
|
|
158
|
+
probe is the backstop for a node that joins after the converge. A Node's `kernelVersion` does not say whether
|
|
159
|
+
`landlock` is in its LSM list, so only a probe on the node can tell.
|
|
150
160
|
- **The wire.** `POST /effects/<id>` starts an Effect and answers at once (202, `state: running`). A second `POST` with
|
|
151
161
|
the same id starts nothing and attaches (200). The answer to a `push` start names the commit it runs against
|
|
152
162
|
(`pinned`), read before the answer, so the Orchestrator writes it to the ledger's intent row. `GET /effects/<id>`
|
|
@@ -274,8 +284,8 @@ only asks. Its Role reads no Secret either: it may create and patch one, never g
|
|
|
274
284
|
- `unreachable`: nothing was sent — the name did not resolve, the connection or TLS failed.
|
|
275
285
|
- `refused`: the Effector refused it and sent nothing — a path segment that is `.`, `..` or empty (an Agent's param
|
|
276
286
|
can cause it, so it is output, not a fault), or a host and secrets that no Effect placed there names. The
|
|
277
|
-
Orchestrator settles an `http` start longer than 4 MiB `refused` the same way, before it sends anything
|
|
278
|
-
|
|
287
|
+
Orchestrator settles an `http` start longer than 4 MiB `refused` the same way, before it sends anything, and a file
|
|
288
|
+
body with no cut's row (ADR-0075); a `push` and a `command` have no `refused`.
|
|
279
289
|
- `unknown`: the connection was made and no answer came (the timeout, a reset): the host may have acted.
|
|
280
290
|
|
|
281
291
|
Each outcome other than `response` carries a `reason` in words. A pick that runs an `http` Effect is answered, by
|
|
@@ -402,36 +412,37 @@ only asks. Its Role reads no Secret either: it may create and patch one, never g
|
|
|
402
412
|
echo, a side channel. The claim is that an Effector does not return a secret it holds, in the forms it sent or in
|
|
403
413
|
common encodings.
|
|
404
414
|
- **What a credentialed command's child can still do.** It reads the secret the command names — the author put it there,
|
|
405
|
-
and a file the Agent wrote that the argv runs reads it too — and egress is open
|
|
406
|
-
ADR-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
415
|
+
and a file the Agent wrote that the argv runs reads it too — and egress is open but for the cloud metadata addresses
|
|
416
|
+
([ADR-0076](0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md)), so the claim is the three
|
|
417
|
+
of ADR-0067: the Agent never reads or spends the secret from its own container, the tool sees a pinned commit and not
|
|
418
|
+
the live Worktree, and the output is scrubbed. It shares the Effector's uid, so it can signal the Effector (a fault of
|
|
419
|
+
its own run, not a leak) and connect to its port, where nothing without the key is accepted. It cannot outlive its
|
|
420
|
+
Effect (rule 1), share a temporary tree with another child (rule 3), or be alive beside a `gpg-agent` (rule 2).
|
|
410
421
|
- **A long GPG push delays a concurrent credentialed command** by its duration, against that command's timeout, and the
|
|
411
422
|
reverse. Rejected: refusing a credentialed command beside a GPG-signing push at `jr2 up` — the hole is open only while
|
|
412
423
|
a GPG push signs, so the rule is that the two never overlap; and a second container per credentialed placement — its
|
|
413
424
|
child would still share a uid with that container's Effector and could spend that container's key, so it would need
|
|
414
425
|
Landlock anyway, plus a key, a Secret, a port and a NetworkPolicy per placement.
|
|
415
|
-
- **A node with no Landlock runs no
|
|
416
|
-
|
|
417
|
-
|
|
426
|
+
- **A node with no Landlock runs no Sandbox** (ADR-0077). A Sandbox placed there fails at provision, naming the node;
|
|
427
|
+
the fix is a kernel with `landlock` in its LSM list. The home cluster's nodes are arm64, so the Effector binary and
|
|
428
|
+
the Kit images need `linux/arm64` builds there.
|
|
418
429
|
- An `unknown` outcome is the Machine's to route. jr2 sends an `http` call again only when the author marked it
|
|
419
430
|
`idempotent` ([ADR-0067](0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md)).
|
|
420
431
|
- The Effector's Go tests run Landlock for real on the host's kernel: a child that reads the key, an unnamed secret or
|
|
421
432
|
`@git`, or writes the Worktree, ends with a permission error and an answer with no secret in it; one that reads its
|
|
422
433
|
named secret from env and from its file succeeds; a child of the child is confined too; a failing probe stops the
|
|
423
|
-
start of
|
|
424
|
-
|
|
425
|
-
command
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
434
|
+
start of the Effector, with the node in the reason. And the three rules of "One child cannot reach another" run with
|
|
435
|
+
real processes: a `setsid` escapee, and an escapee of that escapee, is dead once its command has settled; a live
|
|
436
|
+
command's daemon, under its keeper, lives through the settle of another Effect beside it; the keeper ends what its
|
|
437
|
+
child left and answers the child's own end; a command started during a GPG push runs only after the agent is gone, and
|
|
438
|
+
the reverse, with no sample of that Effector's processes seeing both alive — the command's child matched to its
|
|
439
|
+
scratch, and the agent known from a witness in place of `gpg-agent` that tells the test before the launcher runs and,
|
|
440
|
+
after it exits, which processes the agent left, so an agent that lives less than one sample is never missed; a waiter
|
|
441
|
+
past its timeout settles timed out and never runs late; a child's `TMPDIR` write is invisible to the next child, and
|
|
442
|
+
`/tmp` refuses both; a signed DELETE ends a credentialed command's group, `exited` with SIGTERM, a program that
|
|
443
|
+
catches SIGTERM gives its code beside the signal, a child that ignores SIGTERM is killed past the grace (`exited` with
|
|
444
|
+
SIGKILL), an unsigned DELETE stops nothing, a DELETE on a push or an `http` path answers 405, and a command start that
|
|
445
|
+
arrives after a DELETE of its id runs nothing, while a push or an `http` start does.
|
|
435
446
|
- The `@kind` tier carries the proofs a unit test cannot: an echo server in the seed namespace answers each header and
|
|
436
447
|
the url in every encoding, and the run's snapshot, its feed and the answer the model reads must hold no form of the
|
|
437
448
|
value — the step decodes every base64 and percent-encoded run it reads, so a leak of what the Effector built
|
|
@@ -60,8 +60,10 @@ their own maintainers, and the escapes shipped in systems that used them were in
|
|
|
60
60
|
`gate`, `fail`, a generic action's name or an integer, because those are the kit's; the Gate itself is lent as
|
|
61
61
|
`gate: true`, and the Part that fails the run as `fail: true`
|
|
62
62
|
([ADR-0073](0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md)), because the Machine
|
|
63
|
-
decides which ends a Chart can choose. The
|
|
64
|
-
|
|
63
|
+
decides which ends a Chart can choose. The guards `jr2.failure`, which tells a Failure from a defect on an `onError`,
|
|
64
|
+
and `jr2.ready`, which says that a cut settled `ready`
|
|
65
|
+
([ADR-0075](0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md)), are in every Chart, as `jr2.sentBy` is:
|
|
66
|
+
each is a predicate and gives no capability.
|
|
65
67
|
- **Constants for authority, references for data.** `invoke.src`, guard and action `type`s, delay names and event names
|
|
66
68
|
are constants, checked against the lent set and the Chart's own declarations at admission. Data flows through
|
|
67
69
|
references: a placeholder `{{context.ticket.title}}`, or `{ "$ref": "event.summary" }`, in an invoke's `input`, an
|
|
@@ -130,11 +132,14 @@ their own maintainers, and the escapes shipped in systems that used them were in
|
|
|
130
132
|
briefing names the lent Agents, and a body that allows no model has NO `agents` in its grammar and a briefing that
|
|
131
133
|
says so: a planner reads a field it cannot fill as one to fill (measured: eight of ten first Charts minted under a
|
|
132
134
|
`never` model, and read "must not have additional properties" for it), and a Chart that brings `agents` anyway reads
|
|
133
|
-
at the door why there is none.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
Chart
|
|
135
|
+
at the door why there is none. The briefing of a body that lends an Effect says how a Chart works under Repo Slots:
|
|
136
|
+
work happens in a Worktree; a lent cut takes a `path` of `/work/<slot>/<name>` and a `branch`, and `<name>` is not the
|
|
137
|
+
branch; its `onDone` has an edge with the guard `jr2.ready` and an edge with no guard, for a refused cut; and that one
|
|
138
|
+
literal path is the `cwd` of each Turn and the `worktree` of each Effect there. And the Agent's surface MARKS a pick
|
|
139
|
+
whose parameter is a Chart with that grammar's briefing (`SurfaceEvent.briefing`, by schema identity), so the Harness
|
|
140
|
+
says the text in the Turn part, right before "Allowed now", on every Turn where that pick is Allowed, and on a
|
|
141
|
+
restatement that opens it ([ADR-0062](0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md)). Nowhere else: the
|
|
142
|
+
standing Briefing stays Chart-agnostic, the tools block stays the same bytes on every Turn
|
|
138
143
|
([ADR-0029](0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md)), and a seat whose Menu
|
|
139
144
|
holds no Chart pick reads not a word of it. The cost is the author's to scope: a seat whose Menu holds a Chart pick
|
|
140
145
|
carries the grammar as cached tool parameters on every Turn, so an author who wants a planner that only sometimes
|
|
@@ -206,13 +211,14 @@ their own maintainers, and the escapes shipped in systems that used them were in
|
|
|
206
211
|
above. The minted Agents: every model against the allowed list and the placed keys, every Workspace access against the
|
|
207
212
|
allowed set. The structure: no `always` cycle without a guard, through parallel regions and history too; no `onDone`
|
|
208
213
|
on an Agent invoke, because a Turn never settles by itself, it ends on a pick in `on`; no Agent state that nothing can
|
|
209
|
-
end; no parallel state at or below an Agent's
|
|
210
|
-
|
|
211
|
-
`agent.fault` that no
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
214
|
+
end; no parallel state at or below an Agent's; no Agent in two states that can be active together. An Agent state of a
|
|
215
|
+
Chart needs no `agent.fault` route. A state of a Machine needs one
|
|
216
|
+
([ADR-0074](0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md)). An `agent.fault` that no
|
|
217
|
+
transition of the Chart takes goes, unchanged, to the Machine that invoked the Open body, and that Machine's
|
|
218
|
+
`agent.fault` route takes it, as it takes the fault of an Agent of its own. So a Machine that invokes an Open body
|
|
219
|
+
that can run an Agent needs the route too, on that state or above it. A route in the Chart stays legal, and it can
|
|
220
|
+
read `{{event.reason}}`. The wrapper is the right place: a Chart under a body that lends no `fail` has no correct end
|
|
221
|
+
for a fault, because a final state means `done`
|
|
216
222
|
([ADR-0073](0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md)) and a state that waits
|
|
217
223
|
holds the run until the wall clock. So the briefing does not teach the route, and the grammar's fragment does not show
|
|
218
224
|
it. And two checks that stand in for the types a Chart does not have. **Dataflow**: every reference a Chart makes,
|
|
@@ -224,12 +230,17 @@ their own maintainers, and the escapes shipped in systems that used them were in
|
|
|
224
230
|
is refused. The fields a Chart declares are its types. **Lent input shapes**: an Effect carries an `input` schema and
|
|
225
231
|
an Agent's Turn input is a fixed key set (`prompt`, `cwd`, `continue`, `model`, `thinkingLevel`, no placement), so the
|
|
226
232
|
literal parts of a Chart's `input` objects are validated at the door and the resolved object again at invoke time,
|
|
227
|
-
where a failure is that invoke's error and never a mid-Turn surprise.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
`
|
|
232
|
-
|
|
233
|
+
where a failure is that invoke's error and never a mid-Turn surprise. Then the rules of a Worktree (ADR-0075). Under
|
|
234
|
+
Repo Slots, a Turn's `cwd` and an Effect's `worktree` are required, and the door refuses a `cwd` that is a slot's
|
|
235
|
+
folder. A cut lent to a Chart must state `expect`, which is static, so a Chart cannot change it. The door refuses
|
|
236
|
+
`expect: "existing"` beside a lent force push, because together they let a Chart force-push any existing branch that
|
|
237
|
+
`default/` does not hold. A Chart's payload has no `pattern`, so a bad branch name reaches the cut, and the cut
|
|
238
|
+
refuses it. Last, the door compiles the Chart as the body would: `jr2Setup`'s own refusals (less its `agent.fault`
|
|
239
|
+
refusal), and the load warnings a hand-written Machine gets (an unentered mode), are refusal lines for a Chart,
|
|
240
|
+
because a planner reads a report and nobody reads a warning. A refusal is the body's OUTPUT,
|
|
241
|
+
`{ outcome: "refused", report }`, routed by the wrapper's `onDone` with a guard on `outcome`, so a wrapper whose
|
|
242
|
+
`onDone` returns to the planning state hands it to the planner as the output link in its Chain
|
|
243
|
+
([ADR-0067](0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md)): "Chart refused:
|
|
233
244
|
`src: "linter"` is not a lent actor; lent: coder, reviewer, push", and the planner answers with a corrected Chart. It
|
|
234
245
|
is not the body's error, because a child's error is a defect that never reaches the model (ADR-0067, which stands); at
|
|
235
246
|
the HTTP door it is 422 with the same report. No new mechanism: an Agent reads its children's outputs through the
|
|
@@ -39,7 +39,7 @@ comment "the run faults rather than lie about a decision", and that run ended `d
|
|
|
39
39
|
the run holds then, and nor does a Workspace that was stopped in `placing`
|
|
40
40
|
([ADR-0064](0064-a-workspace-waits-for-capacity.md)): a parent that invokes the same Machine again gives the new
|
|
41
41
|
Workspace the same invoke id, and so the same Sandbox name. That new Workspace takes over the Sandbox and its
|
|
42
|
-
|
|
42
|
+
Worktrees, as a restore does, with the work of the failed attempt still in it. To keep the work for a person to read,
|
|
43
43
|
the Machine waits at a Gate before it fails.
|
|
44
44
|
- **`jr2 run` exits 0 only for `done`.** It exits 1 for every other end: `failed`, `error`, `cancelled`, `lost`,
|
|
45
45
|
`drifted`, and a run that was parked before it settled. Exit code 2 stays the CLI's code for a usage error. The status
|