@notiamo/runner 0.1.2 → 0.2.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.
Files changed (3) hide show
  1. package/README.md +79 -6
  2. package/dist/cli.js +1098 -260
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -10,7 +10,9 @@ npx @notiamo/runner init
10
10
  and a name for the runner. It checks that `claude --version` works, runs `claude auth login` into the runner's own config
11
11
  folder, then installs the service and starts it. When it ends, the runner is running and starts again by itself.
12
12
 
13
- Needs Node.js 22 or later and [Claude Code](https://docs.claude.com/en/docs/claude-code) installed on the machine.
13
+ Needs Node.js 22 or later and [Claude Code](https://docs.claude.com/en/docs/claude-code) 2.1.314 or newer installed on
14
+ the machine. On Linux and WSL2 the shell sandbox also needs `bubblewrap` and `socat` (Debian/Ubuntu:
15
+ `sudo apt install bubblewrap socat`); `init` checks for them. On macOS there is nothing more to install.
14
16
 
15
17
  From a checkout of the Notiamo repository: `pnpm --filter @notiamo/runner build`, then
16
18
  `node packages/runner/dist/cli.js init`.
@@ -54,6 +56,69 @@ Notiamo's MCP server, sends Notiamo what the agent does, and reports when it end
54
56
  claude.ai login or rate limits for their products." The runner only runs your own Claude Code, on your machine, for
55
57
  your own work.
56
58
 
59
+ ## What the agent can and cannot touch on this machine
60
+
61
+ Each run gets its own Claude Code settings file, and Claude Code runs with `--permission-mode acceptEdits`. Three
62
+ things do the work:
63
+
64
+ - **File tools stay in the run folder.** `permissions.blockReadsOutsideWorkingDirectories` and
65
+ `blockWritesOutsideWorkingDirectories` make Claude's own file tools (read, edit, write, search) refuse anything
66
+ outside the folder of the run. The permission mode accepts edits only inside that folder; any other call that would
67
+ ask for permission is refused (the runner runs Claude Code in print mode, where nobody can answer). Edits are also
68
+ denied by rule in the runner's own files, your credential and config folders (`~/.ssh`, `~/.aws`, `~/.config`,
69
+ `~/.npm`, `~/.local`, `~/.claude`, `~/Library` on macOS, …), your shell start files, and every folder holding a
70
+ program the runner itself runs (each folder on the `PATH`, the folders of `git`, `claude` and `node`, the runner's
71
+ own install).
72
+ - **Shell commands run in the OS sandbox.** On macOS (Seatbelt) and on Linux or WSL2 (bubblewrap and socat), every
73
+ command the agent runs in a shell is confined. If the sandbox cannot start, the run does not start.
74
+ - **No git config, credential helper, credential file or token of the machine reaches the agent.** Claude Code gets
75
+ only a short list of the runner's environment variables (`PATH`, `HOME`, `USER`, `LOGNAME`, `SHELL`, `LANG`,
76
+ `LC_ALL`, `LC_CTYPE`, `TERM`, `TMPDIR`, `TZ`, `XDG_*`, the proxy settings `HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY`, and
77
+ `NODE_EXTRA_CA_CERTS`, `SSL_CERT_FILE`, `SSL_CERT_DIR`) plus the run's own: never `SSH_AUTH_SOCK`, `GH_TOKEN`,
78
+ `GITHUB_TOKEN`, `NPM_TOKEN`, `AWS_*` or `ANTHROPIC_*`. The agent's git sees no global or system git config
79
+ (`GIT_CONFIG_GLOBAL=/dev/null`, `GIT_CONFIG_NOSYSTEM=1`) and commits as "Notiamo agent". Commands that name
80
+ `credential`, `osxkeychain`, `libsecret`, `security find-`, `secret-tool`, `gh auth` or `ssh-add` are denied.
81
+ - **The keychain service itself is reachable from the sandbox on macOS.** The programs that read it are also denied
82
+ to the shell as defense in depth: git's credential helpers (`git-credential-*` in `git --exec-path`),
83
+ `/usr/bin/security`, `secret-tool` and `gnome-keyring` on Linux, and `gh`. That is not a wall: a script the agent
84
+ writes can still start system programs such as `/usr/bin/security`. This is why the runner needs no credential of
85
+ the machine: keep no GitHub login in this user's keychain (`notiamo-runner init` warns when it finds one), or run
86
+ the runner as a dedicated macOS user with its own keychain, holding nothing but what the runner needs.
87
+
88
+ What the shell sandbox allows and denies:
89
+
90
+ - **Reads denied:** the other projects' folders (other runs at the same time included), the runner's own files
91
+ (`config.json` with the runner token, `state.json`, `runs/`, `claude/`, `git/`, the log) and your credential files
92
+ and folders that exist: `~/.ssh`, `~/.aws`, `~/.gnupg`, `~/.netrc`, `~/.docker`, `~/.config/gh`,
93
+ `~/.git-credentials`, `~/.config/git`, `~/.npmrc`, `~/.pypirc`, `~/.config/gcloud`, `~/.config/hub`.
94
+ - **Writes allowed:** only in the run folder. The run's `.git/hooks` and `.git/config` are read-only.
95
+ - **Network allowed:** GitHub, npm, Notiamo, the WordPress site (for a WordPress run) and any extra hosts you add on
96
+ the Runners card in Notiamo. Everything else is refused.
97
+
98
+ The agent never pushes and holds no credentials. The runner pushes the run branch itself after the agent's work.
99
+
100
+ When a run ends, the runner kills every process left in its process group (a dev server or a watcher the agent
101
+ started). A process that detached itself with `setsid` has left that group and can outlive the run.
102
+
103
+ **Windows.** Plain Windows has no shell sandbox: only the file-tool rules above apply, and `init` says so. For the
104
+ full sandbox, run the runner inside WSL2.
105
+
106
+ ## Several runs at once
107
+
108
+ On the Runners card in Notiamo you set how many runs this runner does at the same time (1 to 3). Notiamo hands the
109
+ number to the runner when it connects. Each run uses your Claude plan, so keep it to what the plan has room for. One
110
+ project never runs twice at once: a second run on the same project waits for the first (and the runner refuses one
111
+ that arrives while the first still runs here).
112
+
113
+ Each run at the same time has its own slot folder, `projects/0`, `projects/1` or `projects/2`, made when the runner
114
+ starts: a run's sandbox cannot read the other slots at all, even a project cloned there after the run began.
115
+
116
+ ## A runner for one client
117
+
118
+ When you make a runner in Notiamo, you can limit it to one client (workspace). It then takes only that client's runs.
119
+ To set it up on the client's PC: install Claude Code there, run `npx @notiamo/runner init`, and paste the runner's
120
+ token. The Claude login on that PC is the one that pays for the runs, so sign in with the account that should pay.
121
+
57
122
  ## WordPress sites
58
123
 
59
124
  The connection is on the project's Website access card in Notiamo: an application password, and optionally SSH or
@@ -89,17 +154,25 @@ Everything lives in `~/.notiamo-runner` (or the folder in `NOTIAMO_RUNNER_HOME`)
89
154
  | --- | --- |
90
155
  | `config.json` | Notiamo's address, the runner's name and the runner token (`ntr_…`), nothing else. Readable by you only (mode 0600). |
91
156
  | `claude/` | Claude Code's config folder for the runner (`CLAUDE_CONFIG_DIR`): its sign-in and sessions. |
92
- | `projects/` | The working copies of your projects, one folder per project. |
157
+ | `projects/<slot>/<project>/` | The working copies of your projects, one folder per project in each slot (`0`, `1`, `2`: one per run at the same time). A WordPress run works in a temporary folder inside it, removed after the run. A project not used for the number of days set on the Runners card is removed; the next run clones it again. |
158
+ | `git/` | The runner's own copies of your repositories: `<project>.git` (a bare mirror, the only copy that talks to GitHub; removed with the project's last working copy), `<project>.<slot>.worktree.json` (what the working copy's `.git` looked like after the runner's own git, checked before the runner uses it again) and `<project>.last-run` (its time is the project's last run). The agent can neither read nor write this folder. |
159
+ | `runs/<id>.json` | The Claude Code settings of a run in progress (permission rules, the sandbox's folders and hosts; no secret). Deleted when the run ends. |
93
160
  | `runner.log` | What the runner did (no secrets), rotated at 10 MB. |
94
- | `state.json` | The run in progress, so a restart can carry on. |
161
+ | `state.json` | The ids of the runs in progress. After a restart, each is reported to Notiamo as failed (`runner_restarted`). |
95
162
 
96
- The secrets of a run (WordPress passwords, the run's MCP token) come from Notiamo with the run and
163
+ The secrets of a run (WordPress passwords, the run's MCP token, its GitHub token) come from Notiamo with the run and
97
164
  live only in memory: the agent gets them as environment variables (the MCP config on its command line only names them),
98
165
  never in a file. The two MCP tokens are `NOTIAMO_RUN_TOKEN` and `NOTIAMO_WP_TOKEN`. What the agent prints is scrubbed
99
166
  of them before the transcript is uploaded.
100
167
 
101
- The runner never gets a GitHub token. The machine's own git credentials clone and push; Notiamo opens and merges the
102
- pull request through its GitHub App.
168
+ The agent has no git credentials at all, and the runner never uses the machine's git credentials: its own git runs
169
+ with no user or system git config (`GIT_CONFIG_GLOBAL=/dev/null`, `GIT_CONFIG_NOSYSTEM=1`), so no credential helper or
170
+ keychain of the machine is asked. Instead each run gets from Notiamo a one-hour GitHub App token for the run's
171
+ repository only. The runner keeps it in memory and uses it for its own fetch and push, handed to git through its
172
+ environment (never on a command line, never in a file); close to the hour's end it asks Notiamo for a fresh one. The
173
+ runner clones only from `https://github.com/…` and pushes the run branch itself after the agent's work. It refuses to
174
+ push if the run changed the clone's `.git` folder. Notiamo opens and merges the pull request through its GitHub App.
175
+ Keep no GitHub login in this user's keychain: the runner does not need it, and a run could reach it.
103
176
 
104
177
  The SSH host key of a WordPress server is not verified yet.
105
178