diff --git a/02-DECISIONS/0213-the-operator-sets-the-agents-managed-settings-through-the-agent-module.md b/02-DECISIONS/0213-the-operator-sets-the-agents-managed-settings-through-the-agent-module.md new file mode 100644 index 0000000..97390dc --- /dev/null +++ b/02-DECISIONS/0213-the-operator-sets-the-agents-managed-settings-through-the-agent-module.md @@ -0,0 +1,71 @@ +--- +topic: what runs on it +status: accepted +date: 2026-10-04 +deciders: jochen +reconstructed: false +extends: 02-DECISIONS/0183-the-anthropic-licence-manager-is-a-module-and-hands-tokens-to-the-agent-over-the-bus.md +--- + +# 213. The operator sets the agent's managed settings through the agent module, under the mesh's own keys + +## Context + +The agent module writes the agent's machine-wide managed settings file +([to-be 36](../03-DESIGN/01-to-be/36-the-operators-agent-on-a-machine.md) §2). It carries the mesh's +own keys only: the attribution convention of its repositories, the connectors kept beside the managed +tool servers, and the key-helper for an API-key licence. Every other key was left to the person's own +settings, so that the mesh never reverts a person's choice on a push. + +That left no place for a rule the **operator** wants to hold in every session on a machine: what the +agent may do without asking, what it must never do, and what its unattended mode allows. These keys +are not preferences. They are policy about what an agent may do on the mesh. Set by hand in one +person's settings on each machine, they are unmanaged state the mesh cannot see, and the agent refuses +to change them itself, as it should. + +## Considered Options + +1. **Leave them to each person's settings.** Rejected: policy by hand on each machine, invisible to + the mesh, and a session cannot be asked to loosen its own permissions. +2. **A field per vendor key** (permissions, auto mode, environment, hooks) in the module's settings. + Rejected: the vendor adds keys, and every one would be a change to the module. +3. **One setting holding managed-settings keys, laid under the mesh's own.** The operator sets it + for the mesh or for one node through the controller's settings verb. The module copies its keys into + the managed settings file, then lays the mesh's keys over them. + +## Decision + +Option 3. + +1. The agent module takes a setting, `managed_settings`: an object in the vendor's settings shape. It + is set for the whole mesh or for one node, like the module's other settings, through the + controller's settings verb. +2. The managed settings file is that object with **the mesh's keys laid last**: the attribution + convention, the connectors kept beside the managed servers, and the key-helper. A setting can + neither replace one of these nor add a key-helper that the binding did not ask for. +3. Only the operator sets it, and it is declared state like the role and the extra tool servers. A + person's preferences stay in their own settings; the mesh still sets none of them by itself. + +## Consequences + +- The operator's rules for the agent are declared once, for the mesh or per node, and reach every + node at the next push. A rule set in the managed settings outranks every other scope, so it holds + in every session on the node. +- A setting layer is replaced whole by the controller's verb. Setting this key without the role or + the extra tool servers clears those in that layer; the module's documentation says so. +- **What got harder:** a person cannot override a rule set here, which is the point. A rule that is + wrong is wrong on every session of the node until the operator changes the setting. + +## How it is checked + +| Rule | Checked by | +|---|---| +| The operator's keys reach the managed settings file | the agent module's test: an auto-mode allow list and a permissions list set in the setting appear in the rendered file | +| The mesh's keys always win | the same test: a setting naming the attribution, the connectors key or a key-helper is overridden, and a key-helper appears only for an API-key binding | +| Live | the setting given for the mesh; the managed settings file on each node carries the key after the next push | + +## References + +- [ADR 0183](0183-the-anthropic-licence-manager-is-a-module-and-hands-tokens-to-the-agent-over-the-bus.md) — the agent module and the files it writes +- [to-be 36](../03-DESIGN/01-to-be/36-the-operators-agent-on-a-machine.md) — the design this amends (§2, §6) +- the vendor's documentation on managed settings and their precedence diff --git a/02-DECISIONS/README.md b/02-DECISIONS/README.md index b806cb5..0ebf373 100644 --- a/02-DECISIONS/README.md +++ b/02-DECISIONS/README.md @@ -312,6 +312,7 @@ python3 00-META/checks/index.py fail if stale - **0208** — [The graphical session is one module per piece, on the mesh's seats](0208-the-graphical-session-is-one-module-per-piece-on-the-meshs-seats.md) - **0209** — [A login on a node moves that node to the account it logged in to; an API key is added from any node, sealed](0209-a-login-on-a-node-moves-that-node-to-its-account-and-an-api-key-is-added-from-any-node-sealed.md) - **0211** — [A machine's power is a node seat, its moments take contributions, and its states are events](0211-a-machines-power-is-a-node-seat-its-moments-take-contributions-and-its-states-are-events.md) +- **0213** — [The operator sets the agent's managed settings through the agent module, under the mesh's own keys](0213-the-operator-sets-the-agents-managed-settings-through-the-agent-module.md) ### How it is built diff --git a/03-DESIGN/01-to-be/36-the-operators-agent-on-a-machine.md b/03-DESIGN/01-to-be/36-the-operators-agent-on-a-machine.md index 0dd5cde..3a9ff59 100644 --- a/03-DESIGN/01-to-be/36-the-operators-agent-on-a-machine.md +++ b/03-DESIGN/01-to-be/36-the-operators-agent-on-a-machine.md @@ -1,9 +1,10 @@ --- layer: to-be -status: implemented +status: in-progress code: [mesh-catalog modules/claude-code] updated: 2026-10-04 decisions: + - 02-DECISIONS/0213-the-operator-sets-the-agents-managed-settings-through-the-agent-module.md - 02-DECISIONS/0209-a-login-on-a-node-moves-that-node-to-its-account-and-an-api-key-is-added-from-any-node-sealed.md - 02-DECISIONS/0206-a-node-reports-the-anthropic-grant-it-holds-and-the-licence-manager-adopts-a-licence-by-refreshing-it.md - 02-DECISIONS/0181-the-operator-account-is-a-node-fact-and-a-home-is-a-placement-root.md @@ -50,7 +51,7 @@ instruction file and the manager's tools: |---|---| | `~/.claude/CLAUDE.md` | the managed instruction file: how a session on this mesh works (§3) | | `~/.claude/rules/00-hal-mesh.md`, `~/.claude/rules/conventions.md` | sections of the same file: this node's identity, the repositories' conventions | -| `~/.claude/settings.json`, merged | the managed settings file: the mesh's keys only, outranking nothing a person did not also set | +| `~/.claude/settings.json`, merged | the managed settings file: the mesh's keys, and the rules the operator set for the agent (§2) | | `~/.claude/skills/hal-switch-license/SKILL.md` | the manager seat's `switch` verb, listed by the console, and a sentence in the instruction file saying to use it | | `~/.claude/skills/cleanup/SKILL.md` | nothing; it named the predecessor's forge | | the console's entry in the agent's user-scope state | the managed settings' tool-server key, from the console's provision (§4) | @@ -68,7 +69,7 @@ lists them, and until they go the agent reads stale instructions beside the mesh **Declared, applied by the host:** the agent's package (§7); the module's state directory; a facts file in that directory carrying the node's name and the console's endpoint, and a settings file carrying the -role and the extra tool servers, merged from the module's settings layers — the bundle is told the two +role, the extra tool servers and the operator's managed-settings keys, merged from the module's settings layers — the bundle is told the two files' paths, because a bundle's words are paths and constants only (ADR 0192); the bus, the console's provision, and that it uses the `anthropic-licence-manager` seat. Two directories, declared so the ownership check sees them: the agent's managed directory under `/etc`, root's, and `~/.claude` under the operator's home, the operator's. No *file* resource under @@ -79,7 +80,7 @@ changes: | path | content | |---|---| -| the managed settings file | the mesh's keys: the tool servers (the console, plus any the operator declared as settings), the attribution trailers, and — for an API-key binding only — the key-helper that serves the key | +| the managed settings file | the keys the operator set in the module's `managed_settings` setting, with the mesh's keys laid over them: the tool servers (the console, plus any the operator declared as settings), the attribution trailers, and — for an API-key binding only — the key-helper that serves the key | | the managed instruction file | §3 | | the agent's credentials file under the operator's home | for a subscription binding only: the access token the manager handed over, as the operator, readable by the operator alone, atomic, no refresh token | | the module's keypair in its state | made once, the private half never leaves (§5) | @@ -94,6 +95,14 @@ requires. The model, the spinner, the drafts and every other preference are the predecessor's experience with the model key is the evidence: a mesh that sets a preference reverts a person's choice on every push. +**The operator's rules for the agent** ([ADR 0213](../../02-DECISIONS/0213-the-operator-sets-the-agents-managed-settings-through-the-agent-module.md)). +What the agent may do without asking, what it must never do and what its unattended mode allows are +not preferences: they are policy about an agent on the mesh, and a session must not loosen its own. The +operator sets them in the module's `managed_settings` setting, in the vendor's settings shape, for the +mesh or one node. The module copies those keys into the managed settings file and lays the mesh's keys +last, so a setting can never replace the attribution convention, the connectors key or the key-helper, +and a key-helper appears only for an API-key binding. The mesh still sets no preference by itself. + ## 3. What the instruction file says Prose, not a paste; the file is the module's. @@ -177,7 +186,8 @@ a token is fetched by request when the state says it changed. **Every node with an operator account** ([ADR 0181](../../02-DECISIONS/0181-the-operator-account-is-a-node-fact-and-a-home-is-a-placement-root.md)). All four nodes carry one since 2026-10-03. **Per node:** the role. **Per mesh or per node:** -extra tool servers. **Prerequisite:** the manager holds its seat and has adopted the licences. +extra tool servers, and the operator's managed-settings keys (§2). The controller's verb replaces a +setting layer whole, so a layer set for one of these keeps the others it already held. **Prerequisite:** the manager holds its seat and has adopted the licences. **Order:** the manager assigned and a refresh observed; the console's provision in the catalogue; this module on one workstation; the six predecessor files and the hand-made console entry removed there; a @@ -204,6 +214,7 @@ installer is rejected: it puts a self-updating binary under the person's home, i | on a lab machine with no account, the assignment is refused naming the fact | ADR 0181 | | a switch asked of the seat through the console changes the licence and the token on the node; no tool answer and no log line holds a token | ADR 0183 | | the API-key binding writes nothing under the home and the agent authenticates through the helper | ADR 0183 | +| the module's test: keys set in `managed_settings` (an auto-mode allow list, a permissions list) appear in the rendered managed settings file, a setting naming the attribution, the connectors key or a key-helper is overridden, and a key-helper appears only for an API-key binding | ADR 0213 | | the console's provision resolves by co-location; a machine without the console refuses the module by name | ADR 0027, ADR 0152 | | a new session on the assigned workstation lists the console's five tools under `mesh` ([ADR 0195](../../02-DECISIONS/0195-the-meshs-tools-are-found-by-address-not-announced-whole.md)) and answers "which node am I" from the instruction file | the exit of the build |