167 lines
5.5 KiB
Markdown
167 lines
5.5 KiB
Markdown
# 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.1` direct access → no auth
|
|
- Requests via cloudflared (have `X-Forwarded-For`) → require `Authorization: Bearer <EAGLE_TOKEN>`
|
|
or `eagle_token` cookie (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>.pid` immediately 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
|
|
|
|
- `pf` anchor: `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 managed
|
|
- `homebrew.mxcl.postgresql@17` — Homebrew managed
|
|
- `personal.os.wiki-ingest` — cron schedule
|
|
- `com.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
|
|
|
|
```bash
|
|
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.dashboard` Login Item. cloudflared tunnel at `dashboard.qentra.top`. Ghost entries cleaned via `launchctl remove`.
|