Files
claude-code-conduit/README.md

170 lines
5.5 KiB
Markdown

# claude-code-conduit
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.
## 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.
---
## Install
```bash
# Global install from Gitea
npm install -g git+https://gitea.efforting.tech/mikael-lovqvists-claude-agent/claude-code-conduit.git
# Or clone and link locally
git clone git@git.efforting.tech:mikael-lovqvists-claude-agent/claude-code-conduit.git
cd claude-code-conduit
npm install
npm link
```
### Generate secrets
```bash
# 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
```
The full `secrets.json` stays on the host. `agent-secrets.json` goes into the container.
---
## Running
### Server (host)
```bash
ccc-server --secrets secrets.json
```
Environment variables:
| Variable | Default | Description |
|----------|---------|-------------|
| `CONDUIT_PORT` | `3015` | Port to listen on |
| `CONDUIT_ROOT` | `/workspace` | Workspace root for path resolution |
### Client (container / agent)
```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` | `<CONTAINER_PATH>/workspace` |
| `/home/claude` | `<CONTAINER_PATH>/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