5.5 KiB
Eagle Dashboard
Process supervisor and control panel for all Eagle local services.
URL: http://localhost:8880 (internal) · https://dashboard.qentra.top (external, token-required)
Source: ~/Developer/eagle-dash/
LaunchAgent: com.eagle.dashboard (single Login Item, KeepAlive=true)
Architecture
FastAPI + HTMX + Tailwind CDN + Alpine.js. No build step, CDN-only frontend.
~/Developer/eagle-dash/
├── main.py # FastAPI app, lifespan, SSE
├── auth.py # Origin-aware auth: 127.0.0.1 → free; cloudflared → token
├── supervisor.py # PID-file-based process supervisor (asyncio, setsid)
├── registry.py # Service registry + YAML loader
├── health.py # Health checks: http / port / process / launchctl
├── control.py # /restart /stop /start endpoints
├── memory.py # RSS tracking (main process + children)
├── loader.py # services.yaml loader
├── services.yaml # Service definitions (source of truth)
├── .env # EAGLE_TOKEN=<secret> (not committed)
├── templates/
│ └── index.html # Dashboard UI — 3 tabs: Services, Pages, Files
└── com.eagle.dashboard.plist
Auth
127.0.0.1direct access → no auth- Requests via cloudflared (have
X-Forwarded-For) → requireAuthorization: Bearer <EAGLE_TOKEN>oreagle_tokencookie (set on first login, 30-day expiry) - Token stored in
.env
Supervisor (supervisor.py)
PID-file-based — survives Dashboard restarts without crashing.
- Services launched with
start_new_session=True(setsid) → run in own session - PID written to
~/.eagle-dash/pids/<id>.pidimmediately on launch - On Dashboard restart: reads PID file → if process alive → adopts without re-launching
- If process dead → launches fresh
- Auto-restart with exponential backoff (2s → 60s max)
macOS integration
pfanchor:localhost:80→127.0.0.1:8880(setup-pf-redirect.sh)- cloudflared tunnel:
dashboard.qentra.top→localhost:8880 - FileBrowser:
http://127.0.0.1:8181(FB_NOAUTH=true, auth delegated to Dashboard)
Services
24 services in services.yaml, 10 with autostart: true. Секция Docker — сервисы, запускающиеся через docker-compose — отображается первой в дашборде.
Docker
| ID | Name | Access | Type |
|---|---|---|---|
| zulip | Zulip Connector | localhost | container |
| filebrowser | FileBrowser | localhost | container |
AI
| ID | Name | Access | Type |
|---|---|---|---|
| hermes | Hermes Eagle | localhost | daemon |
| hermes_whale | Hermes Whale | localhost | daemon |
| claude_proxy_node | Claude Proxy (node) | localhost | daemon |
| claude_proxy_python | Claude Proxy (python) | localhost | daemon |
| asana_mcp | Asana MCP | localhost | daemon |
| virfield | Virfield | localhost | daemon |
ML
| ID | Name | Access | Type |
|---|---|---|---|
| mlx_lm | MLX LM Server | lan (0.0.0.0:8080) | on-demand |
| ollama | Ollama | lan (0.0.0.0:11434) | on-demand |
Reflect
| ID | Name | Access | Type |
|---|---|---|---|
| reflect_proxy | Reflect Proxy | lan | daemon |
| reflect_fdroid | Reflect F-Droid Server | lan | daemon |
Monitoring
| ID | Name | Access | Type |
|---|---|---|---|
| aw_ddg | AW Watcher DDG | localhost | daemon |
| aw_xcode | AW Watcher Xcode | localhost | daemon |
Orchestration
| ID | Name | Access | Type |
|---|---|---|---|
| wiki_ingest | Wiki Ingest | localhost | cron |
Remaining LaunchAgents (not under Dashboard):
com.colima.start— Homebrew managedhomebrew.mxcl.postgresql@17— Homebrew managedpersonal.os.wiki-ingest— cron schedulecom.personalos.heartbeat— cron schedule
Network Access Badges
- 🟢
localhost— bound to 127.0.0.1, safe - 🟡
lan— bound to 0.0.0.0, accessible on home network (mlx_lm, ollama) - 🔵
tunnel— cloudflared-managed, token-protected
Tabs
Services — health cards with: status badge, PID, uptime, memory (RSS + children), start/stop/restart controls, log tail drawer (last 200 lines).
Pages — URL input + iframe for quick local page testing.
Files — FileBrowser iframe at http://localhost:8181.
Deployment
cd ~/Developer/eagle-dash
# Install deps
uv sync
# Generate token
python3 -c "import secrets; print('EAGLE_TOKEN='+secrets.token_hex(32))" > .env
# Setup pf redirect (localhost:80 → :8880)
sudo bash setup-pf-redirect.sh
# Load LaunchAgent
launchctl load ~/Library/LaunchAgents/com.eagle.dashboard.plist
Troubleshooting
Services show running: null — check supervisor.supervisor_status field in /api/services (not top-level running). The supervisor status is in the nested supervisor object.
PID files stale — ls ~/.eagle-dash/pids/ and compare with ps. Supervisor adopts automatically on next poll.
Ghost launchctl entries — after removing plists, use launchctl remove <label> (not bootout) to clear bootstrap session entries.
Virfield port conflict — if previously a LaunchAgent (plist deleted but still loaded), stop it: launchctl stop com.virfield.server && launchctl remove com.virfield.server.
History
- 2026-06-04: Initial build. Migrated 14 LaunchAgent plist services under Dashboard supervisor. Single
com.eagle.dashboardLogin Item. cloudflared tunnel atdashboard.qentra.top. Ghost entries cleaned vialaunchctl remove.