Close the review's gaps round the agent account (hq ADR 0266)
mesh/merge-gate pass: builds claude-code, claude-licence-manager, restic, sudo → ace, g14, novox, shanks; no bus step; every machine composes with the chan…
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; it…
mesh/delivery delivered

The managed settings apply to every account's claude, so a key-helper script under the
agent's home would run as the operator, and through sudo as root: the command is now
carried whole in the root-owned managed settings and reads the key only as the agent.
The helper reads only a regular file, bounded, without blocking, and a request it does
not answer fails in time instead of holding every later one; a directory entry it
answers that is not a name is refused. The guard's record is matched on its binary in
the query, a half-named agent account is not taken as the operator's, the kept API key
stays until the agent's home holds one, restic refuses to restore beside a directory
others may write, and the agent-home check cleans a path before judging it.
This commit is contained in:
jochen
2026-10-09 23:48:15 +02:00
parent b382045721
commit b240085e0a
11 changed files with 295 additions and 33 deletions
+36 -9
View File
@@ -64,7 +64,12 @@ func PathsFrom(env func(string) string) (Paths, bool) {
Settings: env("MESH_CLAUDE_CODE_SETTINGS"), Home: env("MESH_AGENT_HOME"), Node: env("MESH_NODE"),
Agent: env("MESH_AGENT_ACCOUNT"), Operator: env("MESH_OPERATOR_ACCOUNT"),
AgentHome: env("MESH_AGENT_HOME"), OperatorHome: env("MESH_OPERATOR_HOME")}
if p.Home == "" || p.Agent == "" {
if (p.Home == "") != (p.Agent == "") {
// Half of the agent's account is not "the operator's": taken so, the agent's account rule of the guard
// would be off and its home never written, and nothing would say so. Not launched, said (main).
return p, false
}
if p.Home == "" {
p.Home, p.Agent, p.AgentHome = env("MESH_OPERATOR_HOME"), p.Operator, env("MESH_OPERATOR_HOME")
}
return p, p.State != "" && p.Facts != "" && p.Settings != "" && p.Home != "" && p.Node != ""
@@ -122,10 +127,27 @@ func (p Paths) binding() string { return filepath.Join(p.State, "licence.jso
func (p Paths) apiKey() string { return filepath.Join(p.keyDir(), "api-key") }
func (p Paths) helper() string { return filepath.Join(p.keyDir(), "api-key-helper") }
// keyDir is where an API-key licence and its key-helper live: the module's state, which the agent runs the
// helper from as the operator — or, where the agent runs as an account of its own, which cannot enter the
// module's state, a directory of the module's in that account's agent directory (novox/hq ADR 0266). Either
// way readable by the account the agent runs as, and by it alone.
// helperCommand is the managed settings' apiKeyHelper: the key-helper's path in the module's state where the agent
// runs as the operator. Where it runs as an account of its own, never a file under that account's home: the
// managed settings apply to every account's `claude`, the operator's and root's included, so a script the agent
// owns there would run as them at their next start (novox/hq ADR 0266, review of 2026-10-09). It is a command
// written whole in the root-owned managed settings instead, which reads the key only when run as the agent's
// account, and as it.
func (p Paths) helperCommand() string {
if !p.Confined() {
return p.helper()
}
return "[ \"$(id -un)\" = " + shellQuote(p.Agent) + " ] && exec cat -- " + shellQuote(p.apiKey())
}
// shellQuote is s as one word of a POSIX shell.
func shellQuote(s string) string { return "'" + strings.ReplaceAll(s, "'", `'\''`) + "'" }
// keyDir is where an API-key licence lives: the module's state, with the key-helper the agent runs from it as
// the operator — or, where the agent runs as an account of its own, which cannot enter the module's state, a
// directory of the module's in that account's agent directory (novox/hq ADR 0266), and no helper there: the
// managed settings carry its command (helperCommand). Either way readable by the account the agent runs as,
// and by it alone.
func (p Paths) keyDir() string {
if p.Confined() {
return filepath.Join(p.Home, ".claude", "mesh")
@@ -210,7 +232,7 @@ func RenderNow(p Paths, write WriteManaged) ([]string, error) {
if mesh != nil {
mesh.Refused = RefusedCounts(GuardDataOf(p, mesh).Log)
}
files := RenderWithMesh(facts, settings, binding, p.helper(), Registered(p), config, mesh)
files := RenderWithMesh(facts, settings, binding, p.helperCommand(), Registered(p), config, mesh)
tree, _ := json.Marshal(MarketplaceWith(config, guardFiles(p, mesh)))
files[MarketplaceDir+"/"] = string(tree)
names := make([]string, 0, len(files))
@@ -432,10 +454,15 @@ func Apply(p Paths, c Current, write WriteManaged) (map[string]any, error) {
if err := fsys.WriteFile(p.apiKey(), []byte(strings.TrimSpace(plain)+"\n"), 0o600); err != nil {
return nil, err
}
if err := fsys.WriteFile(p.helper(), []byte("#!/bin/sh\nexec cat '"+p.apiKey()+"'\n"), 0o700); err != nil {
return nil, err
if p.Confined() {
// No script under the agent's home: the managed settings carry the command whole (helperCommand).
_ = fsys.Remove(p.helper())
} else {
if err := fsys.WriteFile(p.helper(), []byte("#!/bin/sh\nexec cat "+shellQuote(p.apiKey())+"\n"), 0o700); err != nil {
return nil, err
}
_ = fsys.Chmod(p.helper(), 0o700)
}
_ = fsys.Chmod(p.helper(), 0o700)
} else {
var g Grant
if err := json.Unmarshal([]byte(plain), &g); err != nil {