巫女 (Miki the Monitor) — one dashboard for every Claude Code session you have open, with end-to-end encrypted remote control from your phone.
Install · Architecture · Self-host · Security
You run several Claude Code panels at once. One finishes; you don't notice. You walk away from your desk and can't peek. A teammate's machine has the context; you can't see it.
Miki-Moni hooks into every Claude Code panel on your machine and aggregates them into a single dashboard at http://127.0.0.1:8765. An optional encrypted relay lets a phone or second laptop see the same view and push prompts back.
- Aggregates, doesn't replace. Hooks sit alongside
claude— you keep starting sessions the way you already do. - One conversation, any device. Start on laptop A, keep prompting from your phone on the train, finish on laptop B at home — same session UUID, same transcript, same context. No copy-paste of where you were.
- Sessions outlive windows. Every session is resumable from any terminal by UUID.
miki claude -r <uuid>brings back full context even if the original panel was closed or crashed. - Local by default, remote when you opt in. The daemon binds
127.0.0.1only. Phone access flows through E2E-encrypted envelopes via a Cloudflare Worker that never holds keys.
npm install -g miki-moni
miki startFirst run launches a wizard that asks for language, relay mode (hosted / self-host / local-only), and prints a permanent pairing QR:
Scan the QR once on each device. The token is permanent until you miki pair --rotate. Dashboard is live at http://127.0.0.1:8765.
┌─ your machine ─────────────────────────────────────────────────────────┐
│ │
│ Claude Code (any panel) │
│ │ │
│ │ PS hooks (SessionStart / Stop / UserPromptSubmit / PreToolUse / │
│ │ PostToolUse) │
│ │ ── POST /event ──▶ │
│ │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │
│ │ │ miki-moni daemon (Node, 127.0.0.1:8765) │ │
│ │ │ ─ session store (better-sqlite3) │ │
│ │ │ ─ HTTP: /event /sessions /focus /send /wrap/* │ │
│ │ │ ─ WS: /ws (dashboard) /wrap (CLI) /ws_ext (ext) │ │
│ │ │ ─ RelayClient (X25519 + NaCl secretbox) │ │
│ │ └─────┬──────────────┬────────────────┬───────────┬───────┘ │
│ │ │ WS /ws │ WS /ws_ext │ WS /wrap │ relay │
│ ▼ ▼ ▼ ▼ │ envelope │
│ hooks web dashboard VSCode helper miki claude │ │
│ (Preact SPA) extension (wrap CLI) │ │
│ │ │
└──────────────────────────────────────────────────────────┼────────────┘
│
╭───────────────────▼──────────╮
│ Cloudflare Worker relay │
│ (zero-knowledge: opaque blobs│
│ only, never holds keys) │
╰───────────────────┬──────────╯
│ E2E encrypted
▼
╭──────────────────────────────╮
│ Phone PWA / 2nd laptop │
│ Ed25519 keypair in IndexedDB │
╰──────────────────────────────╯
| Component | Role |
|---|---|
| PS hooks | Posted by Claude Code to /event on every session/tool boundary so non-wrapped panels show up in the dashboard. |
| daemon | Node + express + ws + better-sqlite3. Holds session state and routes the four WS planes. |
| web dashboard | Preact + Tailwind SPA mounted at /. Reads /ws, posts /send and /focus. |
wrap CLI (miki claude) |
Wraps a Claude Code session so the daemon can push prompts (/send), switch model (/wrap/model), and resume by UUID. |
| VSCode helper extension | Connects to /ws_ext; receives claude-vscode.focus and pre-fills prompts into the active panel. |
| RelayClient | E2E-encrypts envelopes (X25519 ECDH per peer → NaCl secretbox) and ships them to the Worker. |
| Cloudflare Worker | Stateless relay. Routes opaque ciphertext between daemon and paired peers. Verifies Ed25519 signatures on daemon_id ‖ utc_minute. |
| Phone PWA | Web client served from Pages. Scans QR, holds an Ed25519 signing key in IndexedDB, talks to the relay. |
Full protocol details in docs/protocols/relay-protocol.md.
- Multi-session grid — every Claude Code panel on the machine, regardless of which VSCode window or terminal started it.
- Status counters that filter — click
5 activeto scope the grid; click again to clear. - New CLI popover — kick off a fresh
miki claude --freshin any folder; remembers recent cwds via a native picker so jumping into a new project is one click. - Live transcript in chat-bubble layout (user right, assistant/system/tool left). Toggle tool calls, limit slider (10 / 50 / 200 / all).
- WS status dot — green when receiving live updates, amber while reconnecting.
- Model chip — pop open to switch model live: default / Sonnet / Opus / Haiku / custom id. Broadcasts to every connected dashboard via
POST /wrap/model. - Mode chip with color —
acceptEditsblue,bypassred, plain ask grey. Locked for the session's lifetime. - Open CLI — spawn
wt.exerunningmiki claude -r <session-uuid>to take over a session from a terminal with full context. Works even if the original panel has been closed or crashed. - Send composer — multi-line input with auto-grow. Enter or Ctrl/⌘+Enter to send (your choice). Paste, drop, or pick image attachments.
- TTFT chart — Time-to-First-Token (ms) plotted per turn for every
miki claudewrapped session; track how fast Claude starts replying. - TPS chart — tokens (chars) per second during streaming; spot model or network degradation at a glance.
- Fleet average — dashed reference line shows the rolling 48 h average across all sessions so individual turns have context.
- Window selector — switch between 1 h / 6 h / 24 h / 48 h rolling views without reloading.
- Phone parity — the ⚡ Monit button in the phone app header shows the same charts via the encrypted relay proxy.
Note: TTFT/TPS data is only collected for sessions started with
miki claude(wrap mode). Hook-only sessions appear in the grid but do not generate performance metrics.
- Phone dashboard — same grid, single-column layout, scoped tap targets.
- Chat-bubble transcript matches the desktop, fits a phone viewport.
- Swipe-right-to-close session modals — document-level gesture with translateX preview.
- Composer with image-upload button (mobile file picker), textarea auto-grow, and iOS focus-zoom + keyboard-resize fixes.
- Collapsible transcript controls (show-tool / limit / load-all / reload) tucked behind one sliders popover.
| Hosted | Self-host | Local-only | |
|---|---|---|---|
| Setup | 0 sec | ~5 min wizard | 0 sec |
| Needs CF account | No | Yes | No |
| Phone access | Yes | Yes | No |
| Trust author's infra | Yes | No | N/A |
| Bandwidth ceiling | Author's CF free tier (~100k req/day) | Your CF free tier | N/A |
| Switch later | miki setup |
miki setup |
miki setup |
Default is Hosted, pointing at relay.f1telemetrystationpro.org. The wizard will deploy a Worker + Pages site to your own CF account if you pick Self-host.
The daemon binds 127.0.0.1 only — nothing on the public network can reach it. Phone access is end-to-end encrypted: X25519 ECDH at pair time, NaCl secretbox per envelope. Encryption keys never leave the daemon and the paired phone.
The daemon trusts any process running as your user to call /event, /send, /focus, and connect to /ws_ext. This keeps hooks and the helper extension token-free but means: anything that runs as your user can talk to the daemon. Treat ~/.miki-moni/ like ~/.ssh/.
| The phone can | The phone cannot |
|---|---|
| See live session state + transcript | Run arbitrary shell commands |
| Push prompts (pre-fill in VSCode; direct send to wrap CLI) | Auto-submit a prompt into VSCode without your keystroke |
| Focus an existing panel | Bypass Claude Code's per-tool permission prompts |
Everything between daemon and phone runs over WSS (TLS to the CF edge). Once the Worker terminates TLS, it sees:
| The relay (Worker) sees | The relay cannot see |
|---|---|
| Daemon's public keys (Ed25519 + X25519) | Message contents (every envelope is NaCl-secretbox-sealed end-to-end) |
| Pairing token at pair time (one-shot, then discarded) | Private keys (X25519 / Ed25519 stay on daemon and phone — IndexedDB on the phone) |
| Phone's public key + reconnect signatures | What you typed, what Claude said, transcripts, tool I/O |
| Metadata: who's paired with whom, connection times, envelope sizes, peer IDs |
For the hosted relay this metadata is visible to the operator (the author + Cloudflare). Self-host avoids that — you become the operator.
The phone client is a PWA served from Cloudflare Pages. A compromised relay operator could push a malicious bundle that exfiltrates the phone's X25519 private key from IndexedDB on first load. End-to-end encryption is only as trustworthy as the code running on both ends. Mitigations:
- Self-host — you control both the Worker and the Pages bundle.
- Pin the bundle — pair on a known-good day, then disable PWA updates (browser-dependent).
- Watch the source — the Pages bundle is reproducible from this repo at the tag matching the daemon version.
Risk table, hardening options, and the full hooks / extension trust analysis: docs/security/.
| Command | What it does |
|---|---|
miki start |
Run the daemon; first run launches the setup wizard. |
miki setup |
Re-run the wizard (change language, switch relay mode). |
miki pair |
Show the permanent QR + paired-phones list. |
miki pair --rotate |
Invalidate the current QR; already-paired phones keep working. |
miki claude [...args] |
Wrap a Claude Code session; auto-spawns the daemon if down. |
miki install-hooks |
Merge Claude Code hooks into ~/.claude/settings.json. |
miki --help for the full list. Verbose logs: MIKI_LOG_LEVEL=info miki start. Full trace always in ~/.miki-moni/miki-moni.log.
The setup wizard does this end-to-end; for manual deployment:
cd worker
wrangler login
wrangler deploy --config wrangler-selfhost.toml --name my-relay
wrangler pages project create my-phone --production-branch=main
wrangler pages deploy ../dist/web-phone --project-name my-phone --branch=mainThen point ~/.miki-moni/config.json at your endpoints:
{
"remote": {
"worker_url": "wss://my-relay.<your-cf-username>.workers.dev",
"phone_pwa_url": "https://my-phone.pages.dev/"
}
}git clone https://github.com/WarmBed/Miki-Moni
cd Miki-Moni
pnpm install
pnpm dev # tsx watch src/index.ts
pnpm test # daemon + worker test suites
pnpm typecheckSource tree: src/ daemon · web/ dashboard SPA · web-phone/ phone bootstrap · worker/ Cloudflare Worker · extension/ VSCode helper · hooks/ PS hook scripts · bin/miki.js CLI entry.
Branch model: main ships releases (current v0.3.18), dev carries active work with a package.json version bump on every change.
Happy (slopus/happy-cli) solves an overlapping itch from a different angle. Both can coexist on the same machine.
| Miki-Moni | Happy | |
|---|---|---|
| Entry point | Hooks into existing panels | Replaces claude |
| Phone client | Web PWA (no install) | Native iOS / Android |
| Multi-session dashboard | Yes — aggregated grid | Per-session |
| Supported agents | Claude Code | Claude Code, Codex, Gemini, ACP |
Use Happy for a polished mobile-first multi-agent experience. Use Miki-Moni if you live in VSCode and want a single dashboard for every parallel panel, with a relay you can self-host in minutes.
MIT — see LICENSE.
Built with Anthropic Claude via Claude Code.







