Files
vortex/README.md
T

160 lines
7.2 KiB
Markdown
Raw Normal View History

# Vortex — unified messaging terminal client
All your chat networks (WhatsApp, Telegram, Google Messages, X/Twitter, LinkedIn,
Discord, Instagram) in one terminal UI, built on Matrix + mautrix bridges, with an
MCP server that lets AI agents read your messages and stage drafts — but never
send without a human.
```
┌────────────┐ ┌─────────┐ ┌──────────────────────────────┐
│ Textual TUI│──▶│ Synapse │◀──│ mautrix bridges (7 networks) │
└─────┬──────┘ │ +Postgres│ └──────────────────────────────┘
│ drafts └────┬────┘
▼ SQLite │
┌────────────┐ │
│ MCP server │────────┘ read tools + draft staging for AI agents
└────────────┘
```
## Quickstart
```sh
cp .env.example .env # edit MATRIX_PASSWORD / POSTGRES_PASSWORD at minimum
make up # builds + provisions + starts everything
make tui # open the terminal client (already logged in)
make verify # smoke-test the whole stack
```
That is the entire setup. Bootstrap renders the Synapse config, creates
databases, generates bridge configs + appservice registrations, registers the
admin user, and writes credentials to a shared volume. Re-running `make up` is
a no-op. No YAML editing, no token copying.
## Make targets
| target | what |
|---|---|
| `make up` | build + start the full stack, wait for healthy |
| `make tui` | run the Textual TUI (interactive) |
| `make verify` | smoke tests: health, appservice round-trip, MCP draft gating |
| `make logs` | follow all logs |
| `make down` | stop containers |
| `make nuke` | stop and delete **all data** (volumes included) |
## Logging into bridges
Press `b` in the TUI for the bridge screen. It shows each bridge's login state
(via the bridge provisioning API). **Enter starts a guided login wizard**:
1. Pick a login method (the bridge advertises its flows: QR, pairing code,
phone number, browser cookies, ...).
2. The wizard walks each step — QR codes render right in the terminal,
text fields become prompts, and the wizard waits while you scan/confirm.
3. On success the bridge starts syncing your chats into the room list.
The wizard talks to each bridge's `/_matrix/provision/v3` API, authenticated
with your own Matrix access token — no extra secrets to configure.
| network | typical flows |
|---|---|
| WhatsApp | QR scan or phone pairing code |
| Google Messages | Google account / QR from the Messages app |
| Telegram | phone number + code. **Requires `TELEGRAM_API_ID`/`TELEGRAM_API_HASH` in `.env`** ([my.telegram.org](https://my.telegram.org)); without them the bridge is skipped. |
| X/Twitter | browser cookies (wizard prompts for each value) |
| LinkedIn | browser cookies |
| Discord | manual only — legacy bridge without the v3 provisioning API. Press `m` to open the bot DM and use `login qr` or `login token`. |
| Instagram | browser cookies (bridge runs in `instagram` mode) |
`m` on any bridge opens the bot DM for manual commands (`help`, `logout`,
relay settings, ...). Passkey/WebAuthn flows aren't supported in a terminal —
use the corresponding cookie or QR flow instead. Double puppeting is
pre-configured for all bridges — your own messages sent from other devices
appear as you.
## The TUI
Three panes: room list grouped by network (with `[wa] [tg] [gm] [tw] [li] [dc]
[ig] [mx]` labels and unread counts), message timeline, input bar.
Keys: `q` quit · `b` bridge manager · `d` drafts panel · `tab` cycle focus ·
`enter` (in room list) open room. Type `/reply <text>` to reply to the last
message. Media shows as download links.
The drafts panel lists drafts staged by AI agents via MCP (live, 2s poll):
`e` edit · `s` send · `x` discard. This is the human approval step.
Native run (no Docker): `pip install -e ./tui`, then
`MATRIX_HOMESERVER=http://localhost:8008 vortex-tui` (needs the shared volume
mounted or `CREDENTIALS_FILE`/`DRAFTS_DB`/`STORE_DIR` pointed somewhere useful).
## MCP server for AI agents
Runs at `http://localhost:8765/mcp` (streamable HTTP) and over stdio.
Tools: `list_rooms`, `read_messages`, `search_messages`, `create_draft`,
`list_drafts`, `update_draft`, `discard_draft`, `send_draft`.
`create_draft` never sends. `send_draft` is refused while `MCP_ALLOW_SEND=false`
(the default) — the agent is told a human must send from the TUI. Drafts appear
in the TUI drafts panel live.
Claude Code:
```sh
claude mcp add --transport http vortex http://localhost:8765/mcp
```
Claude Desktop (`claude_desktop_config.json`), HTTP transport:
```json
{ "mcpServers": { "vortex": { "url": "http://localhost:8765/mcp" } } }
```
stdio transport (spawns inside the running container):
```json
{ "mcpServers": { "vortex": {
"command": "docker",
"args": ["compose", "-f", "/absolute/path/to/vortex/docker-compose.yml",
"exec", "-i", "mcp", "vortex-mcp", "--stdio"] } } }
```
## Environment variables
See `.env.example` — every value is documented inline. Highlights:
- `BRIDGES_ENABLED` — comma-separated bridge list; compose starts exactly these.
After changing it, run `make down && make up`.
- `REGISTRATION_SHARED_SECRET` / `DOUBLEPUPPET_SHARED_SECRET` — leave as `auto`;
generated once and persisted in the data volume.
- `MCP_ALLOW_SEND` — set `true` only if you want AI agents to send without you.
## How provisioning works
`bootstrap` (one-shot, runs before Synapse) renders `homeserver.yaml` from
`synapse/homeserver.template.yaml`, generates a signing key and secrets, creates
one Postgres DB per bridge, patches each bridge's config from the pristine
upstream example in `bridges/templates/` (homeserver address, appservice
address, Postgres URI, admin permissions, double-puppet secret; Telegram API
creds; Instagram mode), and writes matching `registration.yaml` files for
Synapse *and* each bridge. `provision` (one-shot, after Synapse is healthy)
registers the admin user via the shared-secret admin API and writes
`credentials.json` to the shared volume; the TUI and MCP server log in from it
with their own devices. Everything is keyed on "file already exists" — re-runs
change nothing.
## Troubleshooting
- **`The as_token was not accepted` in a bridge log** — registration drift;
run `make down && make up` (bootstrap rewrites registrations from persisted
tokens). If it persists: `make nuke && make up`.
- **Telegram container prints "skipped"** — expected without
`TELEGRAM_API_ID`/`TELEGRAM_API_HASH`. Set them, then `make down && make up`.
- **Bridge bot doesn't answer** — `docker compose logs <bridge>`; the bridge
must show a successful websocket/appservice connection to Synapse.
- **TUI can't log in** — check `docker compose logs provision`; delete
`tui-store/session.json` in the shared volume to force a fresh login.
- **Changed `MATRIX_SERVER_NAME` after first boot** — Synapse server names are
permanent; `make nuke && make up`.
- **Ports busy** — Synapse uses host `8008`, MCP uses `MCP_HTTP_PORT` (8765).