See your cluster. Run your models.
Web dashboard for exo — the open-source distributed Apple Silicon inference framework.
Try it live: exoscopy.net (guest mode, no signup — Chat + Help only, token-limited)
- Chat — stream responses with presets, file attachments, multi-turn editing, thinking mode, system prompts, stats, code block save
- Multimodal inference — attach images or PDFs and use vision-capable models (Gemma 4, Qwen3-VL, Kimi K2.5). PDF text + page rasterization happens client-side, fully offline. Requires exo v1.0.70+.
- Mobile + PWA — responsive layout at
<768px(same URL, no separate build). Chat-focused interface with conversations drawer, 5 essential settings only. Installable as a PWA via "Add to Home Screen" → full-screen app experience on iOS/Android. - Dashboard — model matrix across nodes, load/unload, sync via rsync, delete, cluster monitoring (RAM, GPU, temp, SSD)
- Downloads — search HuggingFace (exo qualified MLX models), smart cross-filters, distributed download (auto-rsync to all nodes)
- Settings — node discovery, SSH key setup, config check (8 dependencies per node), endpoint test
- Multi-user — administrator mode with login, per-user conversations, role-based access (admin/user/guest)
- Guest mode — token-limited guest access for visitors, no login required
- Self-contained — all UI dependencies (React, Tailwind, PDF parser) bundled in the Docker image. No CDN calls at runtime, works on air-gapped networks.
- Runs anywhere — Mac, Linux, Raspberry Pi 5, NAS. The dashboard host doesn't need to be beefy — our demo at exoscopy.net runs on a Raspberry Pi 5, piloting a Mac Studio cluster over LAN.
- exo installed and running — github.com/exo-explore/exo
- SSH enabled — macOS: System Settings → General → Sharing → Remote Login → ON
- Python 3 installed (comes with macOS)
- huggingface_hub on the primary node (for HF downloads):
pip3 install huggingface_hub
- rsync installed (comes with macOS)
- Docker (recommended) or Node.js 20+
- Network access to your cluster nodes on:
- Port 52415 (exo API)
- Port 22 (SSH — for sync, delete, metrics)
docker run -d --name exoscopy \
-p 3456:3456 \
-v ~/.ssh:/root/.ssh \
-v exoscopy-data:/app/data \
--restart unless-stopped \
ghcr.io/sofille65/exoscopy:latest
~/.sshmount — gives ExoScopy access to your SSH keys for node management (sync, metrics, delete).exoscopy-datavolume — persists settings, conversations, users, and download history across container restarts and updates.
git clone https://github.com/Sofille65/exoscopy.git
cd exoscopy
docker compose up -dThe docker-compose.yml pulls from GHCR automatically. To build locally instead:
docker compose up -d --buildgit clone https://github.com/Sofille65/exoscopy.git
cd exoscopy
npm install
node server/index.jsNote:
sshpassandrsyncmust be installed on the host for SSH key setup and model sync. macOS:brew install hudochenkov/sshpass/sshpassLinux:apt install sshpass rsync
ExoScopy runs on http://localhost:3456
- Open http://localhost:3456 (or your server IP)
- Go to Settings
- Click Re-discover to scan your LAN for exo nodes, or add nodes manually (name + IP)
- Set the EXO Endpoint to your master node IP (port 52415)
- Click Test to verify the exo API is reachable
- Enter the SSH password for each node → click Setup SSH Keys (one-time — installs keys for sync)
- Click Check Config to verify all dependencies per node (green = OK)
- Go to Chat → your installed models appear in the dropdown
- Start chatting!
ExoScopy supports optional multi-user access for shared LAN deployments.
- Go to Settings → Administrator Mode
- Set an admin password → Enable
- You're automatically logged in as admin
| Admin | User | Guest | |
|---|---|---|---|
| Chat | Own conversations | Own conversations | Shared (token-limited) |
| Dashboard | Full access | Read-only | No access |
| Downloads | Full access | No access | No access |
| Settings | Full access + user management | No access | No access |
When enabled (Settings → Guest Access), visitors can chat without logging in:
- Token limit per session (default: 50,000 tokens)
- Chat-only access
- "Try as Guest" button on login screen
- Toggle off to cut all guest access instantly
Settings shows a live connection log with timestamp, username, action, and IP for every login, logout, guest session, and failed attempt.
ExoScopy adapts to mobile viewports (<768px) automatically — same URL, no separate build, no app store. Open http://<your-host>:3456 on your phone and the layout switches:
- Chat is the primary screen. Header is fixed (burger · ExoScopy · settings gear). Conversations open as a slide-in drawer.
- Model picker sits on a dedicated row below the source toggle and inference preset — nothing gets truncated on iPhone 14/15 widths.
- Settings are stripped to what makes sense on mobile: Accessibility, Administrator Mode, Guest Access, Connection Log, Users. Node management and cluster admin are desktop-only.
- Dashboard & Downloads — desktop-only (these need the real estate and aren't useful from a phone).
The app ships with a manifest.json + Apple meta tags. On iOS Safari or Android Chrome:
- Tap Share → Add to Home Screen
- Launch from the home icon → full-screen, no browser chrome
- Icon uses the ExoScopy brand yellow (
#ffde00)
No app store, no install flow, just a web shortcut that feels native.
A native SwiftUI/Android app would need two codebases, App Store review cycles, and runs on top of the same HTTP API anyway. Responsive web + PWA gives you 90% of the native feel for 10% of the work and supports every phone. If you want tight iOS-only integrations later (Siri, Shortcuts, Watch), a native companion can be added separately without touching the web app.
docker pull ghcr.io/sofille65/exoscopy:latest
docker stop exoscopy && docker rm exoscopy
docker run -d --name exoscopy \
-p 3456:3456 \
-v ~/.ssh:/root/.ssh \
-v exoscopy-data:/app/data \
--restart unless-stopped \
ghcr.io/sofille65/exoscopy:latestYour settings, conversations, users, and downloads are preserved in the exoscopy-data volume.
Note: After updating, SSH keys inside the container are regenerated. Re-run Setup SSH Keys in Settings to restore node connectivity.
WARNING: Never run
docker volume rm exoscopy-data— this permanently deletes all your data. The volume is safe throughdocker stop,docker rm, anddocker pull— onlydocker volume rmdestroys it.
cd exoscopy
git pull
docker compose up -d --pull alwayscd exoscopy
git pull
npm install
# restart the serverBrowser → ExoScopy (:3456) → exo cluster (:52415)
↕ SSH
Node management (sync, delete, metrics)
| Component | Tech |
|---|---|
| Backend | Node.js 20 + Express + Socket.IO |
| Frontend | React 18 + Tailwind + Babel (all self-hosted in public/vendor/) |
| pdf.js 3.11.174 (client-side text extraction + rasterization) | |
| Auth | bcryptjs + cookie-session (signed cookies, no server-side store) |
| Build | None — single index.html, no webpack/vite |
| Mobile | Responsive <768px + PWA (manifest.json, Apple touch icon, theme-color, viewport-fit=cover) |
| Persistence | JSON files in data/ (Docker volume) |
| Node comms | HTTP (exo API) + SSH (rsync, metrics, delete) |
| Docker | node:20-alpine + openssh + sshpass + rsync |
All frontend libraries are bundled in the Docker image under public/vendor/ (~5 MB total). ExoScopy makes zero external network calls at runtime — safe for air-gapped or locked-down networks.
data/
settings.json — cluster config, admin/guest mode toggles
users.json — user accounts (username, passwordHash, role)
auth-log.json — connection log (last 500 events)
session-secret.txt — cookie signing key (auto-generated)
conversations.json — shared conversations (admin mode OFF)
downloads.json — download queue and history
users/
admin/
conversations.json — admin's conversations (admin mode ON)
alice/
conversations.json — per-user conversations
| Problem | Solution |
|---|---|
| Nodes show "0/N online" | Check that exo is running on your nodes and port 52415 is reachable |
| SSH keys fail | Make sure Remote Login is enabled on each Mac (System Settings → Sharing) |
| Download fails | Run Check Config in Settings — verify Python 3 and huggingface_hub on primary node |
| Models don't appear after sync | exo takes time to re-scan models — wait ~30s or restart exo |
| TTFT very slow (30-50s) | Qwen3.5 thinking mode — make sure Thinking is OFF in Inference Settings |
| White screen | Check browser console for errors — try hard refresh (Cmd+Shift+R) |
| 401 after update | Admin mode is ON — log in, or disable admin mode via Settings |
| SSH keys lost after update | Normal — re-run Setup SSH Keys in Settings after every container update |
| "Current model doesn't support vision" warning | Select a vision-capable model (👁 in dropdown) — Gemma 4, Qwen3-VL, Kimi K2.5 — or remove the image/PDF attachment |
| PDF stuck on "Processing…" | Browser is extracting text and rasterizing pages. Expect ~500ms per page. Encrypted PDFs are not supported. |
| Vision models not appearing | Update exo to v1.0.70+ on your cluster; models need capabilities: ["vision"] in their manifest |
MIT