4.6 KiB
4.6 KiB
Hermes Hub — Déploiement et Exploitation Multi-Univers sur VPS
Instance auteur : gemini / nabil
Date : 2026-08-19
Tags : hermes, hub, multi-univers, vps, tailscale, cloudflare-access, fastapi, filebrowser, dsh
Statut : valide
Problème
Nabil dispose de 4 instances Hermes strictement cloisonnées :
- TT (
hermes-tt, port 3010, NAS Synology) — Achats Zone Sud, Tunisie Telecom - Nyora (
hermes-nyora, port 3020, NAS Synology) — Venture perso, Dr Nexum - Perso (
hermes-perso, port 3031, NAS Synology) — Famille, personnel, santé - Nabil (
hermes-nabil, local loopback, VPS Contabo) — Master agent & DeepSeek Harness (DSH)
L'objectif est d'offrir une interface web unifiée sur le VPS Contabo avec :
- Un Workspace Switcher latéral fluide et étanche entre les 4 univers.
- Un Sélecteur Toggle DSH (conception Claude Design) pour basculer instantanément entre la session de dialogue et l'explorateur de fichiers DSH (
/u/nabil/files/). - L'absence totale de fuite de contexte, de mémoire JS ou de cookies de session entre univers.
- La sécurisation intégrale via Cloudflare Access (OTP Email + 2FA) et maillage Tailscale sans exposition de port WAN sur le NAS ni d'IP publique directe sur le VPS.
Contexte et contraintes
- Réseau : Communication exclusive via le maillage Tailscale (
100.86.197.88NAS ↔100.94.90.119VPS). - Port Hermes Perso : Port 3031 fixé définitivement (port 3030 bloqué et purgé).
- Sécurité & Auth : Authentification Cloudflare Access en amont du tunnel Cloudflare (
hub.yesminedor.tn). - Filebrowser DSH : Image épinglée en SemVer
filebrowser/filebrowser:v2.32.0(jamais:latest), lancée avec--noauth(délégué à Cloudflare Access) et thème sombre (--branding.theme=dark). - Montage DSH : Montage Read-Write strict sur
/home/dsh-agent/dsh-vps/workspacevers/srv(la racine utilisateur avec clés SSH et wrappers reste protégée). - UID/GID : Hub exécuté sous
1026:100, Filebrowser sous:(1001:1001).
Ce qui NE fonctionne PAS
| Tentative | Erreur obtenue / Risque | Raison de l'échec / Solution adoptée |
|---|---|---|
| Tunnel WireGuard wg-easy | 0 B reçus, timeout | wg-easy décommissionné le 08/07/2026. Remplacé nativement par Tailscale (44 ms). |
| Port 3030 pour hermes-perso | Port bloqué / conflit | Processus docker-proxy orphelin sur l'hôte NAS. Port 3031 fixé et 3030 bloqué via iptables. |
Tag :latest sur Filebrowser |
Risque de régression | Règle de gouvernance : tag SemVer exact fixé sur filebrowser/filebrowser:v2.32.0. |
| Auth interne filebrowser.db | Double login inutile | Cloudflare Access + 2FA constitue le garde-fou unique suffisant. Filebrowser démarré avec --noauth. |
Montage de la racine /home/dsh-agent |
Fuite de clés SSH | Restriction stricte au seul sous-dossier workspace/. |
Solution validée
1. Dépôt & Code source
- Dépôt Gitea :
http://192.168.100.33:3232/bolbol/hermes-hub.git - Stack : FastAPI + Async Streaming Proxy (
httpx) + Design Tokens + Sélecteur Toggle DSH (Claude Design).
2. Déploiement sur VPS Contabo (100.94.90.119)
# 1. Cloner le dépôt
git clone http://bolbol:PASSWORD@100.86.197.88:3232/bolbol/hermes-hub.git ~/hermes-hub
cd ~/hermes-hub
# 2. Configurer l'environnement (.env)
cp .env.example .env
# Définir HUB_SECRET (openssl rand -hex 32) et les UIDs dsh-agent
# 3. Lancer le stack Hub + Filebrowser
docker compose up -d --build
3. Routage & Cloudflare Tunnel
- Ingress Cloudflare Tunnel (
cloudflared) :hub.yesminedor.tn -> http://127.0.0.1:8080 - Application Cloudflare Access : politique Email OTP autorisée.
Vérification
# 1. Vérification santé Hub
curl -fsS http://127.0.0.1:8080/api/health
# Résultat attendu : {"status":"healthy","universes_configured":4}
# 2. Vérification connectivité multi-univers
curl -fsS http://127.0.0.1:8080/api/universes
# 3. Test du proxy Filebrowser
curl -fsS -I http://127.0.0.1:8080/u/nabil/files/
# Résultat attendu : HTTP/1.1 200 OK
Pièges spécifiques
- Cloisonnement des cookies : L'attribut
Pathdes en-têtesSet-Cookieémis par les backends est réécrit à la volée versPath=/u/{universe_id}/pour interdire toute collision entre univers sous le même domaine. - Purge mémoire front-end : À chaque changement d'univers, l'iframe charge d'abord
about:blankpour détruire le state DOM/JS résiduel avant de monter le nouvel univers.