- Go 54.6%
- HTML 20.7%
- CSS 13.6%
- Shell 9.7%
- Dockerfile 1.4%
manager.go: CapAdd, Devices, SecurityOpt beim Container-Create ergänzt. BUILD.md: Abschnitt FUSE-Capability-Fix dokumentiert. |
||
|---|---|---|
| container-files | ||
| docker | ||
| handlers | ||
| models | ||
| scripts | ||
| static | ||
| templates | ||
| .gitignore | ||
| BUILD.md | ||
| go.mod | ||
| go.sum | ||
| main.go | ||
| README.md | ||
Hermes SaaS
Web-Portal zur Verwaltung von Hermes-Zugängen für Freunde und Familie. Registrierung nur mit Einmal-Token → automatischer Docker-Container mit ttyd + Hermes. Lokale KI – kein Cloud-Anbieter, kein API-Key, alles auf eigener Hardware. Dark-Theme, Golden-Ratio-Typographie, Alpine.js.
Admin-Zugang
Der erste Admin (User admin) wird beim initialen Setup mit bcrypt-Hash angelegt.
Tokens erstellen auf /admin → „Token erstellen". Admin wird nach Login direkt auf /admin umgeleitet.
| Komponente | Wert |
|---|---|
| KI-Modell | Local (JL-Spark auf DGX Spark, kein Cloud-API) |
| Server | 192.168.99.76 |
| URL | https://portal.ki-tools.business |
| Port | 8000 |
| DB | $HOME/.hermes-saas/portal.db |
| Service | systemctl [start|stop|restart|status] hermes-saas |
| Binary | /opt/hermes-saas/hermes-saas |
| Templates | /opt/hermes-saas/templates/ |
| Static | /opt/hermes-saas/static/ |
| Repo | git.ki-tools.business/lewald/hermes-saas |
| Admin-Passwort | ki-tools-2026 (bcrypt) |
| Container-Daten | /opt/hermes-saas-data/<user>/ (Volume-Mount) |
Architektur
Caddy (10.200.200.1)
portal.ki-tools.business → 192.168.99.76:8000
Go-Backend (Port 8000)
├── /, /login, /register → Landing + Auth
├── /dashboard → Container-Status + Start/Stop + Terminal-Button
├── /admin → User + Container (unified) + Token-Verwaltung
├── /user/{id}/ → Reverse Proxy zu ttyd-Container (WebSocket)
└── /api/ → JSON-Endpunkte (Status, Admin-API)
Lokale KI-Modelle (DGX Spark / Spark2)
api.proxy → 192.168.99.36:8888 (JL-Spark, Custom-Provider)
Open Source Modelle, rein lokaler Betrieb
Docker-Container (pro User)
Name: hermes-user-{name}
Image: hermes-saas:latest
Port: 7682–7699 (ttyd)
Netz: kollegen-net (172.19.0.0/16, Bridge, kein LAN-Zugriff)
Vol.: /opt/hermes-saas-data/<user>/ → /etc/hermes-saas/
ExtraHosts: api.proxy, searxng.proxy
socat-Proxies (alpine/socat:latest)
api-proxy 172.19.0.1:36888 → spark1:8888 (JL-Spark API)
searxng-proxy 172.19.0.1:36808 → searxng:8080 (SearXNG API)
Beide auf Host-Netzwerk (kein Bridge), nur für kollegen-net erreichbar
Caddy (ssh.ki-tools.business)
portal.ki-tools.business → 192.168.99.76:8000 (Go-Portal)
ssh.ki-tools.business → 192.168.99.76:8000 (SSH-Terminal)
/user1-/user2- → auth → 192.168.99.76 (BasicAuth)
Features
- Lokale KI – Open-Source-Modelle auf eigener GPU, kein Cloud-Anbieter, keine API-Kosten
- Login/Registrierung mit bcrypt-Passwörtern, Session-Cookies (24h, httpOnly, Secure, SameSite=Lax)
- Einladungstoken (UUIDv4, once-only, max_uses=1)
- Container-Management per Docker-API (start/stop/restart, kein CLI)
- ttyd-Proxy inkl. WebSocket für interaktives Terminal, Basic-Auth-Injection (Auto-Login)
- Container-Volume – Sessions, Memory, Logs überleben Container-Neustart
- Wöchentliches Update –
hermes update -yläuft 1× pro Woche beim Container-Start - Admin-Redirect – Admin wird nach Login direkt auf
/adminumgeleitet - Rate-Limiting – 5 Login-Versuche/Minute/IP
- NULL-sichere Queries –
sql.NullStringfür nullable DB-Felder - Passwort-Generierung mit
crypto/rand.Readfür Container-Passwörter
Container-Image: hermes-saas:latest
Das Custom-Image baut auf ttyd-custom:latest auf.
| Komponente | Details |
|---|---|
| Basis | ttyd-custom:latest (ttyd 1.7.7 + openssh-client + sshpass auf Ubuntu 24.04) |
| Python | 3.12 (für Hermes Agent) |
| Node.js | 22.23 (NodeSource setup_22) |
| Hermes Agent | v0.18.x (aktuellste per hermes update) |
| MCP SearXNG | mcp-searxng v1.11.0 → 172.19.0.1:36808 |
| Persona | SOUL.md – Such-Priorität SearXNG-MCP, Date-Check, Skills-Hinweis |
| Preload-Skills | date-verification, weather-queries |
| ttyd | Port 7681, --yolo --tui, trap '' INT, copyOnSelect=true |
| Scrollbalken | Ausgeblendet via eigenem ttyd-index.html (-I Flag) |
| Basis-Config | /etc/hermes-saas.base/ → bei erstem Start ins Volume kopiert |
Pfad im Container: HERMES_HOME=/etc/hermes-saas
Erststart-Seed
Beim ersten Start eines Containers (leeres Volume):
- SOUL.md aus
/etc/hermes-saas.base/kopieren config.yamlmit MCP-SearXNG und Skill-Preload generierenhermes update -yfür neueste Version- ttyd starten mit
-I /etc/hermes-saas/ttyd-index.html
Volumes
Pro User ein Volume:
Host: /opt/hermes-saas-data/<user>/ → Container: /etc/hermes-saas/
Enthält: config.yaml, SOUL.md, ttyd-index.html, Sessions, Memory, Logs.
Wöchentliches Update
start-hermes.sh prüft Kalenderwoche via /etc/hermes-saas/.last_update.
Nur 1× pro Woche wird hermes update -y ausgeführt – non-interactive (kein hängen).
Container-Proxies (socat)
| Container | Image | Port | Ziel | Zweck |
|---|---|---|---|---|
api-proxy |
alpine/socat:latest |
172.19.0.1:36888 |
192.168.99.36:8888 (spark1) |
JL-Spark API für Hermes |
searxng-proxy |
alpine/socat:latest |
172.19.0.1:36808 |
192.168.99.8:8080 |
SearXNG Suche |
Keine eigenen Images – direkt alpine/socat:latest aus dem Docker-Hub.
socat lauscht auf der Bridge-Gateway-IP 172.19.0.1, Container in kollegen-net erreichen die Proxies über ExtraHosts.
Custom Images
Das Portal baut auf einem Basis-Image auf (separates Projekt/Repo):
| Image | Basis | Enthalten | Repo |
|---|---|---|---|
hermes-saas:latest |
ttyd-custom:latest |
Python 3.12, Node 22, Hermes Agent, mcp-searxng | dieses Repo |
ttyd-custom:latest (ttyd 1.7.7 + openssh-client + sshpass auf Ubuntu 24.04, ~231 MB)
wird in einem separaten Projekt gebaut und ist keine Datei in diesem Repo.
Projektstruktur
hermes-saas/
├── main.go # Router, Session-Middleware, Server-Start
├── handlers/
│ ├── auth.go # Login, Logout, Register, Rate-Limiting
│ ├── dashboard.go # Landing, Dashboard, ProxyToContainer
│ ├── admin.go # Admin-Seite (UserWithContainer-Struktur)
│ └── api.go # JSON-API (Container-Start/Stop, Status)
├── models/
│ ├── models.go # SQLite-Modelle (User, Session, Token, Container), bcrypt
│ └── tokens.go # Token-Generierung (UUIDv4)
├── docker/
│ └── manager.go # Docker-API-Manager via Unix Socket + Volume-Mount
├── templates/ # html/template (mit Alpine.js)
│ ├── base.html # Shell + Navigation (dynamischer Admin-Link)
│ ├── landing.html # Hero mit Icon
│ ├── login.html # Glaskarte
│ ├── register.html # Mit Passwort-Stärke
│ ├── dashboard.html # 3er-Grid, Status-Dots, Terminal-Button
│ └── admin.html # User+Container unified, Token-Grid
├── static/
│ └── style.css # Eigenes Design-System (kein Framework)
├── container-files/ # Build-Artefakte für das Docker-Image
│ ├── Dockerfile # FROM ttyd-custom -> hermes-saas:latest
│ ├── SOUL.md # Persona (SearXNG-Suche, Date-Check, Skills)
│ ├── start-hermes.sh # Entrypoint (Seed, Update, ttyd-Start)
│ └── ttyd-index.html # Custom index.html (Scrollbalken aus)
└── README.md
UI-Design
- Dark-Theme mit Glas-Effekten (backdrop-filter blur, border-glow)
- Animierter Grid-Hintergrund + radiale Glows
- Golden-Ratio-Typographie: 15px Basis → 13/15/20/24/32/44/60px, line-height 1.7
- Kein Tailwind/Framework – eigenes CSS (Zero-CDN, nur Google Fonts: Inter + JetBrains Mono)
- Navigation: Username dezenter Text (kein Button-Look) mit Admin-Link für Admins
- Terminal-Button öffnet
/user/{id}/im neuen Tab, Proxy injectt Basic-Auth (Auto-Login)
Technologie-Stack
| Komponente | Technologie |
|---|---|
| KI-Modell | Lokal (JL-Spark, Open Source, DGX Spark GPU) |
| Backend | Go (Single Binary, ~22 MB) |
| Datenbank | SQLite |
| Templates | html/template + Alpine.js 3.x |
| CSS | Eigenes Design-System |
| Docker | Docker-API via Unix Socket |
| Passwörter | bcrypt (golang.org/x/crypto/bcrypt) |
| Fonts | Inter (UI) + JetBrains Mono (Terminal) |
| Reverse Proxy | Caddy (SSL-Terminierung) |
| Container-Image | Ubuntu 24.04, Python 3.12, Node 22, Hermes Agent |
| Suche (MCP) | mcp-searxng (Node.js) via SearXNG-Proxy |
Umgebungsvariablen
| Variable | Zweck | Fallback |
|---|---|---|
HERMES_SAAS_PORT |
Server-Port | KOLPORT_PORT → 8000 |
HERMES_SAAS_DB_PATH |
SQLite-DB-Pfad | KOLPORT_DB_PATH → ~/.hermes-saas/portal.db |
Migration (2026-07-11)
- Projekt umbenannt von
kollegen-portal→hermes-saas - Datenbank umgezogen von
~/.kollegen-portal/portal.db→~/.hermes-saas/portal.db - Umgebungsvariablen:
KOLPORT_*wird als Fallback weiter unterstützt - Service-Env auf
HERMES_SAAS_PORTaktualisiert - Passwort-Hashing: bcrypt (vorher SHA256, Umstellung 2026-07-10)
- Image-Umstellung:
hermes-kollege:latest→hermes-saas:latest(2026-07-11) - Volumes pro User:
/opt/hermes-saas-data/<user>/(2026-07-11)
Image-Build (Server)
cd /tmp/hermes-saas-build
docker build -t hermes-saas:latest .
docker stop hermes-user-{user}; docker rm hermes-user-{user}
docker run -d \
--name hermes-user-{user} \
--network kollegen-net \
--add-host api.proxy:172.19.0.1 \
--add-host searxng.proxy:172.19.0.1 \
-e SEARXNG_URL=http://searxng.proxy:36808 \
-v /opt/hermes-saas-data/{user}:/etc/hermes-saas \
-p {port}:7681 \
hermes-saas:latest \
/start-hermes.sh user{id} {user} {password}