Files
hermes-config/skills/devops/hermes-dashboard/SKILL.md
T
2026-07-12 10:17:17 -04:00

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
linux
hermes
tags related_skills
hermes
dashboard
monitoring
control-panel
docker
hermes-webui
hermes-agent
server-health-check

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 #18181b surface, #27272a borders, 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. Use env_file (not inline environment:) and ensure the hash value is quoted or escaped. The dashboard reads the raw password from DASHBOARD_PASSWORD and 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_key must be stable. Set DASHBOARD_SECRET_KEY in .env (not auto-generated) so sessions survive container restarts. Without it, os.urandom(24).hex() generates a new secret each restart, invalidating all cookies.

  • hermes CLI doesn't work in Docker: The venv shebang points to host Python paths. Don't try to run hermes CLI inside the container — read files directly instead.

  • Toolsets may be list or dict in config.yaml: The toolsets key 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 with enabled: false should show as disabled. The view function must handle both formats, and the toggle endpoint must convert list→dict before writing. Always define all_known BEFORE the conversion block (Python NameError otherwise).

  • Docker Compose env_file vs inline environment:: When using env_file: .env, Docker Compose reads the file directly without $ substitution. Inline environment: treats $ as variable references. For bcrypt hashes (which contain $2b$...), always use env_file.

  • Container recreate needed for image changes: docker restart keeps the old image. Use docker compose up -d --build --force-recreate to pick up code changes.

References

  • references/firecrawl-reddit-limitations.md — Firecrawl blocks Reddit; RSS workaround for WSB
  • references/dashboard-app.py — Full app.py source (or use skill_view to read it)