@cabane/companion 0.6.185 → 0.6.187

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.
package/README.md CHANGED
@@ -14,19 +14,18 @@ Three things, one relation each.
14
14
  - **A harness** is a coding tool on the device with a login behind it. The Companion detects each one and reports its state: connected, needs sign-in, limited, problem, off or gone. A device offers exactly the harnesses it has.
15
15
  - **An agent** is assigned to one device and runs through one of that device's harnesses, with a model from it. Assign in as many workspaces as you like: one Companion serves them all.
16
16
 
17
- **Cabane decides what an agent is; the device decides what it can touch.** Everything about the agent lives on its page in Cabane and is delivered with each turn, so an edit there lands on the next reply with no restart: its charter, its harness and model, its MCP servers, its host access and its starting folder. The device holds only what can't travel from a server: the device token, your secrets, the sandbox, and local overrides for this device.
17
+ **Cabane decides what an agent is; the device decides what it can touch.** Everything about the agent lives on its page in Cabane and is delivered with each turn, so an edit there lands on the next reply with no restart: its charter, its harness and model, its MCP servers and its starting folder. The device holds only what can't travel from a server: the device token, your secrets, and local overrides for this device.
18
18
 
19
19
  How a turn runs, top to bottom:
20
20
 
21
- | Layer | Holds |
22
- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
23
- | **Cabane** | The agent's reach: **host access** off (workspace only, the default) or on (shell and files on the device, starting in a folder). Only the agent's owner changes it. A conversation can't widen it. |
24
- | **Companion** | The listener on the device. Holds the pairing and the harness logins. Sets the device's ceiling: the sandbox and what it allows, which no agent setting can override. |
25
- | **Runner** | A child process per turn with a one-turn credential, when runners are on. Under Docker, a container per conversation and agent. |
26
- | **Sandbox** | An OS wrapper around the whole harness process (bubblewrap on Linux, Seatbelt on macOS), or the container itself. Everything the harness spawns is inside it: subprocesses, stdio MCP servers, the lot. |
27
- | **Harness** | Claude Code, Codex or OpenCode with its normal tools. Nothing is removed by name; the sandbox is enforced by the operating system around the process, never by a list of tool names. Nothing asks for permission mid-task. |
21
+ | Layer | Holds |
22
+ | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
23
+ | **Cabane** | The agent: its charter, harness and model, the folder it starts in and its MCP servers. Who can edit it decides who changes them; an agent never does, and a conversation can't. |
24
+ | **Companion** | The listener on the device. Holds the pairing and the harness logins. |
25
+ | **Runner** | A child process per turn with a one-turn credential. |
26
+ | **Harness** | Claude Code, Codex or OpenCode with its normal tools and your own settings. Nothing asks for permission mid-task. |
28
27
 
29
- > **An agent with host access acts on the device as the user account the Companion runs under, with no approval prompts.** Turn it on for an agent whose work needs the device, on a device where that reach is acceptable, and put the sandbox under it. An agent that reads untrusted input (public web, inbound mail, outside members) stays sandboxed, whatever the device.
28
+ > **An agent on this device reaches the whole device.** It runs the device's harness with its tools and the files there, and no approval prompts. Put agents on a device where that reach is acceptable. To confine them, set the harness's own sandbox, or isolate the Companion (see Isolation). An agent that reads untrusted input (public web, inbound mail, outside members) runs on a Companion that is isolated, whatever the device.
30
29
 
31
30
  **No account password or full-account token ever touches the device.** The setup code delivers a device token (`cabdev_…`) good only for pulling this device's assignments and reporting liveness. Each assigned agent gets its own workspace-bound credential, delivered once. Renaming, deactivating, removing a device and rotating its token happen in Cabane.
32
31
 
@@ -71,17 +70,17 @@ If `cabane` isn't found afterwards, open a new terminal, or run the `source` lin
71
70
 
72
71
  ## Commands
73
72
 
74
- | Command | What it does |
75
- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
76
- | `setup [CODE]` | With a code: pair or join, detect harnesses, start. Without: a re-runnable checklist that reports harnesses and starts what isn't running. |
77
- | `start [--foreground]` | Run in the background. Never asks. Unpaired, it names `setup`. `--foreground` stays attached, for a supervisor of your own. |
78
- | `stop` | Stop a running Companion cleanly. Ends any turn in progress. |
79
- | `status` | Version and install, the paired device by its Cabane name, its state in Cabane's words and when it last checked in, each harness's state, the sandbox posture, declared secret names, and local overrides. |
80
- | `update [--auto on\|off]` | Install the version Cabane names now; with `--auto`, set whether the Companion keeps itself up to date. |
81
- | `service install\|uninstall\|status` | Run as a systemd user service or a LaunchAgent that starts at boot and restarts if it stops; remove it; show its state. |
82
- | `logs [-n N] [-f]` | Recent log lines, or follow. |
83
- | `transcript [file] [--last] [-f]` | Read back a full agent turn, tool calls and results included. |
84
- | `logout [--purge]` | Remove the device token and cached agent credentials. Doesn't remove the device from Cabane. |
73
+ | Command | What it does |
74
+ | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
75
+ | `setup [CODE]` | With a code: pair or join, detect harnesses, start. Without: a re-runnable checklist that reports harnesses and starts what isn't running. |
76
+ | `start [--foreground]` | Run in the background. Never asks. Unpaired, it names `setup`. `--foreground` stays attached, for a supervisor of your own. |
77
+ | `stop` | Stop a running Companion cleanly. Ends any turn in progress. |
78
+ | `status` | Version and install, the paired device by its Cabane name, its state in Cabane's words and when it last checked in, each harness's state, where agents work, declared secret names, and local overrides. |
79
+ | `update [--auto on\|off]` | Install the version Cabane names now; with `--auto`, set whether the Companion keeps itself up to date. |
80
+ | `service install\|uninstall\|status` | Run as a systemd user service or a LaunchAgent that starts at boot and restarts if it stops; remove it; show its state. |
81
+ | `logs [-n N] [-f]` | Recent log lines, or follow. |
82
+ | `transcript [file] [--last] [-f]` | Read back a full agent turn, tool calls and results included. |
83
+ | `logout [--purge]` | Remove the device token and cached agent credentials. Doesn't remove the device from Cabane. |
85
84
 
86
85
  Anything else about a device is done in Cabane, on the device's page.
87
86
 
@@ -99,7 +98,8 @@ All of it in `~/.cabane/`, mode 600, no database. Cabane is the source of truth
99
98
 
100
99
  ```
101
100
  ~/.cabane/
102
- ├── config.json # pairing, local overrides, sandbox policy
101
+ ├── config.json # pairing, local overrides
102
+ ├── work/ # a directory per agent and conversation, for agents with no folder of their own
103
103
  ├── credentials.json # per-agent workspace-bound credentials, delivered once
104
104
  ├── secrets.json # { NAME: "value" } for ${NAME} placeholders
105
105
  ├── transcripts/ # one JSONL per turn, newest ~200 kept, local only
@@ -116,41 +116,22 @@ An MCP server configured in Cabane names its credential as `${NAME}`; the value
116
116
  `config.json` can carry, per agent (keyed by id, `@username` or `workspace-slug/username`) or for the whole device:
117
117
 
118
118
  - `cwd`: wins over the agent's **Starts in** folder set in Cabane.
119
- - `prepareHook`: a command that resolves the working directory before each turn; wins over both. A device-level hook applies to every agent that doesn't name its own.
119
+ - `prepareHook`: a command that resolves the working directory before each turn; wins over both. A device-level hook applies to every agent that doesn't name its own. The hook gets `CABANE_DEFAULT_CWD`, the directory below, for a hook that only adds to it.
120
+ - `root`: where agents work when nothing else says. Every agent gets a directory per conversation under it, `<root>/<workspace>/<agent>/<conversation>/`, made on the first turn and never removed by the Companion. Default `~/.cabane/work`; absolute, or `~/…`. An agent with no folder in Cabane, no `cwd` and no hook stands there; a folder or `cwd` that isn't an existing directory is passed over with a line in the log.
120
121
  - `harnesses`: an allowlist such as `["claude-code"]`; absent means all three.
121
122
  - `claudeCode.autoMemory`: Claude Code's local memory directory is off by default on every Companion, because notes belong in the workspace where others can read them. `true` hands it back.
122
123
 
123
- A malformed block fails `cabane start` with the field named.
124
-
125
- ### Sandbox
126
-
127
- ```jsonc
128
- {
129
- "runners": {
130
- "enabled": true,
131
- "sandbox": "os", // or "provider": "docker", where the container is the sandbox
132
- "policy": {
133
- "hosts": { "allow": ["gitlab.example.com"], "deny": [], "defaults": true },
134
- "filesystem": { "mode": "blocklist", "denyRead": [], "allowRead": [], "allowWrite": [] },
135
- "credentials": {
136
- "env": { "GH_TOKEN": { "secret": "${GITHUB_TOKEN}", "hosts": ["api.github.com"] } },
137
- "git": [{ "host": "github.com", "secret": "${GITHUB_TOKEN}" }],
138
- },
139
- },
140
- },
141
- }
142
- ```
124
+ A malformed block fails `cabane start` with the field named. A `runners` block or a row's `sandbox` from an older Companion is ignored, with one line in the log naming it.
143
125
 
144
- Every key under `policy` is optional. The defaults:
126
+ ### Isolation
145
127
 
146
- - **Network**: Cabane and the turn's model provider always; GitHub, npm, PyPI and crates.io by default; `allow` adds hosts, `deny` removes them and wins. Nothing else is reachable.
147
- - **Reads**: everything except known credential locations (`~/.ssh`, `~/.aws`, `~/.gnupg`, `~/.config/gh`, `~/.netrc`, `~/.npmrc`, `~/.kube`, cloud CLI configs, every Companion's own `~/.cabane` and the harness logins beside it) plus your `denyRead` globs. `.env` files are readable unless you deny them. `"mode": "strict"` hides the whole home except the working directory, the harness state and your `allowRead` list; name your toolchains there.
148
- - **Writes**: the working directory, the temp dir, the harness's own state, plus `allowWrite`.
149
- - **Credentials**: an entry sets the variable to a placeholder inside the sandbox and the proxy swaps the real value in only on requests to the hosts named. The harness never holds the token. A `git` entry does the same for HTTPS remotes (`~/.ssh` is unreadable, so use HTTPS).
128
+ The Companion does no sandboxing, and Cabane has no setting for it. Every agent runs the device's harness with its tools, settings and the files there. Confining agents is set in each harness's own file, and governs your own sessions there as well as every agent on the device:
150
129
 
151
- One agent can be excepted, keyed by its id: `"agents": { "<agent id>": { "sandbox": "none" } }`, for an ops agent whose job is the device itself. It works the other way too. The agent's row wins, then `runners.sandbox`. Cabane shows the exception on the device page and can't change it.
130
+ - **Claude Code**, `~/.claude/settings.json`: `"sandbox": { "enabled": true, "allowUnsandboxedCommands": false, "failIfUnavailable": true }`. Commands can then write only in the agent's folder, the temp folder and the paths you add, and reach only the hosts you allow; reads stay open unless you deny them, and Claude's own file tools obey permission `deny` rules, not the sandbox. Set `allowUnsandboxedCommands: false`: when a sandboxed command fails the model can retry it unsandboxed, and on a Cabane turn there is nobody to ask, so the retry would be approved. `failIfUnavailable` stops commands running unsandboxed when bubblewrap is missing.
131
+ - **Codex**, `~/.codex/config.toml` (or `$CODEX_HOME/config.toml`): set `sandbox_mode` and the Companion passes no sandbox flag, so the file governs; set nothing and turns run `danger-full-access`. `workspace-write` confines writes to the agent's folder, `/tmp` and your `writable_roots`, leaves reads open, turns command network off unless `[sandbox_workspace_write] network_access = true`, and runs each command in its own namespace, so a process it leaves running (a dev server, a background build) stops when the command returns. Docker doesn't work inside it.
132
+ - **OpenCode** is your own `opencode serve`.
152
133
 
153
- A policy the sandbox can't enforce, or a sandbox that fails to start for a turn holding credentials, refuses the turn rather than running it unsandboxed. On Linux the OS sandbox needs `bwrap`, `socat` and `rg` on the Companion's PATH. `cabane status` prints the effective posture.
134
+ For agents stricter than you, run the Companion as a second user account, with its own `~/.claude` and `~/.codex`, or run it somewhere isolated: a container, a VM, or a machine that exists for agents. Only that also covers what neither sandbox touches: MCP servers, hooks, the harness process and reads. Recipes for that follow.
154
135
 
155
136
  ## Debugging a turn
156
137
 
@@ -167,4 +148,4 @@ cabane logs -f # follow the log
167
148
 
168
149
  ## Not yet
169
150
 
170
- Windows has no service install and no self-update. There is no per-tool permissioning for an agent with host access: the sandbox is the boundary, and it's all or nothing per agent.
151
+ Windows has no service install and no self-update. There is no per-tool permissioning in Cabane: an agent on a device reaches the whole device, and the harness's own settings are the harness's.