parley-cli
v0.2.1
Published
Agent-to-agent relay with human approval gates. E2E-encrypted rooms over a Cloudflare Durable Object relay; every inbound request and outbound response is approved by a human.
Maintainers
Readme
parley
parley is a Cloudflare Worker + Durable Object relay and a Bun CLI for encrypted request/response messages between agents. The relay only sees routing metadata and ciphertext. The receiving human approves the inbound request before anything runs, then approves the output before it leaves their machine.
Quick Start
Deploy your relay:
cd relay
bun install
wrangler deployInitialize your local identity and room:
cd ../cli
bun install
bun src/main.ts init --name rhys --relay wss://parley-relay.<subdomain>.workers.dev --label dave-and-meSend the printed parley://... invite link out of band. Start your gated responder:
bun src/main.ts serve --webAsk your friend’s agent:
bun src/main.ts ask "Can you check your local config?" --to daveBackground Mode (Recommended)
On macOS, install the responder once and leave it in the background:
parley daemon installThe daemon runs parley serve --web with a local browser approval UI on 127.0.0.1. When a request arrives, parley sends a macOS notification. Click the notification to open the browser UI, or run:
parley daemon openApprove or decline the inbound request in the browser. If approved, the handler runs in the background; when it finishes, approve, edit, or withhold the response before it leaves your machine.
Useful daemon commands:
parley daemon status
parley daemon uninstall
parley daemon install --web-port 4917For source checkouts, the equivalent foreground command is:
bun src/main.ts serve --web --port 4917It prints the local UI URL at startup. Plain terminal approval remains available as a fallback:
bun src/main.ts serveMulti-User Rooms
A room is a group. Everyone who has the invite link shares the same room key and can read all room traffic addressed to the room, including broadcast requests. Use separate rooms when you want pairwise privacy.
Run serve with no --room to serve every room in your config at once. Pass --room <label> to restrict it to one room. Requests from all served rooms go through one sequential approval queue, and the approval frame shows the room label.
Peer names are self-asserted labels. If a second connection joins a room with the same peer name, the relay closes the old socket with code 4001 and keeps the newer connection, so each peer name has at most one live socket per room.
Broadcast asks use --to *, which is also the default when --to is omitted. Every online peer in the room receives the request, and offline peers known to the relay get it queued for the same 7-day window as targeted requests. Every receiving human sees the approval prompt; the first response for that request id wins and later responses are ignored.
Onboard Your Friend
Ask your friend to install Bun, clone this repo, and install the CLI dev files:
cd cli
bun installThen have them join with the invite link:
bun src/main.ts join 'parley://<relay-host>/<room-key>' --name dave --label rhys-and-meThey can set a handler by editing ~/.parley/config.json. The default is:
claude -p --output-format textAny shell command works as long as it reads the approved prompt on stdin and writes the response on stdout/stderr. Then they run the background responder:
parley daemon installIf they prefer a foreground process, bun src/main.ts serve --web opens the same local browser approval UI, and bun src/main.ts serve uses terminal prompts.
For scripted tests only, serve supports hidden --yes and --no flags to auto-approve or auto-decline gates.
Security Model
The invite link contains a 32-byte room key. Possession of that link is membership in the room, so share it only through a trusted out-of-band channel such as Signal.
The CLI derives three values from the room key with HKDF-SHA256:
roomId, a public-ish routing id the relay sees.authKey, used for the opaque room proof and peer auth headers.msgKey, used for AES-256-GCM message encryption.
Request and response payloads are end-to-end encrypted. The relay can see the room id, peer labels, timestamps, target labels, message ids, and ciphertext sizes. It cannot read prompts or responses, and it cannot derive the room key.
There are two approval gates on the receiving machine:
- The inbound prompt is shown verbatim before the handler runs.
- The captured handler output is shown before it is sent back.
If serve exits while a request is being handled, unanswered requests are not redelivered in v1. The asker waits until its timeout.
Prompt-Injection Warning
The inbound prompt is untrusted input. Gate 1 shows it verbatim before anything runs. The handler command should run an agent in its normal permission-prompting mode. Do not use --dangerously-skip-permissions or an equivalent bypass for a parley handler.
Development
Run local relay:
bun run --cwd relay devRun typechecks:
bun run typecheckRun the e2e verification:
bun run test