6.7 KiB
name, description, version, author, license, platforms, metadata
| name | description | version | author | license | platforms | metadata | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hermes-dashboard | Deploy and extend the Hermes Dashboard — a browser control panel for managing config, models, tools, skills, cron jobs, and logs without the CLI. | 1.0.0 | Hermes Agent | MIT |
|
|
Hermes Dashboard
A Flask-based control panel that complements the Hermes Web UI (chat interface). While the Web UI provides chat + session browsing, the Dashboard provides management — config editing, model switching, tool toggling, skill browsing, cron job control, and log viewing.
Architecture Decision: No CLI Dependency
The dashboard reads Hermes state directly from the filesystem rather than shelling out to the hermes CLI. This avoids the venv shebang problem (host Python paths break in Docker containers) and keeps the image lightweight.
| Data | Source |
|---|---|
| Config | config.yaml (read/write via PyYAML) |
| Cron jobs | cron/jobs.json (read/write) |
| Skills | Filesystem scan of skills/ for SKILL.md |
| Logs | logs/gateway.log, logs/error.log (tail) |
| System status | systemctl, curl health checks |
Deployment
Directory Layout
~/docker/hermes-dashboard/
├── app.py # Flask backend (all routes)
├── templates/ # Jinja2 HTML (Bootstrap 5 dark theme)
│ ├── base.html # Nav + layout shell
│ ├── login.html # Password auth
│ ├── index.html # Overview with live status
│ ├── config.html # YAML editor + quick setter
│ ├── models.html # Model/provider switcher
│ ├── tools.html # Toolset enable/disable
│ ├── skills.html # Skill browser + installer
│ ├── cron.html # Cron job list + toggle/trigger
│ └── logs.html # Log viewer with auto-refresh
├── Dockerfile # python:3.11-slim + Flask + PyYAML
├── docker-compose.yml # Volume mounts ~/.hermes, port 5000, env_file
├── requirements.txt # flask, pyyaml, werkzeug
└── .env # DASHBOARD_PASSWORD, DASHBOARD_SECRET_KEY
Quick Deploy
# 1. Create the directory and files (see references/dashboard-app.py for full source)
mkdir -p ~/docker/hermes-dashboard/{templates,static}
# 2. Set password (generate hash with werkzeug)
python3 -c "from werkzeug.security import generate_password_hash; print(generate_password_hash('your-password'))"
# 3. Launch
cd ~/docker/hermes-dashboard
docker compose up -d --build
# 4. Add nginx reverse proxy (reuse existing certbot SSL cert)
# See existing nginx configs under /etc/nginx/sites-enabled/ for pattern
Nginx Config Pattern
server {
listen 3445 ssl; # Pick next available port
server_name your-domain.duckdns.org;
ssl_certificate /etc/letsencrypt/live/your-domain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8788;
proxy_set_header Host $host;
}
}
Cron Job Management
Cron jobs are read from ~/.hermes/cron/jobs.json. The JSON structure uses id (not job_id) as the primary key, with schedule_display for human-readable schedules and enabled as a boolean.
Toggle pause/resume by flipping enabled in jobs.json and writing it back. The scheduler picks up changes on next tick.
For "Run Now", write a trigger flag at ~/.hermes/cron/.trigger_<id> with a timestamp. The scheduler polls for these.
Design System
The UI uses a Linear/Vercel-inspired dark theme with these characteristics:
- Sidebar navigation (240px fixed, icons + labels, active state highlight)
- Glass-morphism cards with
#18181bsurface,#27272aborders, 12px radius - Stat cards with gradient top border accent (
#6366f1 → #a855f7) and pulse-animated status dots - Typography: Inter for UI, JetBrains Mono for code/logs
- No Bootstrap JS/CSS dependency — pure custom CSS with Bootstrap Icons
- Color tokens:
--accent: #6366f1(indigo),--green: #22c55e,--red: #ef4444,--amber: #f59e0b
Pitfalls
-
Password hash with
$signs in Docker env_file: Docker Compose interprets$as variable substitution. Useenv_file(not inlineenvironment:) and ensure the hash value is quoted or escaped. The dashboard reads the raw password fromDASHBOARD_PASSWORDand hashes it at startup. If login silently fails (returns 200 with login page, no redirect), the password hash is wrong. Verify with:docker exec hermes-dashboard python3 -c " from werkzeug.security import check_password_hash, generate_password_hash import os pw = os.environ['DASHBOARD_PASSWORD'] print('Password:', pw) print('Verify with fresh hash:', check_password_hash(generate_password_hash(pw), pw)) "Then test login directly:
curl -sk -c /tmp/cookies.txt -X POST https://localhost:3445/login \ -d "password=YOUR_PASSWORD" -w "%{http_code}" # Should return 302 (redirect to /), not 200 (login page) -
Session cookies break on restart: Flask
secret_keymust be stable. SetDASHBOARD_SECRET_KEYin.env(not auto-generated) so sessions survive container restarts. Without it,os.urandom(24).hex()generates a new secret each restart, invalidating all cookies. -
hermesCLI doesn't work in Docker: The venv shebang points to host Python paths. Don't try to runhermesCLI inside the container — read files directly instead. -
Toolsets may be list or dict in config.yaml: The
toolsetskey can be either format. When it's a list (e.g.['hermes-cli']), it's a per-platform override — NOT an exhaustive enabled-set. Treat all known tools as enabled by default when toolsets is a list. Only explicit dict entries withenabled: falseshould show as disabled. The view function must handle both formats, and the toggle endpoint must convert list→dict before writing. Always defineall_knownBEFORE the conversion block (Python NameError otherwise). -
Docker Compose
env_filevs inlineenvironment:: When usingenv_file: .env, Docker Compose reads the file directly without$substitution. Inlineenvironment:treats$as variable references. For bcrypt hashes (which contain$2b$...), always useenv_file. -
Container recreate needed for image changes:
docker restartkeeps the old image. Usedocker compose up -d --build --force-recreateto pick up code changes.
References
references/firecrawl-reddit-limitations.md— Firecrawl blocks Reddit; RSS workaround for WSBreferences/dashboard-app.py— Full app.py source (or use skill_view to read it)