From c45d1967028e2656b81b779714d117f0c5ea9e6d Mon Sep 17 00:00:00 2001 From: mikael-lovqvists-claude-agent Date: Sat, 7 Mar 2026 21:00:22 +0000 Subject: [PATCH] Rewrite README to reflect current state Covers keygen workflow, auth model, all three binaries, env vars, action registry, path resolution, and security notes. Co-Authored-By: Claude Sonnet 4.6 --- README.md | 182 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 139 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 5460f95..b0c67ec 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,25 @@ # claude-code-conduit -A supervised action bridge between Claude Code and the host system. +A supervised action bridge between Claude Code and the host system. Claude requests structured actions; the server applies per-action policies and optionally holds them for human approval before executing. -Claude requests structured actions. The server applies per-action policies: -- **auto-accept** — executed immediately (e.g. open a file in editor) -- **auto-deny** — rejected immediately -- **queue** — held for user approval (e.g. open a browser URL) +## Concepts + +**Actions** are typed verbs with named parameters — not shell commands. The server defines what actions exist and what happens when they are called. Example: + +```json +{ "action": "edit-file", "filename": "/workspace/foo.mjs" } +``` + +**Policies** control what happens when an action is requested: +- `auto-accept` — executed immediately (e.g. open a file in the editor) +- `auto-deny` — rejected immediately +- `queue` — held for human approval (e.g. open a browser URL) + +**Authentication** uses HMAC-SHA256. Every request is signed with the caller's secret. Secrets live in a JSON file — never in environment variables. + +**Users** each have a secret and a `canApprove` list controlling whose queued actions they may approve. + +--- ## Setup @@ -13,54 +27,136 @@ Claude requests structured actions. The server applies per-action policies: npm install ``` -## Running the server +### Generate secrets ```bash -node server/index.js -# or -CONDUIT_PORT=3333 CONDUIT_ROOT=/workspace node server/index.js +# Create a secrets file with random secrets for each user +ccc-keygen --create user,agent + +# Edit secrets.json to configure who can approve whom: +# set user.canApprove = ["agent"] + +# Produce a filtered file for the agent (e.g. to copy into a Docker container) +ccc-keygen --filter agent --output agent-secrets.json ``` -## Using the CLI client +The full `secrets.json` stays on the host. `agent-secrets.json` goes into the container. + +--- + +## Running + +### Server (host) ```bash -# List available actions -node client/index.js list-actions - -# Open a file in the editor (auto-accepted) -node client/index.js edit-file filename=/workspace/myfile.js - -# Open a URL (queued for user approval) -node client/index.js open-browser url=https://example.com +ccc-server --secrets secrets.json ``` -When a queued action is submitted, the server prints the approve/deny URLs to stdout: - -``` -[QUEUE] New request #a1b2c3d4 - Action: open-browser - Params: {"url":"https://example.com"} - Approve: POST /queue/a1b2c3d4.../approve - Deny: POST /queue/a1b2c3d4.../deny -``` - -User approves via: -```bash -curl -X POST http://localhost:3333/queue//approve -``` - -## Environment variables - +Environment variables: | Variable | Default | Description | |----------|---------|-------------| -| `CONDUIT_PORT` | `3333` | Server port | +| `CONDUIT_PORT` | `3015` | Port to listen on | | `CONDUIT_ROOT` | `/workspace` | Workspace root for path resolution | -| `CONDUIT_URL` | `http://localhost:3333` | Server URL (client-side) | -## Adding actions +### Client (container / agent) -Edit `server/actions.js`. Each action needs: -- `description` — shown in list-actions -- `params` — array of `{ name, required, type }` -- `policy` — `"auto-accept"` | `"auto-deny"` | `"queue"` -- `handler(params, helpers)` — async function that performs the action +```bash +ccc-client --secrets agent-secrets.json --user agent '{"action": "list-actions"}' +ccc-client --secrets agent-secrets.json --user agent '{"action": "edit-file", "filename": "/workspace/foo.mjs"}' +``` + +`--secrets` and `--user` can also be set via environment variables: + +```bash +export CCC_SECRETS=/path/to/agent-secrets.json +export CCC_USER=agent +ccc-client '{"action": "list-actions"}' +``` + +The JSON payload can be spread across multiple arguments — they are space-joined before parsing: + +```bash +ccc-client '{"action": "edit-file",' '"filename": "/workspace/foo.mjs"}' +``` + +### Queue manager (host) + +```bash +ccc-queue --secrets secrets.json --user user +``` + +Opens an interactive TUI showing pending actions: + +``` +┌─ Pending Actions ──────────┐ ┌─ Details ───────────────────────────┐ +│ │ │ │ +│ > [a1b2c3] open-browser │ │ Action: open-browser │ +│ [d4e5f6] open-terminal │ │ ID: a1b2c3d4-... │ +│ │ │ Submitted by: agent │ +│ │ │ Created: 2026-03-07T12:00:00Z │ +│ │ │ │ +│ │ │ Params: │ +│ │ │ url: https://example.com │ +└─────────────────────────────┘ └──────────────────────────────────────┘ + [y] approve [n] deny [r] refresh [q] quit +``` + +Supports `CCC_SECRETS` and `CCC_USER` env vars the same as the client. + +--- + +## Actions + +Query available actions at runtime: + +```bash +ccc-client '{"action": "list-actions"}' +``` + +Built-in actions: + +| Action | Policy | Params | +|--------|--------|--------| +| `list-actions` | auto-accept | — | +| `edit-file` | auto-accept | `filename` (path) | +| `open-browser` | queue | `url` (http/https only) | +| `open-terminal` | queue | `path` (optional) | + +### Adding actions + +Edit `server/actions.mjs`. Each entry needs: + +```js +'my-action': { + description: 'What this does', + params: [{ name: 'foo', required: true, type: 'string' }], + policy: 'auto-accept', // or 'auto-deny' | 'queue' + handler: ({ foo }) => { + // do something + return { result: foo }; + }, +}, +``` + +--- + +## Path resolution + +The server translates container-side paths to host-side paths using the volume map in `server/helpers.mjs`. By default this matches the `docker-compose.yml` layout: + +| Container path | Host path | +|----------------|-----------| +| `/workspace` | `/workspace` | +| `/home/claude` | `/claude-home` | + +Paths outside known volumes are rejected. Edit `CONTAINER_PATH` and `VOLUME_MAPPING` in `server/helpers.mjs` to match your setup. + +--- + +## Security notes + +- Secrets are never passed via environment variables or command line arguments — only via a file +- HMAC signatures include a timestamp; requests older than 30 seconds are rejected +- `canApprove` is empty by default — permissions must be explicitly granted +- Browser URLs are validated to `http`/`https` only before being passed to `xdg-open` +- All path arguments are resolved against the volume map; traversal outside known volumes is rejected