Files
mesh-catalog/modules/claude-code
jochen 1d8f1ceff8 claude-code: drop what was removed while away, render one at a time, refuse an empty settings set
Review of the nox-mesh plugin: a watch hands over what is there, not what
went, so the view is pruned to the state's keys before it starts; the
watches and the tools render one at a time, as the home's record is read
and written whole; the register answer names how an item is offered.
2026-10-05 14:25:10 +02:00
..
2026-10-04 12:27:36 +02:00
2026-10-04 12:27:36 +02:00

claude-code

The operator's agent on a machine (novox/hq design 36): its package, its machine-wide managed configuration, and the consumer side of the Anthropic licence manager (design 39, ADR 0183).

What it owns

Two directories, declared, so the mesh refuses a second module owning either:

  • /etc/claude-code, the agent's machine-wide managed directory, root's, 0755.
  • ~/.claude under the operator account's home, the operator's, 0700. The module owns the directory — that it exists, who owns it, its mode — and of what is inside only what it writes. Everything else in it (memory, history, projects, local settings, a person's own rules and skills) is the person's and is never read or written (hq ADR 0182). Unassigned, the module leaves the directory: the host removes a directory only when it is empty.

What it writes

Under the agent's managed directory, /etc/claude-code, owned whole by this module and rewritten whenever the node's tool runtime collects the module's tools:

file holds
managed-mcp.json the tool servers every session loads: the mesh's console as mesh, and the servers set in this module's mcp_servers setting. Exclusive: a server not listed here does not load — not one added with claude mcp add, not a project's .mcp.json, not a plugin's
managed-settings.json the keys set in this module's managed_settings setting, then the settings registered through this module (the mesh's, then this node's), under the mesh's own keys: the repositories' attribution convention, the claude.ai connectors kept beside the managed servers, the key-helper while the node holds an API-key licence, and the two that name the nox-mesh marketplace and enable its plugin
CLAUDE.md how a session on this mesh works, this node's name and role, the conventions — then the instruction sections registered for every node and for this one
marketplace/ the nox-mesh plugin (hq ADR 0216): the skills, subagents, commands, hooks and output styles registered for every node and for this one, offered in a session as nox-mesh:<name>. Replaced whole, staged beside and swapped in

Under the operator's home: ~/.claude/.credentials.json, only when the licence manager hands this node a subscription token; and what is registered at the home scope for this node — a skill, subagent, command, output style, or instructions as a rule file — each path recorded in the module's state (home-placed.json). It writes, changes and removes only those, never a path the person made, and leaves a placed file alone once it was changed by hand (hq ADR 0182). The status tool reads the rest of the home's items to report them; nothing else is read or written.

Over NATS

Everything between this module and the rest of the mesh is NATS, in three kinds: an event says that something happened and carries no secret, because a stream keeps it; a request carries a token, because nothing keeps it (hq design 32 §10); and state is the current value of something every node must see, a node that joins later included — kept, so it carries no secret either (hq ADR 0201).

what how
what this node holds the module's holdings state, one key for this node — the account, the kind, fingerprints and expiries, never a token — written at start and whenever the credentials file changes (hq ADR 0206)
a person ran /login here the credentials file gains a refresh token this module never writes; its next report shows it, and the licence manager asks claude_code_grant for it, giving its key — the one time a refresh token leaves the node, for the manager to adopt by refreshing it
what this node should hold the licence manager's bindings state, this node's key; on a newer generation this module asks anthropic-licence-manager.current for its token, sealed to the key it sends, and writes it access-token-only — so the agent here never refreshes. A node that was off reads its key when it is back
an MCP server registered through this module a key in the module's servers state — all.<server> for every node, <node>.<server> for one; every node watches it and renders what applies to it, a node's own entry over the one for every node. A node that joins later, or was off, reads the whole current set at start; unregistering is a delete. An entry with a secret in its env or headers is refused by the runtime
the agent's configuration (hq ADR 0216) a key in the module's config state per registration — mesh.<kind>.<name> for every node, node.<node>.<kind>.<name> for one, home.<node>.<kind>.<name> for one account's own directory — the item and its files in one value, at most 256 KiB. Every node watches it and renders what applies to it, a node item over a mesh item of the same kind and name

Tools

claude_code_status, claude_code_render, claude_code_pull, claude_code_grant (for the licence manager), claude_code_mcp_list, claude_code_mcp_register (this node by default; nodes: "all" or a list for more — called for this node alone, its answer names the other nodes running claude-code), claude_code_mcp_unregister.

The agent's configuration (hq ADR 0216), each registered at a scope — mesh (the default), node (nodes, or this node) or home (the operator account's own ~/.claude on nodes, or this node):

  • for each kind — skill, agent, command, hook, output_style, instructions — claude_code_<kind>_list, _register, _unregister. A skill is its files (files, or content for a lone SKILL.md); a hook is an event, a matcher, a command and its scripts as files, with ${HOOK_DIR} in the command naming their directory. Hooks and settings take no home scope;
  • claude_code_settings_get, _set (merged into the scope, or replace), _clear; claude_code_permission_add and _remove for one allow, ask or deny rule. The agent refuses to loosen its own settings: these are the operator's to call;
  • claude_code_config_list, _show (one registration in full), _status (what applies here, the plugin as written, and the home's own items — which the mesh placed, which share a name with a mesh item, which call a tool server not loaded here) and _import (an item of this node's home, registered at a scope; the original stays).

A new session takes a change; a running one at /reload-plugins.

Settings

Per node or for the whole mesh, through mesh-controller.settings module=claude-code:

  • role — what this node is, in a few words; shown to every session.
  • mcp_servers — extra tool servers, set by the operator for the mesh or a node, beside the ones registered through the tools; keyed by name, in the vendor's .mcp.json entry shape ({"type":"http","url":…} or {"type":"stdio","command":…,"args":[…]}). The name mesh is the module's own and cannot be set. Put a person's own servers here, or they stop loading.
  • managed_settings — keys of the agent's managed settings, in the vendor's settings.json shape: permissions (allow, ask, deny), autoMode (environment, allow, soft_deny), env, hooks and so on. Managed settings outrank every other scope, so a rule here holds in every session on the node. The mesh's own keys (attribution, allowAllClaudeAiMcps, apiKeyHelper) are laid last and cannot be set. A setting layer is replaced whole: setting managed_settings without role or mcp_servers clears those in that layer.

On a machine that carried the predecessor

Remove these by hand, once; the mesh removes nothing it did not make (ADR 0182):

  • ~/.claude/CLAUDE.md
  • ~/.claude/rules/00-hal-mesh.md, ~/.claude/rules/conventions.md
  • ~/.claude/skills/cleanup/, ~/.claude/skills/hal-switch-license/
  • the hand-made console entry in ~/.claude.json under mcpServers — it is ignored now anyway

Escalation

Writing /etc/claude-code needs root. The runtime runs as the operator account, and the module uses that account's passwordless sudo; on a machine without it, claude_code_render says so and nothing is written.

Code

Go, one binary (cmd/claude-code) the node's runtime launches. Tested with go test ./...; the managed instruction file is held to the TypeScript renderer it replaced (testdata/rendered-by-typescript.json), and the sealed box is the licence manager's own format.