Skip to content

Claude Code

Each Claude Code session on a developer workstation becomes a citizen of the town, “Claude (name of the agent)”, typing at its desk while Claude works. Its permission requests reach the mail, so you can approve them from your phone. And a member of the town can give the agent a mission: a task Claude runs in a repository, on the plan of the developer.

This goes through the local agent, a small program of the Myrtille command line that runs on the workstation.

  • It never writes a file into a repository: the hooks go in the settings of the user, never in a project, and only after you saw the change and said yes.
  • It never reads, stores or forwards a Claude credential, and never offers a “sign in with Claude”. Missions run the claude you installed and signed in to yourself.
  • It never sends a prompt, a file content, an edit, a tool response, an answer of Claude or a transcript to the town hall. Transcripts never leave the workstation.
  • It never skips the permissions of Claude Code, and never uses --bare. Myrtille is never the only barrier: the permission rules and the sandbox of Claude Code stay in force.
  • It never opens a port to the network: the agent connects out to the town hall, and serves its hooks on 127.0.0.1 only.
  • Claude Code 2.1.300 or later.
  • Node 22.19 or later on the 22 line, or 24.6 or later.
  • The command line of Myrtille, installed once: npm install -g @myrtillehq/cli. It is published by the release pipeline of the project: the next channel before version 1.0 (@myrtillehq/cli@next), latest from 1.0.

In the town, an owner creates an enrolment token (the step Agent of the setup, at /setup). It is single use and valid 15 minutes. Then, on the workstation:

Terminal window
myrtille connect https://town.example.com
myrtille agent
myrtille init --hooks
  1. myrtille connect <address> enrols the workstation: paste the enrolment token when it asks (typed without echo), or set MYRTILLE_ENROLMENT_TOKEN. A token given as an argument could be read by other users of the machine while the command runs. The token is traded for the long-term token of the agent, which the town hall renews over the connection. Options: --name <name> (the name the town plan refers to, by default the host name), --port <port> (the hook port, 7421 by default). The address must be https, or http on this machine only, unless you add --allow-http.
  2. myrtille agent runs the agent until Ctrl+C. It keeps one outbound connection to the town hall and comes back on its own after a cut.
  3. myrtille init --hooks gives Claude Code its hooks (below).

The agent keeps its state readable by you only, in MYRTILLE_AGENT_DIR or else:

System Directory
Linux $XDG_STATE_HOME/myrtille/agent (by default ~/.local/state/myrtille/agent)
macOS ~/Library/Application Support/myrtille/agent
Windows %APPDATA%\myrtille\agent

myrtille init --hooks writes the hooks of Claude Code in the settings of the user: $CLAUDE_CONFIG_DIR/settings.json, else ~/.claude/settings.json. Never in a project.

  • It shows what changes and asks first (--yes skips the question).
  • It adds its hooks after your own, and keeps the previous file as settings.json.myrtille-backup, readable by you only.
  • It never touches a file that is not valid JSON, nor one that changed while you were asked.
  • It refuses to make Claude Code run a copy of the command line that lies in a git working tree, a temporary folder or an npx cache, which a coding agent working there could change: install the command line instead.
  • Run it again after an upgrade of Myrtille to refresh the hooks. myrtille init --remove-hooks removes them, and only them.

Each hook runs myrtille hook claude-code <event>, which passes the event to the agent over a private channel of the workstation. No key or port is written in the settings. Every hook but the permission request runs in the background, so a session is never slowed down.

Per event: the session id, the working directory, the tool name and a summary of at most 200 characters (the description Claude Code writes, the file a tool acts on, the site of an address), the subagent type, the task subject, the notification message. Never a command line, a query, a search pattern or the input of an MCP tool. What looks like a secret in a summary is masked first. The masking is a safety net, not a guarantee.

The town places a session in the district of the repository the town plan declares for its working directory (repo.path), else in a district of kind local.

When Claude Code asks for a permission, the request becomes an item of the mail, critical when the command looks destructive (deleting files, rewriting git history, dropping data). The hook waits for the decision of the town:

  • Allow lets Claude Code go on; Deny refuses, with your message to Claude if you wrote one.
  • The item expires 5 seconds before the hook does (the hook waits 600 seconds). Claude Code then applies its own behaviour, its prompt on the workstation, and the town says the request expired.
  • When nobody can decide it (actions off in its district), the request is let go at once and Claude Code decides as if there were no hook.

A mission is a task given to an agent from its panel in the town (Give a mission), by a member with at least the operator role, after a confirmation: a mission spends the plan of the developer. It runs only:

  • in a repository the town plan names for that agent, with missions.enabled: true;
  • in a repository you allowed on that workstation: myrtille agent --allow-missions <path> (the root of a git repository; up to 50);
  • one at a time per repository, on a clean working tree, on a branch of its own that the agent creates first (mission/...).

The agent runs the claude of the workstation, unmodified and signed in by its user, headless: claude -p --output-format stream-json --verbose --permission-mode default, the prompt on standard input. A mission uses your own Claude plan, under the terms of Anthropic and the usage limits of that plan. The town sees the mission start, its turns, the tool in use and its end; it never sees the prompt again, nor the transcript or the answers of Claude. A member can cancel a mission: Claude gets an interrupt, then a stop after 5 seconds.

A headless session cannot show a prompt, so a mission gives its own session a hook, for that session only, passed with --settings (nothing is written into the repository or into your settings). When a tool call is not allowed by the settings of the repository, the session stops on that call and the agent opens a permission item in the mail. The item waits up to 30 minutes.

  • On Allow or Deny, the agent resumes the session with claude -p --resume <session id>, and the hook answers that very call. The mission goes on.
  • Without a decision in time, the call is denied with the reason, and the mission goes on without it.
  • Calls an allow rule of the repository lets through, and calls a deny or ask rule may match, follow the settings of the repository as before.
  • The end of a mission names the tools that were refused, never their input.

Allow missions only in a repository whose .claude/settings.json you trust, as you would before running claude there yourself.

Myrtille is a comfort layer, never the only barrier.

  • When the agent is not running, myrtille hook exits at once and Claude Code goes on as if there were no hook.
  • When the town hall cannot be reached, or is lost while a permission waits, the hook answers at once with nothing, and Claude Code applies its own behaviour: its prompt on the workstation.
  • Events wait in a queue on the workstation and are sent once the town hall is back. Meanwhile the citizens of the workstation turn grey in the town, with the time of their last news.
  • A town hall that refuses the token of the agent (revoked or expired) stops it: run myrtille connect again.
  • The hook server listens on 127.0.0.1 only and refuses a request without the key of the agent, from another host name, or from a web page.
  • The hooks of Claude Code reach the agent over a private channel only its user can open, and the key itself never travels on it.
  • The agent sends its long-term token in the Authorization header of one WebSocket to the town hall. Tokens and the key are never printed or logged.
  • Missions run claude and git by their absolute path, never a copy committed in the repository, and pass them only the variables they need: never a token of Myrtille, of Coolify or of an API, and never ANTHROPIC_API_KEY.