# ouija

Ad-hoc collaboration between live Claude Code sessions, across machines.

## The idea

You're deep in a Claude Code session — it has rich context about your API, your architecture, your current problem. In another terminal, a different session has been working on the frontend for hours. Suddenly you need them to talk to each other.

Instead of copy-pasting context, re-explaining what each session knows, or setting up some orchestration layer — you just say "use ouija to ask web what the auth token format is." The message appears in the other session's input, it responds naturally, and the answer comes back. Both sessions get smarter on the spot.

You stay fully in control: these are live, interactive Claude Code terminals you can jump into anytime. This is **after-the-fact, ad-hoc collaboration** between independent sessions that were never designed to work together.

## What it looks like

Everything happens through natural language. You talk to your Claude session, it uses the ouija tools behind the scenes.

### Sending messages

> "Use ouija to ask api what port the auth service runs on"

Claude calls `session_send` and the message appears in the `api` session's input as:

```
[from web]: what port is the auth service on?
```

The `api` session responds naturally and sends the answer back:

```
[from api]: Auth service is on port 8443, configured in config/services.yaml
```

Your session tells you the answer. Neither session needed to be designed for this.

### Discovering sessions

> "Who's online on ouija?"

Claude calls `session_list` and reports:

```
web   — local, working on /Users/you/code/frontend (building login page)
api   — local, working on /Users/you/code/backend (fixing auth bug)
ios   — remote (macbook), working on /Users/you/code/app
```

### Cross-machine messaging

Sessions on remote daemons appear with a prefix:

> "Use ouija to ask macbook/ios what the bundle ID is"

The message travels over Nostr DMs to the other machine and gets injected into the `ios` session's tmux pane.

### Scheduling tasks

> "Set up an ouija task to remind api to check the error logs every morning at 9am"

Claude creates a cron task (`0 9 * * * UTC`). If the `api` session's pane is dead when the task fires, the daemon automatically revives it — creates a new tmux window, launches `claude --continue`, and re-registers the session.

> "Run that task now to test it"

Triggers the task immediately.

> "Set up a one-shot task to verify the deploy in 5 minutes"

Creates a task with `--once` that fires once then auto-deletes.

> "What ouija tasks are scheduled?"
> "Pause the morning-logs task"
> "Delete it"

All task management works through natural conversation.

### Unsupervised sessions

For sessions that receive messages from other agents or scheduled tasks, start them with `--dangerously-skip-permissions` so they can respond and use tools without manual approval:

```bash
tmux new-window "claude --dangerously-skip-permissions"
```

## How it's different

**Agent Teams / orchestrators** are designed upfront — you define who does what before anyone starts working. Collaboration is planned, not emergent.

**Headless agent frameworks** give you event-driven daemons you reach through chat apps, with state in markdown files. The AI runs in the background.

**Ouija** connects real, interactive Claude Code terminals after the fact. Sessions that were started independently — on different machines, hours apart — can collaborate on the fly. No orchestrator, no shared context, no setup. Think Slack for AI sessions, not a task queue.

## Quick start

```bash
cargo install ouija
```

Start the daemon and register the MCP:

```bash
ouija start &
claude mcp add --scope user --transport http ouija http://localhost:7880/mcp
```

Then open Claude Code in a tmux session (needed for message injection):

```bash
tmux new-session && claude
```

Sessions are auto-registered within a few seconds using the working directory as the name (e.g. a session in `/code/api` becomes `api`). To disable this, run `ouija config set auto_register false` (or toggle it in the admin dashboard). Start talking right away:

> "Use ouija to tell api to check the auth logs"

If you cloned the repo, `./setup.sh` handles all of the above automatically.

## Connecting two machines

On machine A:

```bash
ouija ticket
# prints: nprofile1qqsr...#a1b2c3d4e5f6...
```

On machine B:

```bash
ouija connect <ticket> --name macbook
```

Done. Sessions on both machines discover each other automatically. The ticket includes a connect secret — only nodes who present it are authorized.

## How it works

1. Each machine runs an **ouija daemon** (a small Rust binary)
2. Claude Code sessions connect via **MCP** and auto-register on startup
3. Local messages are delivered by **tmux injection** into the target pane
4. Remote messages travel as **Nostr NIP-17 private DMs** through relays — encrypted, decoupled, NAT-traversing
5. Nodes authenticate with a **connect secret** embedded in the ticket — unknown senders are rejected

## Security

- **Tickets are secrets.** A ticket contains the Nostr identity (nprofile) and a connect secret. Only share them out-of-band (copy/paste, not through Claude sessions).
- **Connect secret authentication.** Nodes must present the correct secret to be authorized. Unknown senders are rejected.
- **Encrypted transport.** Messages travel as NIP-17 gift-wrapped DMs (NIP-44 encryption, NIP-59 gift wrap). The relay cannot read message content.
- **Localhost only.** The daemon binds to `127.0.0.1` — the API and MCP endpoint are not exposed to the network.
- **Claude never sees tickets.** MCP tools only expose session IDs and messages. Pairing is a human/CLI/dashboard operation.
- **Regenerate anytime.** `ouija regenerate-ticket --yes` creates a new identity and secret, invalidating all existing node connections.

## CLI

The daemon is managed with a few commands:

```bash
ouija start     # start the daemon
ouija stop      # stop it
ouija update    # install latest from crates.io, restart
```

Run `ouija --help` for the full command list. Session management, messaging, node connections, scheduled tasks, and settings all have subcommands.

A live dashboard is available at `http://localhost:7880/admin`.

## Data

Stored in `~/.local/share/ouija/` (or `ouija start --data <path>`). Message metadata is logged to `messages.jsonl` for diagnostics (content is not logged).

## Testing

```bash
cargo test

# E2E (Docker)
docker build -f tests/e2e/Dockerfile -t ouija-test . && docker run --rm ouija-test

# Nostr P2P E2E (Docker Compose — includes relay + 4 daemons + auth tests)
docker compose -f tests/e2e/docker-compose.nostr.yml up --build --abort-on-container-exit
```
