@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.
- package/console/components/app.ts +55 -15
- package/console/components/bottom-drawer.ts +259 -0
- package/console/components/drawer.ts +14 -6
- package/console/components/fleet.ts +36 -13
- package/console/components/machine-pane.ts +61 -5
- package/console/components/nav.ts +54 -8
- package/console/conversation.ts +161 -0
- package/console/login.ts +123 -0
- package/console/main.ts +600 -60
- package/console/store.ts +611 -64
- package/console/style.css +231 -0
- package/docs/agents-block.md +2 -2
- package/docs/concepts/agents-and-turns.md +142 -29
- package/docs/concepts/architecture.md +60 -34
- package/docs/concepts/callers-and-hooks.md +205 -0
- package/docs/concepts/charts.md +13 -8
- package/docs/concepts/composition.md +10 -1
- package/docs/concepts/converge.md +7 -2
- package/docs/concepts/durable-runs.md +43 -16
- package/docs/concepts/effects.md +133 -50
- package/docs/concepts/gates.md +95 -25
- package/docs/concepts/machines-and-workflows.md +14 -4
- package/docs/concepts/pools-and-sources.md +193 -17
- package/docs/concepts/secrets-and-trust.md +186 -62
- package/docs/concepts/the-menu.md +14 -0
- package/docs/concepts/workspaces.md +135 -56
- package/docs/get-started/a-model-decides.md +14 -13
- package/docs/get-started/an-agent-in-a-sandbox.md +46 -35
- package/docs/get-started/connect-a-model.md +1 -1
- package/docs/get-started/create-an-instance.md +17 -14
- 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 +72 -15
- package/docs/guides/answer-a-gate-from-a-forge.md +304 -0
- package/docs/guides/answer-a-gate.md +74 -37
- package/docs/guides/bind-open-parts.md +29 -12
- package/docs/guides/call-an-mcp-tool-from-a-machine.md +572 -0
- package/docs/guides/cancel-a-run.md +26 -16
- package/docs/guides/cut-a-worktree.md +560 -0
- package/docs/guides/fix-a-failed-converge.md +19 -7
- package/docs/guides/give-a-caller-a-scope.md +203 -0
- package/docs/guides/give-an-agent-mcp-tools.md +422 -0
- package/docs/guides/iterate-on-a-machine-on-kind.md +10 -9
- package/docs/guides/let-an-agent-write-a-chart.md +14 -7
- package/docs/guides/look-inside-a-sandbox.md +115 -42
- package/docs/guides/move-from-kind-to-a-shared-cluster.md +16 -0
- package/docs/guides/open-the-console.md +63 -14
- package/docs/guides/push-and-sign-a-branch.md +67 -21
- package/docs/guides/read-a-conversation.md +225 -0
- package/docs/guides/run-a-chart-by-name.md +17 -16
- package/docs/guides/run-a-command-or-call-a-url.md +84 -41
- package/docs/guides/run-one-worker-per-item.md +34 -27
- package/docs/guides/set-an-agents-access-and-dials.md +69 -22
- package/docs/guides/size-a-workspace.md +19 -6
- package/docs/guides/start-a-run-for-each-alert.md +290 -0
- package/docs/guides/start-a-run-from-a-run.md +18 -16
- package/docs/guides/start-a-run.md +24 -18
- package/docs/guides/steer-an-agent.md +332 -0
- package/docs/guides/use-a-hosted-provider.md +7 -1
- package/docs/guides/watch-a-run.md +146 -117
- package/docs/guides/write-a-hook-kind.md +238 -0
- package/docs/guides/write-a-tool-as-an-mcp-server.md +351 -0
- package/docs/guides/write-a-workflow.md +18 -8
- package/docs/llms.txt +110 -23
- package/docs/reference/authoring-surface/agent.md +35 -12
- package/docs/reference/authoring-surface/answer.md +20 -4
- package/docs/reference/authoring-surface/arg.md +28 -6
- package/docs/reference/authoring-surface/basic.md +1 -1
- package/docs/reference/authoring-surface/bearer.md +1 -1
- package/docs/reference/authoring-surface/branchname.md +15 -0
- package/docs/reference/authoring-surface/carry.md +23 -0
- package/docs/reference/authoring-surface/chartschema.md +1 -1
- package/docs/reference/authoring-surface/chartschemafor.md +1 -1
- package/docs/reference/authoring-surface/command.md +37 -22
- package/docs/reference/authoring-surface/customize.md +4 -1
- package/docs/reference/authoring-surface/defineconfig.md +1 -1
- package/docs/reference/authoring-surface/defineevent.md +1 -1
- package/docs/reference/authoring-surface/dispatch.md +5 -4
- package/docs/reference/authoring-surface/fail.md +18 -5
- package/docs/reference/authoring-surface/file.md +37 -17
- package/docs/reference/authoring-surface/frompick.md +1 -1
- package/docs/reference/authoring-surface/gate.md +118 -0
- package/docs/reference/authoring-surface/group-agents.md +15 -0
- package/docs/reference/authoring-surface/group-charts.md +16 -0
- package/docs/reference/authoring-surface/group-composition.md +25 -0
- package/docs/reference/authoring-surface/group-config.md +17 -0
- package/docs/reference/authoring-surface/group-effects.md +48 -0
- package/docs/reference/authoring-surface/group-failure.md +13 -0
- package/docs/reference/authoring-surface/group-gates.md +16 -0
- package/docs/reference/authoring-surface/group-machine-and-events.md +15 -0
- package/docs/reference/authoring-surface/group-steering.md +12 -0
- package/docs/reference/authoring-surface/group-types-these-signatures-name.md +137 -0
- package/docs/reference/authoring-surface/group-workspaces-and-repos.md +15 -0
- package/docs/reference/authoring-surface/http.md +109 -9
- package/docs/reference/authoring-surface/isfailure.md +1 -1
- package/docs/reference/authoring-surface/isready.md +14 -0
- package/docs/reference/authoring-surface/jr2setup.md +14 -4
- package/docs/reference/authoring-surface/mcp.md +107 -0
- package/docs/reference/authoring-surface/open.md +41 -8
- package/docs/reference/authoring-surface/openbody.md +1 -1
- package/docs/reference/authoring-surface/paramsof.md +23 -5
- package/docs/reference/authoring-surface/pool.md +79 -11
- package/docs/reference/authoring-surface/push.md +31 -13
- package/docs/reference/authoring-surface/readyof.md +21 -0
- package/docs/reference/authoring-surface/reread.md +19 -0
- package/docs/reference/authoring-surface/secret.md +26 -5
- package/docs/reference/authoring-surface/sentby.md +1 -1
- package/docs/reference/authoring-surface/skip.md +22 -0
- package/docs/reference/authoring-surface/source.md +59 -4
- package/docs/reference/authoring-surface/steer.md +93 -0
- package/docs/reference/authoring-surface/text.md +1 -1
- package/docs/reference/authoring-surface/type-agentdefinition.md +16 -3
- package/docs/reference/authoring-surface/type-agentmcpserver.md +54 -0
- 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 +6 -7
- package/docs/reference/authoring-surface/type-caller.md +40 -0
- package/docs/reference/authoring-surface/type-callerrecord.md +32 -0
- package/docs/reference/authoring-surface/type-carry.md +19 -0
- package/docs/reference/authoring-surface/type-chainlink.md +3 -3
- 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 +23 -5
- package/docs/reference/authoring-surface/type-customize.md +20 -3
- package/docs/reference/authoring-surface/type-delivered.md +17 -0
- package/docs/reference/authoring-surface/type-deliveredevent.md +18 -0
- package/docs/reference/authoring-surface/type-dispatchdefinition.md +4 -3
- package/docs/reference/authoring-surface/type-dispatchoutput.md +9 -2
- package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
- package/docs/reference/authoring-surface/type-effectbinding.md +12 -2
- 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-gatebinding.md +15 -0
- package/docs/reference/authoring-surface/type-gatehooks.md +12 -0
- package/docs/reference/authoring-surface/type-gatelook.md +37 -0
- package/docs/reference/authoring-surface/type-gateoutcome.md +32 -0
- package/docs/reference/authoring-surface/type-gatereply.md +19 -0
- package/docs/reference/authoring-surface/type-gateslot.md +21 -0
- package/docs/reference/authoring-surface/type-gateslotinput.md +13 -0
- package/docs/reference/authoring-surface/type-gateslotparts.md +18 -0
- package/docs/reference/authoring-surface/type-gateslots.md +12 -0
- package/docs/reference/authoring-surface/type-gatetypesat.md +13 -0
- package/docs/reference/authoring-surface/type-hostinjectedinput.md +3 -5
- 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 +8 -2
- package/docs/reference/authoring-surface/type-jr2config.md +37 -2
- package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
- package/docs/reference/authoring-surface/type-lends.md +5 -1
- package/docs/reference/authoring-surface/type-lookoutput.md +14 -0
- package/docs/reference/authoring-surface/type-mcpcalloutput.md +78 -0
- package/docs/reference/authoring-surface/type-mcpnotreturned.md +42 -0
- package/docs/reference/authoring-surface/type-mcpoptions.md +47 -0
- package/docs/reference/authoring-surface/type-mcpoutput.md +50 -0
- package/docs/reference/authoring-surface/type-mcpreadoutput.md +53 -0
- package/docs/reference/authoring-surface/type-mcpremoteserver.md +50 -0
- package/docs/reference/authoring-surface/type-mcpserver.md +41 -0
- package/docs/reference/authoring-surface/type-mcpstdioserver.md +42 -0
- package/docs/reference/authoring-surface/type-namedsecret.md +4 -4
- 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-openbodydeclaration.md +1 -1
- package/docs/reference/authoring-surface/type-openbodyoutput.md +1 -1
- 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 +11 -6
- package/docs/reference/authoring-surface/type-pushinput.md +22 -3
- package/docs/reference/authoring-surface/type-pushoutput.md +33 -8
- package/docs/reference/authoring-surface/type-pushsourcespec.md +38 -0
- package/docs/reference/authoring-surface/type-readsourcespec.md +47 -0
- package/docs/reference/authoring-surface/type-reposlot.md +6 -6
- package/docs/reference/authoring-surface/type-reread.md +18 -0
- package/docs/reference/authoring-surface/type-runsconfig.md +41 -0
- package/docs/reference/authoring-surface/type-sandboxoptions.md +11 -4
- package/docs/reference/authoring-surface/type-sender.md +26 -1
- package/docs/reference/authoring-surface/type-sendercallerfield.md +14 -0
- package/docs/reference/authoring-surface/type-size.md +1 -1
- package/docs/reference/authoring-surface/type-skip.md +19 -0
- package/docs/reference/authoring-surface/type-slothandles.md +5 -13
- package/docs/reference/authoring-surface/type-sourcehooks.md +23 -0
- package/docs/reference/authoring-surface/type-sourcein.md +25 -0
- package/docs/reference/authoring-surface/type-sourcereply.md +18 -0
- package/docs/reference/authoring-surface/type-sourcespec.md +26 -17
- package/docs/reference/authoring-surface/type-sourceverdict.md +49 -0
- package/docs/reference/authoring-surface/type-standingpoolmachine.md +12 -0
- package/docs/reference/authoring-surface/type-standingpoolspec.md +51 -0
- package/docs/reference/authoring-surface/type-steerargs.md +20 -0
- package/docs/reference/authoring-surface/type-thinkinglevel.md +1 -1
- package/docs/reference/authoring-surface/type-usercontainer.md +1 -1
- package/docs/reference/authoring-surface/type-workspaced.md +30 -9
- 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 +66 -38
- package/docs/reference/authoring-surface/worktree.md +71 -0
- package/docs/reference/authoring-surface.md +88 -147
- package/docs/reference/cli.md +413 -74
- package/docs/reference/config.md +248 -6
- package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +10 -9
- package/docs/reference/decisions/0002-duplex-streaming-actor.md +81 -14
- package/docs/reference/decisions/0004-worktree-provisioning-model.md +27 -21
- package/docs/reference/decisions/0005-sandbox-pod-composition.md +39 -25
- package/docs/reference/decisions/0006-agent-control-surface.md +6 -3
- package/docs/reference/decisions/0007-durable-machine-state.md +1 -1
- package/docs/reference/decisions/0009-cli-and-instance-interface.md +150 -49
- package/docs/reference/decisions/0010-bdd-acceptance-tests.md +5 -3
- package/docs/reference/decisions/0011-workflow-defined-events.md +256 -12
- package/docs/reference/decisions/0012-workspace-wrapper-machine.md +39 -32
- package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +54 -30
- package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +53 -27
- package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +26 -16
- package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +9 -7
- package/docs/reference/decisions/0017-pool-over-a-source-port.md +226 -10
- package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +15 -7
- package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +53 -27
- package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +16 -2
- package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +4 -4
- package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +124 -43
- package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +36 -38
- package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +10 -5
- package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +16 -7
- package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +36 -19
- package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +18 -10
- package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +3 -2
- package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +23 -15
- package/docs/reference/decisions/0032-the-console-unlocks-with-a-callers-credential.md +101 -0
- package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +22 -22
- package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +6 -6
- package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +13 -12
- package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +23 -23
- package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +6 -1
- 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/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +55 -40
- package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +14 -13
- package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +31 -24
- package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +18 -10
- package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +137 -86
- package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +165 -91
- package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +7 -7
- package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +2 -2
- package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md +109 -0
- 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
- package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +221 -153
- package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +161 -96
- package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +32 -9
- package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +48 -33
- package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +56 -30
- package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +35 -25
- 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 +296 -0
- package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +347 -0
- package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
- package/docs/reference/decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md +285 -0
- 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
- package/docs/reference/decisions.md +9 -4
- package/docs/reference/glossary.md +138 -45
- package/docs/reference/hooks.md +1077 -0
- package/docs/reference/http-api.md +988 -123
- package/docs/reference/machines.md +0 -1
- package/docs/why.md +7 -5
- package/package.json +10 -3
- package/src/actor.ts +194 -42
- package/src/agent.ts +56 -6
- package/src/ambient.ts +3 -5
- package/src/assembly-checks.ts +530 -4
- package/src/callers.ts +245 -0
- package/src/chart.ts +238 -26
- package/src/charts.ts +9 -2
- package/src/config.ts +566 -7
- package/src/conversations.ts +453 -0
- package/src/custodian.lua +17 -5
- package/src/custodian.ts +257 -77
- package/src/customize.ts +154 -7
- package/src/dispatch-effect.ts +142 -26
- package/src/durability.ts +2 -0
- package/src/duration.ts +20 -0
- package/src/effect-checks.ts +52 -4
- package/src/effect.ts +401 -132
- package/src/effector-wire.ts +278 -30
- package/src/fail.ts +17 -4
- package/src/gate-brand.ts +16 -0
- package/src/gate.ts +634 -48
- package/src/harness-client.ts +84 -35
- package/src/harness-env.ts +33 -0
- package/src/held-secrets.ts +197 -39
- package/src/holds.ts +5 -2
- package/src/hook-kinds/discord.ts +92 -0
- package/src/hook-kinds/door.ts +107 -0
- package/src/hook-kinds/github.ts +89 -0
- package/src/hook-kinds/gitlab.ts +72 -0
- package/src/hook-kinds/hook.ts +7 -0
- package/src/hook-kinds/index.ts +465 -0
- package/src/hook-kinds/slack.ts +151 -0
- package/src/hook-kinds/webhook.ts +113 -0
- package/src/hooks.ts +506 -0
- package/src/http-effect.ts +164 -70
- package/src/http.ts +933 -99
- package/src/index.ts +30 -5
- package/src/instance.ts +196 -26
- package/src/internal.ts +23 -3
- package/src/kube-client.ts +6 -3
- package/src/machine-doc.ts +29 -5
- package/src/mcp-effect.ts +424 -0
- package/src/model-keys.ts +24 -0
- package/src/mount-digest.ts +41 -7
- package/src/names.ts +82 -7
- package/src/notices.ts +576 -59
- package/src/open-body.ts +110 -30
- package/src/open-effect.ts +17 -4
- package/src/open.ts +40 -7
- package/src/parts.ts +125 -35
- package/src/party.ts +35 -1
- package/src/pick.ts +43 -9
- package/src/pool.ts +1172 -172
- package/src/registration.ts +249 -17
- package/src/repo-identity.ts +7 -3
- package/src/repos.ts +20 -2
- package/src/run-host.ts +1614 -190
- package/src/run-list.ts +191 -0
- package/src/sandbox-effect.ts +217 -66
- package/src/sandbox-kube.ts +116 -21
- package/src/secret-placement.ts +248 -35
- package/src/server.ts +53 -4
- package/src/setup.ts +158 -38
- package/src/snapshot-store.ts +565 -48
- package/src/steer.ts +111 -0
- package/src/stub-harness.ts +6 -2
- package/src/tokens.ts +362 -46
- package/src/vocabulary.ts +23 -4
- package/src/wire.ts +27 -10
- package/src/workspace.ts +314 -259
- 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/0032-the-console-unlocks-with-the-instance-token.md +0 -61
- package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +0 -65
package/src/custodian.ts
CHANGED
|
@@ -21,14 +21,17 @@
|
|
|
21
21
|
// so the two pod shapes cannot drift
|
|
22
22
|
//
|
|
23
23
|
// The Custodian is a separate container from the Harness, and that is the point: the Agent
|
|
24
|
-
// executes code in the Harness container and nothing here (ADR-0005). What it holds — the
|
|
25
|
-
//
|
|
26
|
-
// `shareProcessNamespace` stays off, so the Agent cannot read its env, its files or its
|
|
24
|
+
// executes code in the Harness container and nothing here (ADR-0005). What it holds — the pod's
|
|
25
|
+
// control and fetch tokens and every held secret its pod claims — is mounted into this container
|
|
26
|
+
// alone, and `shareProcessNamespace` stays off, so the Agent cannot read its env, its files or its
|
|
27
|
+
// `/proc`. The Harness container gets the Stand-ins, every one but the public fetch Stand-in
|
|
28
|
+
// secret, as files under `/run/jr2/` that no child the Harness confines can read (ADR-0076).
|
|
27
29
|
|
|
28
30
|
import { readFileSync } from "node:fs";
|
|
29
31
|
import type { HarnessEnvVar } from "./config.ts";
|
|
30
32
|
import type { ResourceRequirements } from "./size.ts";
|
|
31
33
|
import {
|
|
34
|
+
CONTROL_TOKEN_NAME,
|
|
32
35
|
CREDENTIAL_HEADERS,
|
|
33
36
|
SANDBOX_TOKEN_NAME,
|
|
34
37
|
standIn,
|
|
@@ -37,10 +40,12 @@ import {
|
|
|
37
40
|
} from "./held-secrets.ts";
|
|
38
41
|
import {
|
|
39
42
|
CA_CONFIGMAP,
|
|
40
|
-
|
|
43
|
+
CUSTODIAN_CONTROL_SOCKET,
|
|
44
|
+
CUSTODIAN_FETCH_PORT,
|
|
41
45
|
CUSTODIAN_HEALTH_PORT,
|
|
42
46
|
CUSTODIAN_PORT,
|
|
43
47
|
CUSTODIAN_PROGRAM_KEY,
|
|
48
|
+
CUSTODIAN_SOCKET_DIR,
|
|
44
49
|
HELD_CONFIGMAP,
|
|
45
50
|
HELD_TLS_SECRET,
|
|
46
51
|
heldSecretsSecret,
|
|
@@ -96,8 +101,60 @@ export const CUSTODIAN_ARGS: readonly string[] = [
|
|
|
96
101
|
"warning",
|
|
97
102
|
];
|
|
98
103
|
|
|
99
|
-
/** The address
|
|
100
|
-
|
|
104
|
+
/** The address of the Custodian's fetch listener: the ask alone (ADR-0053), which
|
|
105
|
+
* `jr2-upload-pack` makes from the domain. The Harness container gets it as `JR2_CUSTODIAN_URL`,
|
|
106
|
+
* the one `JR2_*` name that a confined process keeps (ADR-0076). The agent routes are not here. */
|
|
107
|
+
export const CUSTODIAN_URL = `http://127.0.0.1:${CUSTODIAN_FETCH_PORT}`;
|
|
108
|
+
|
|
109
|
+
/** The Harness env that names the Custodian's control socket ({@link CUSTODIAN_CONTROL_SOCKET}):
|
|
110
|
+
* where the Harness sends each control request — the surface read, a pick, a delivery's poll and a
|
|
111
|
+
* notice's acknowledgment (ADR-0076). A path, never a value; the domain's env does not keep it. */
|
|
112
|
+
export const CUSTODIAN_SOCKET_ENV = "JR2_CUSTODIAN_SOCKET";
|
|
113
|
+
/** The volume of the control socket's folder ({@link CUSTODIAN_SOCKET_DIR}): an emptyDir in the
|
|
114
|
+
* Harness container and the Custodian container, and in no other container of the pod. */
|
|
115
|
+
export const CUSTODIAN_SOCKET_VOLUME = "custodian-socket";
|
|
116
|
+
|
|
117
|
+
/** Where the Harness container holds its Stand-ins (ADR-0059, ADR-0076): a read-only mount that no
|
|
118
|
+
* child the Harness confines can read — the confiner denies all of `/run/jr2`. Never env. */
|
|
119
|
+
export const HARNESS_CREDENTIALS = "/run/jr2";
|
|
120
|
+
/** The secret control Stand-in, as the Harness reads it once at start (C1). */
|
|
121
|
+
export const CONTROL_STANDIN_FILE = `${HARNESS_CREDENTIALS}/control/standin`;
|
|
122
|
+
/** The directory of the held secrets' Stand-ins, one file per name: `<dir>/<NAME>`. */
|
|
123
|
+
export const STANDINS_DIR = `${HARNESS_CREDENTIALS}/standins`;
|
|
124
|
+
/** The Harness env that names the control Stand-in's file; its default is {@link CONTROL_STANDIN_FILE}. */
|
|
125
|
+
export const CONTROL_STANDIN_FILE_ENV = "JR2_CONTROL_STANDIN_FILE";
|
|
126
|
+
/** The Harness env that names the held secrets the `workspace()` grants a confined child (its
|
|
127
|
+
* `secrets` list, ADR-0076), comma-separated: names, never a value. Absent → none is granted. */
|
|
128
|
+
export const GRANTED_SECRETS_ENV = "JR2_GRANTED_SECRETS";
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The keys of a pod's credentials Secret (a Sandbox's `<sandbox>-token`): the fetch token, the
|
|
132
|
+
* control token, the secret control Stand-in, and one secret Stand-in per held secret
|
|
133
|
+
* ({@link heldStandInKey}). The pod's maker mints them all. The tokens are projected into the
|
|
134
|
+
* Custodian alone; each Stand-in into the Custodian, for its swap, and the Harness container.
|
|
135
|
+
*/
|
|
136
|
+
export const CREDENTIAL_KEYS = {
|
|
137
|
+
fetchToken: "token",
|
|
138
|
+
controlToken: "control-token",
|
|
139
|
+
controlStandIn: "control-standin",
|
|
140
|
+
} as const;
|
|
141
|
+
|
|
142
|
+
/** The key of one held secret's secret Stand-in in its pod's credentials Secret. */
|
|
143
|
+
export function heldStandInKey(name: string): string {
|
|
144
|
+
return `standin-${name}`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** The file of a Stand-in among the Custodian's values: beside its value, `<NAME>.standin`. A held
|
|
148
|
+
* name is an env var's, so it holds no `.` and never meets another value's file. */
|
|
149
|
+
export function standInValueFile(name: string): string {
|
|
150
|
+
return `${name}.standin`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** The path prefixes each control credential may ride, after the control listener's rewrite
|
|
154
|
+
* (ADR-0076, C3): the control token on the agent routes alone, the fetch token on the fetch route
|
|
155
|
+
* alone. The program's path guard refuses either one elsewhere, before any swap. */
|
|
156
|
+
export const CONTROL_PATHS = "/agents/";
|
|
157
|
+
export const FETCH_PATHS = "/sandboxes/";
|
|
101
158
|
|
|
102
159
|
/** Where the Orchestrator's Service is, from inside a Harness pod. */
|
|
103
160
|
export type OrchestratorAddress = { host: string; port: number };
|
|
@@ -409,9 +466,15 @@ function isIpLiteral(host: string): boolean {
|
|
|
409
466
|
* The Custodian's static bootstrap (ADR-0059): Envoy's whole configuration, rendered once per
|
|
410
467
|
* converge by `jr2 up`, identical for every Harness pod of the Instance.
|
|
411
468
|
*
|
|
412
|
-
* control
|
|
413
|
-
*
|
|
414
|
-
*
|
|
469
|
+
* control /run/jr2/custodian/control.sock
|
|
470
|
+
* the five agent routes — the Menu, a pick, a delivery's poll, a
|
|
471
|
+
* notice's acknowledgment and a batch of the kept conversation — plain
|
|
472
|
+
* HTTP, to the Orchestrator's Service.
|
|
473
|
+
* They swap the control Stand-in for the control token. A Unix socket
|
|
474
|
+
* in a folder that no confined process can write, so no process in
|
|
475
|
+
* the domain can take this listener's place (ADR-0013, ADR-0076)
|
|
476
|
+
* fetch 127.0.0.1:8081 the ask alone, plain HTTP, to the same Service: it swaps the public
|
|
477
|
+
* fetch Stand-in for the fetch token (ADR-0053, ADR-0076)
|
|
415
478
|
* health 0.0.0.0:15021 GET /healthz, for the kubelet, which cannot reach loopback
|
|
416
479
|
* egress 127.0.0.1:15001 CONNECT only — present when a held secret exists; a bound target
|
|
417
480
|
* goes to its intercept listener, every other one is tunneled
|
|
@@ -428,55 +491,81 @@ export function custodianBootstrap(manifest: HeldManifest, orchestrator: Orchest
|
|
|
428
491
|
metadata: meta({ role: "control", route: name }),
|
|
429
492
|
});
|
|
430
493
|
|
|
494
|
+
// One listener toward the Orchestrator, with its routes and the deny route after them: a route
|
|
495
|
+
// of the other listener gets the deny route's 404 here.
|
|
496
|
+
const toOrchestrator = (name: string, address: Json, routes: Json[]): Json => ({
|
|
497
|
+
name,
|
|
498
|
+
address,
|
|
499
|
+
filter_chains: [
|
|
500
|
+
{
|
|
501
|
+
filters: [
|
|
502
|
+
hcm(name, {
|
|
503
|
+
target: orchestratorTarget,
|
|
504
|
+
idleSeconds: 3600,
|
|
505
|
+
lua: true,
|
|
506
|
+
local: ORCHESTRATOR_UNREACHABLE,
|
|
507
|
+
routes: {
|
|
508
|
+
virtual_hosts: [
|
|
509
|
+
{
|
|
510
|
+
name: "orchestrator",
|
|
511
|
+
domains: ["*"],
|
|
512
|
+
routes: [
|
|
513
|
+
...routes,
|
|
514
|
+
{
|
|
515
|
+
name: "deny",
|
|
516
|
+
match: { prefix: "/" },
|
|
517
|
+
direct_response: { status: 404 },
|
|
518
|
+
metadata: meta({ role: "deny" }),
|
|
519
|
+
},
|
|
520
|
+
],
|
|
521
|
+
},
|
|
522
|
+
],
|
|
523
|
+
},
|
|
524
|
+
}),
|
|
525
|
+
],
|
|
526
|
+
},
|
|
527
|
+
],
|
|
528
|
+
});
|
|
529
|
+
|
|
431
530
|
const listeners: Json[] = [
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
},
|
|
471
|
-
],
|
|
472
|
-
},
|
|
473
|
-
],
|
|
474
|
-
},
|
|
475
|
-
}),
|
|
476
|
-
],
|
|
477
|
-
},
|
|
478
|
-
],
|
|
479
|
-
},
|
|
531
|
+
// The agent routes, on the control socket (ADR-0076). A Unix socket, not a loopback port: a
|
|
532
|
+
// confined process can bind a free loopback port while the Custodian restarts, and it then gets
|
|
533
|
+
// the control Stand-in on the Harness's next request. It cannot make a socket under `/run/jr2`.
|
|
534
|
+
// Envoy removes a socket that a stopped Envoy left, before it binds. Mode 0666, because the
|
|
535
|
+
// Harness runs as a uid that is not Envoy's: the folder, not the mode, keeps the domain out.
|
|
536
|
+
toOrchestrator("control", { pipe: { path: CUSTODIAN_CONTROL_SOCKET, mode: 0o666 } }, [
|
|
537
|
+
// A read retries any reset. A pick retries only a reset before it was sent: one the
|
|
538
|
+
// Orchestrator may have read is not sent twice.
|
|
539
|
+
route("surface", "GET", "/agents/[^/]+/surface", {
|
|
540
|
+
retry_policy: { retry_on: "reset,connect-failure", num_retries: 2 },
|
|
541
|
+
}),
|
|
542
|
+
route("events", "POST", "/agents/[^/]+/events", {
|
|
543
|
+
retry_policy: { retry_on: "reset-before-request,connect-failure", num_retries: 2 },
|
|
544
|
+
}),
|
|
545
|
+
// An outstanding pick's answer, long-polled (ADR-0024): a read, so any reset is retried — an
|
|
546
|
+
// Orchestrator restart mid-poll costs a retry, not the pick.
|
|
547
|
+
route("deliveries", "GET", "/agents/[^/]+/deliveries/[^/]+", {
|
|
548
|
+
retry_policy: { retry_on: "reset,connect-failure", num_retries: 2 },
|
|
549
|
+
}),
|
|
550
|
+
// The acknowledgment of the notices a step boundary appended (ADR-0062, ADR-0076): marking
|
|
551
|
+
// them heard twice changes nothing, so any reset is retried.
|
|
552
|
+
route("notices", "POST", "/agents/[^/]+/notices", {
|
|
553
|
+
retry_policy: { retry_on: "reset,connect-failure", num_retries: 2 },
|
|
554
|
+
}),
|
|
555
|
+
// A batch of the conversation's entries (ADR-0023): the Orchestrator keeps each entry once,
|
|
556
|
+
// so a batch sent twice changes nothing, and any reset is retried.
|
|
557
|
+
route("conversation", "POST", "/agents/[^/]+/conversation", {
|
|
558
|
+
retry_policy: { retry_on: "reset,connect-failure", num_retries: 2 },
|
|
559
|
+
}),
|
|
560
|
+
]),
|
|
561
|
+
// The ask (ADR-0053), on loopback: `jr2-upload-pack` runs in the domain, and the fetch Stand-in
|
|
562
|
+
// is public, so a process that takes this port gets nothing secret. Like a pick, it retries only
|
|
563
|
+
// a reset before it was sent.
|
|
564
|
+
toOrchestrator("fetch", { socket_address: { address: "127.0.0.1", port_value: CUSTODIAN_FETCH_PORT } }, [
|
|
565
|
+
route("fetch", "POST", "/fetch", {
|
|
566
|
+
retry_policy: { retry_on: "reset-before-request,connect-failure", num_retries: 2 },
|
|
567
|
+
}),
|
|
568
|
+
]),
|
|
480
569
|
{
|
|
481
570
|
name: "health",
|
|
482
571
|
address: { socket_address: { address: "0.0.0.0", port_value: CUSTODIAN_HEALTH_PORT } },
|
|
@@ -792,24 +881,40 @@ function luaList(values: readonly string[]): string {
|
|
|
792
881
|
/**
|
|
793
882
|
* The script the Custodian runs: a generated `HELD` table, then `dofile` of the fixed program
|
|
794
883
|
* (`CUSTODIAN_PROGRAM`, mounted beside it from `jr2-held`). The table names every secret this pod
|
|
795
|
-
* holds — the
|
|
796
|
-
* and the targets each is bound to. It never holds a value
|
|
797
|
-
* mounted files at start.
|
|
884
|
+
* holds — the control credentials first, then the manifest's — with the headers and paths each may
|
|
885
|
+
* ride and the targets each is bound to. It never holds a value, and never a secret Stand-in: the
|
|
886
|
+
* program reads both from the mounted files at start (ADR-0059).
|
|
887
|
+
*
|
|
888
|
+
* The control credentials (ADR-0076, C3): the control token, path-restricted to the agent routes,
|
|
889
|
+
* whose secret Stand-in the program reads from its values; and, on a pod that serves a Sandbox
|
|
890
|
+
* (`fetch`), the fetch token, path-restricted to the fetch route, whose Stand-in is the one public
|
|
891
|
+
* one and so rides the table. The Instance Harness and a check pod serve no fetch, and hold the
|
|
892
|
+
* control token alone.
|
|
798
893
|
*/
|
|
799
|
-
export function custodianScript(manifest: HeldManifest): string {
|
|
894
|
+
export function custodianScript(manifest: HeldManifest, opts: { fetch: boolean }): string {
|
|
895
|
+
const control = [
|
|
896
|
+
`{ name = ${lua(CONTROL_TOKEN_NAME)}, headers = { "authorization" }, paths = ${luaList([CONTROL_PATHS])} }`,
|
|
897
|
+
...(opts.fetch
|
|
898
|
+
? [
|
|
899
|
+
`{ name = ${lua(SANDBOX_TOKEN_NAME)}, headers = { "authorization" }, standIn = ${lua(standIn(SANDBOX_TOKEN_NAME))}, ` +
|
|
900
|
+
`paths = ${luaList([FETCH_PATHS])} }`,
|
|
901
|
+
]
|
|
902
|
+
: []),
|
|
903
|
+
];
|
|
800
904
|
const lines = [
|
|
801
905
|
"-- Generated by `jr2 up` from its pod's held manifest (ADR-0059). Names, headers, paths, targets; never a value.",
|
|
802
906
|
`HELD = { secrets = {}, targets = {}, hosts = ${targetsOf(manifest).length} }`,
|
|
803
907
|
`HELD.credential = ${luaList(CREDENTIAL_HEADERS)}`,
|
|
804
|
-
`HELD.secrets[1] =
|
|
805
|
-
|
|
908
|
+
...control.map((entry, i) => `HELD.secrets[${i + 1}] = ${entry}`),
|
|
909
|
+
`HELD.control = { ${control.map((_, i) => `HELD.secrets[${i + 1}]`).join(", ")} }`,
|
|
806
910
|
];
|
|
911
|
+
const offset = control.length + 1;
|
|
807
912
|
manifest.secrets.forEach((s, i) => {
|
|
808
913
|
const paths = s.paths ? `, paths = ${luaList(s.paths)}` : "";
|
|
809
|
-
lines.push(`HELD.secrets[${i +
|
|
914
|
+
lines.push(`HELD.secrets[${i + offset}] = { name = ${lua(s.name)}, headers = ${luaList(s.headers)}${paths} }`);
|
|
810
915
|
});
|
|
811
916
|
for (const t of targetsOf(manifest)) {
|
|
812
|
-
const refs = t.secrets.map((s) => `HELD.secrets[${manifest.secrets.indexOf(s) +
|
|
917
|
+
const refs = t.secrets.map((s) => `HELD.secrets[${manifest.secrets.indexOf(s) + offset}]`);
|
|
813
918
|
lines.push(`HELD.targets[${lua(t.key)}] = { ${refs.join(", ")} }`);
|
|
814
919
|
}
|
|
815
920
|
// `dofile`, not a copy: Envoy's Lua opens the standard libraries (the program already reads its
|
|
@@ -830,12 +935,26 @@ export type CustodianCompositionOptions = {
|
|
|
830
935
|
* makes the pod: the Orchestrator at provision (`sandboxCustodianConfigMap`, owned by the CR),
|
|
831
936
|
* `jr2 up` for the Instance Harness (`INSTANCE_CUSTODIAN_CONFIGMAP`). */
|
|
832
937
|
config: string;
|
|
833
|
-
/**
|
|
834
|
-
*
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
*
|
|
938
|
+
/**
|
|
939
|
+
* The pod's credentials Secret (ADR-0076): its control token, its secret control Stand-in, its
|
|
940
|
+
* fetch token (a Sandbox alone) and one secret Stand-in per held secret, under
|
|
941
|
+
* {@link CREDENTIAL_KEYS} and {@link heldStandInKey} — a key may be renamed here. The pod's maker
|
|
942
|
+
* mints them: the Orchestrator a Sandbox's (`<sandbox>-token`), `jr2 up` the Instance Harness's
|
|
943
|
+
* and a check pod's. The tokens are mounted into the Custodian alone (ADR-0013); each Stand-in
|
|
944
|
+
* into the Custodian and the Harness container, as files.
|
|
945
|
+
*/
|
|
946
|
+
credentials: {
|
|
947
|
+
secret: string;
|
|
948
|
+
keys?: Partial<Record<keyof typeof CREDENTIAL_KEYS, string>>;
|
|
949
|
+
};
|
|
950
|
+
/** This pod's Sandbox — the name the ask is addressed by (ADR-0053), and the reason the pod holds
|
|
951
|
+
* a fetch token. Absent on the Instance Harness and a check pod, which mount no Repo and answer an
|
|
952
|
+
* ask by saying so. */
|
|
838
953
|
sandbox?: string;
|
|
954
|
+
/** The held secrets the `workspace()` grants its confined children (its `secrets` list, ADR-0076):
|
|
955
|
+
* the Harness puts their Stand-ins, and only theirs, in a child's env. Names the manifest does not
|
|
956
|
+
* hold are left out. Absent → none. */
|
|
957
|
+
granted?: readonly string[];
|
|
839
958
|
/** The names of the Instance objects that this Custodian reads in place of the defaults. The
|
|
840
959
|
* defaults are the Instance's own: `pod`'s literal values (`heldSecretsSecret(pod)`),
|
|
841
960
|
* `jr2-held-tls`, `jr2-ca`, and the program in `jr2-held`.
|
|
@@ -850,15 +969,24 @@ export type CustodianCompositionOptions = {
|
|
|
850
969
|
};
|
|
851
970
|
|
|
852
971
|
type Container = Record<string, unknown>;
|
|
972
|
+
|
|
973
|
+
/** The Harness container's credentials volume: its Stand-ins, mounted at {@link HARNESS_CREDENTIALS}. */
|
|
974
|
+
export const HARNESS_CREDENTIALS_VOLUME = "harness-credentials";
|
|
853
975
|
type Volume = Record<string, unknown>;
|
|
854
976
|
type VolumeMount = { name: string; mountPath: string; readOnly?: boolean };
|
|
855
977
|
|
|
856
978
|
/** What one Harness pod gains from the Custodian. */
|
|
857
979
|
export type CustodianComposition = {
|
|
858
|
-
/** The Harness container's env — in `env`, after `harness.env`, before the wire's gate (§5.1)
|
|
859
|
-
*
|
|
860
|
-
*
|
|
980
|
+
/** The Harness container's env — in `env`, after `harness.env`, before the wire's gate (§5.1):
|
|
981
|
+
* the Custodian's fetch address and control socket, the control Stand-in's file, the granted
|
|
982
|
+
* names, the proxy and the
|
|
983
|
+
* trust. Never a Stand-in: those are files ({@link HARNESS_CREDENTIALS}), so no `envFrom` key is
|
|
984
|
+
* hidden by one, and `jr2 up` refuses a held name that is a key of an `envFrom` Secret (R14). */
|
|
861
985
|
harnessEnv: HarnessEnvVar[];
|
|
986
|
+
/** The Harness container's mounts: the trust bundles, its Stand-ins at {@link HARNESS_CREDENTIALS},
|
|
987
|
+
* and the control socket's folder below it. Each is read-only. A placement mounts the
|
|
988
|
+
* conversations' volume at `/run/jr2/conversations`, below the Stand-ins too: the kubelet makes
|
|
989
|
+
* the mount point of a volume that is below a projected volume (ADR-0076). */
|
|
862
990
|
harnessMounts: VolumeMount[];
|
|
863
991
|
custodianContainer: Container;
|
|
864
992
|
volumes: Volume[];
|
|
@@ -892,15 +1020,22 @@ export function custodianComposition(manifest: HeldManifest, opts: CustodianComp
|
|
|
892
1020
|
program = HELD_CONFIGMAP,
|
|
893
1021
|
} = opts.reads ?? {};
|
|
894
1022
|
|
|
1023
|
+
const keys = { ...CREDENTIAL_KEYS, ...opts.credentials.keys };
|
|
1024
|
+
const fetch = opts.sandbox !== undefined;
|
|
1025
|
+
const heldNames = new Set(manifest.secrets.map((s) => s.name));
|
|
1026
|
+
const granted = (opts.granted ?? []).filter((n) => heldNames.has(n));
|
|
1027
|
+
|
|
1028
|
+
// No Stand-in rides env (ADR-0059, ADR-0076): each is a file under `/run/jr2/`, below.
|
|
895
1029
|
const harnessEnv: HarnessEnvVar[] = [
|
|
896
1030
|
{ name: "JR2_CUSTODIAN_URL", value: CUSTODIAN_URL },
|
|
897
|
-
{ name:
|
|
1031
|
+
{ name: CUSTODIAN_SOCKET_ENV, value: CUSTODIAN_CONTROL_SOCKET },
|
|
1032
|
+
{ name: CONTROL_STANDIN_FILE_ENV, value: CONTROL_STANDIN_FILE },
|
|
1033
|
+
...(granted.length ? [{ name: GRANTED_SECRETS_ENV, value: granted.join(",") }] : []),
|
|
898
1034
|
];
|
|
899
1035
|
if (held) {
|
|
900
1036
|
const proxy = `http://127.0.0.1:${CUSTODIAN_PORT}`;
|
|
901
1037
|
const noProxy = "localhost,127.0.0.1,::1";
|
|
902
1038
|
harnessEnv.push(
|
|
903
|
-
...manifest.secrets.map((s) => ({ name: s.name, value: standIn(s.name) })),
|
|
904
1039
|
{ name: "HTTPS_PROXY", value: proxy },
|
|
905
1040
|
{ name: "https_proxy", value: proxy },
|
|
906
1041
|
{ name: "NO_PROXY", value: noProxy },
|
|
@@ -923,7 +1058,20 @@ export function custodianComposition(manifest: HeldManifest, opts: CustodianComp
|
|
|
923
1058
|
// Group-readable, for the pod's fsGroup: the Custodian runs as Envoy's own uid.
|
|
924
1059
|
defaultMode: 0o440,
|
|
925
1060
|
sources: [
|
|
926
|
-
|
|
1061
|
+
// The control credentials (ADR-0076): the control token beside its secret Stand-in, and
|
|
1062
|
+
// on a Sandbox the fetch token, whose Stand-in is public and rides the script. Then each
|
|
1063
|
+
// held secret's secret Stand-in, beside the value the Custodian swaps it for.
|
|
1064
|
+
{
|
|
1065
|
+
secret: {
|
|
1066
|
+
name: opts.credentials.secret,
|
|
1067
|
+
items: [
|
|
1068
|
+
{ key: keys.controlToken, path: CONTROL_TOKEN_NAME },
|
|
1069
|
+
{ key: keys.controlStandIn, path: standInValueFile(CONTROL_TOKEN_NAME) },
|
|
1070
|
+
...(fetch ? [{ key: keys.fetchToken, path: SANDBOX_TOKEN_NAME }] : []),
|
|
1071
|
+
...manifest.secrets.map((s) => ({ key: heldStandInKey(s.name), path: standInValueFile(s.name) })),
|
|
1072
|
+
],
|
|
1073
|
+
},
|
|
1074
|
+
},
|
|
927
1075
|
...(literal.length ? [{ secret: { name: literals, items: literal.map((n) => ({ key: n, path: n })) } }] : []),
|
|
928
1076
|
...manifest.secrets.flatMap((s) =>
|
|
929
1077
|
s.source.kind === "secret"
|
|
@@ -962,6 +1110,31 @@ export function custodianComposition(manifest: HeldManifest, opts: CustodianComp
|
|
|
962
1110
|
]
|
|
963
1111
|
: []),
|
|
964
1112
|
...(trust ? [{ name: "ca", configMap: { name: trustMap } }] : []),
|
|
1113
|
+
// The control socket's folder (ADR-0076): the Custodian makes the socket, and the Harness
|
|
1114
|
+
// connects to it. It goes with the pod, and it outlives a restart of either container, so the
|
|
1115
|
+
// socket's path never changes.
|
|
1116
|
+
{ name: CUSTODIAN_SOCKET_VOLUME, emptyDir: {} },
|
|
1117
|
+
// The Harness container's Stand-ins (ADR-0076): the secret control Stand-in and each held
|
|
1118
|
+
// secret's, as files under `/run/jr2/`, which the confiner denies to every child the Harness
|
|
1119
|
+
// starts. Group-readable for the pod's fsGroup — the Harness runs as no fixed uid, and the
|
|
1120
|
+
// kubelet writes the files as root — so 0440 is the least mode the Harness can read.
|
|
1121
|
+
{
|
|
1122
|
+
name: HARNESS_CREDENTIALS_VOLUME,
|
|
1123
|
+
projected: {
|
|
1124
|
+
defaultMode: 0o440,
|
|
1125
|
+
sources: [
|
|
1126
|
+
{
|
|
1127
|
+
secret: {
|
|
1128
|
+
name: opts.credentials.secret,
|
|
1129
|
+
items: [
|
|
1130
|
+
{ key: keys.controlStandIn, path: "control/standin" },
|
|
1131
|
+
...manifest.secrets.map((s) => ({ key: heldStandInKey(s.name), path: `standins/${s.name}` })),
|
|
1132
|
+
],
|
|
1133
|
+
},
|
|
1134
|
+
},
|
|
1135
|
+
],
|
|
1136
|
+
},
|
|
1137
|
+
},
|
|
965
1138
|
];
|
|
966
1139
|
|
|
967
1140
|
const custodianContainer: Container = {
|
|
@@ -974,6 +1147,8 @@ export function custodianComposition(manifest: HeldManifest, opts: CustodianComp
|
|
|
974
1147
|
volumeMounts: [
|
|
975
1148
|
{ name: "custodian-values", mountPath: CUSTODIAN_VALUES, readOnly: true },
|
|
976
1149
|
{ name: "custodian-config", mountPath: CUSTODIAN_CONFIG, readOnly: true },
|
|
1150
|
+
// The one folder that the Custodian writes: where it makes its control socket.
|
|
1151
|
+
{ name: CUSTODIAN_SOCKET_VOLUME, mountPath: CUSTODIAN_SOCKET_DIR },
|
|
977
1152
|
...(held
|
|
978
1153
|
? [
|
|
979
1154
|
{ name: "custodian-tls", mountPath: CUSTODIAN_TLS, readOnly: true },
|
|
@@ -996,7 +1171,12 @@ export function custodianComposition(manifest: HeldManifest, opts: CustodianComp
|
|
|
996
1171
|
|
|
997
1172
|
return {
|
|
998
1173
|
harnessEnv,
|
|
999
|
-
harnessMounts:
|
|
1174
|
+
harnessMounts: [
|
|
1175
|
+
...(trust ? [{ name: "ca", mountPath: CA_MOUNT, readOnly: true }] : []),
|
|
1176
|
+
{ name: HARNESS_CREDENTIALS_VOLUME, mountPath: HARNESS_CREDENTIALS, readOnly: true },
|
|
1177
|
+
// Read-only: the Harness connects to the socket, and nothing in its container writes here.
|
|
1178
|
+
{ name: CUSTODIAN_SOCKET_VOLUME, mountPath: CUSTODIAN_SOCKET_DIR, readOnly: true },
|
|
1179
|
+
],
|
|
1000
1180
|
custodianContainer,
|
|
1001
1181
|
volumes,
|
|
1002
1182
|
};
|