- Configure tailscale serve automatically for each instance on Windows userspace networking. - Add local UI buttons: start/stop/reset/delete instances (stop/start preserve volumes). - Clean shutdown: stop tailscaled and instances, notify server with instance_stopped. - Restart tailscaled on agent boot using persisted state when pre-auth key is absent. - Sync instance stopped/deleted status to dashboard (server/lib/websocket.ts). - Security: include prior authz/scoping changes across API routes, ephemeral pre-auth keys, ACL policy, internal API key. - Update SUIVI_VPN_ONDEMAND.md and docs/ONBOARDING_CLIENT.md. - Bump agent version to 0.3.5.
16 KiB
Deployeur studioE5 — Onboarding d’un nouvel établissement
Objectif
Ce document décrit le fonctionnement du deployeur studioE5, c’est-à-dire l’application / l’outil qui provisionne et configure un nouvel environnement client (un établissement) prêt à accueillir l’application studioE5.
L’application studioE5 elle-même (agent, VPN on-demand, resolver, Caddy, Headscale, etc.) est documentée dans SUIVI_VPN_ONDEMAND.md. Le deployeur est l’outil qui déploie cette application sur un VPS dédié au client.
Public cible
- Équipe produit / développement du deployeur
- Équipe ops / déploiement
- Référents techniques du client A
Glossaire
| Terme | Définition |
|---|---|
| Deployeur | Application qui provisionne un VPS, déploie la stack studioE5 et configure le DNS/certificats pour un nouvel établissement. |
| Hub central | Dashboard superadmin studioE5 qui orchestre les déploiements et la gestion multi-clients. |
| Établissement | Entité client (école, lycée, université, entreprise). |
| Tag établissement | Slug unique et court identifiant l’établissement dans les URLs et le DNS. |
| Domaine géré | Sous-domaine fourni par studioE5 : *.tag.edudeploy.com. |
| Domaine propre | Domaine détenu par l’établissement : *.tag.monetablissement.fr. |
| Application studioE5 | Stack complète déployée sur le VPS : server, resolver, resolver-vpn, caddy, headscale, postgres, etc. |
| Agent générique | Binaire agent unique, capable de se connecter à n’importe quel serveur studioE5 via résolution d’URL à l’activation. |
Architecture : deployeur vs application studioE5
┌─────────────────────────────────────────────────────────────┐
│ Hub central studioE5 │
│ (superadmin, gestion des établissements, monitoring) │
└───────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Deployeur studioE5 │
│ (provisionning VPS, DNS, certificats, déploiement stack) │
└───────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Application studioE5 (un par client) │
│ Caddy — Resolver — Resolver-VPN — Headscale — Server — DB │
│ ▲ │
│ │ WebSocket / VPN on-demand │
│ ▼ │
│ Agent élève (Windows/Linux) │
└─────────────────────────────────────────────────────────────┘
Flux d’onboarding par le deployeur (vue d’ensemble)
Création de l’établissement dans le hub
↓
Choix du domaine (géré ou propre)
↓
Génération du tag établissement
↓
Provisionning du VPS
↓
Configuration DNS wildcard
↓
Génération du certificat wildcard
↓
Déploiement de la stack studioE5 (Docker Compose)
↓
Initialisation de Headscale et création des clés
↓
Création du compte administrateur de l’établissement
↓
Génération des codes d’activation
↓
Build et mise à disposition de l’agent dédié
↓
Activation de l’agent par un élève
↓
Création d’une première instance (validation du déploiement)
1. Création de l’établissement dans le hub
Le superadmin crée un nouvel établissement dans le hub central.
Données minimales :
- Nom officiel
- Type d’établissement (école, lycée, université, entreprise)
- Pays / fuseau horaire
- Contact administrateur
- Choix du mode de domaine (
managedoucustom)
2. Choix du domaine
Option A — Domaine géré par studioE5 (MVP)
Le deployeur crée automatiquement un sous-domaine du domaine maître :
*.tag.edudeploy.com
Le hub gère le DNS chez le registrar studioE5 (actuellement Infomaniak).
Option B — Domaine propre de l’établissement (évolution)
L’établissement fournit son propre domaine :
*.tag.monetablissement.fr
Prérequis :
- Le client pointe son DNS wildcard vers l’IP du VPS provisionné.
- Le deployeur dispose d’un token API du registrar du client pour le challenge DNS-01.
3. Génération du tag établissement
Le tag est un slug court, unique au niveau du hub, utilisé dans les URLs et le DNS.
Règles
- Uniquement
[a-z0-9-] - Pas de tiret au début ni à la fin
- Longueur conseillée : 2 à 20 caractères
- Vérification d’unicité en base
Exemples
| Nom d’établissement | Tag |
|---|---|
| Lycée Jules Ferry | ljf |
| Institut Supérieur du Digital | isd |
| École Notre-Dame | end |
Gestion des collisions
ljf→ljf-2,ljf-3, etc.
4. Provisionning du VPS
Le deployeur provisionne un VPS dédié pour l’établissement.
Prérequis sur le VPS vierge
- OS Linux (Ubuntu LTS recommandé)
- Docker + Docker Compose installés
- Accès SSH avec clé
- Ports ouverts : 22, 80, 443
Actions automatisées par le deployeur
- Installation de Docker et Docker Compose si absent.
- Création de la structure de dossiers (
/opt/studioe5-<tag>/). - Génération des secrets (
.env) :INTERNAL_API_KEYHEADSCALE_API_KEYHEADSCALE_AUTH_KEY(réutilisable, taguéetag:student-agent)HEADSCALE_RESOLVER_AUTH_KEYINFOMANIAK_API_TOKEN(si domaine géré)NEXTAUTH_SECRET,DATABASE_URL, etc.
- Récupération des images Docker depuis le registry privé (ou build sur place en attendant).
- Génération des fichiers de configuration (
Caddyfile,docker-compose.yml,headscale/config.yaml,headscale/acl_policy.hujson).
5. Configuration DNS wildcard
Domaine géré
Le deployeur appelle l’API du registrar pour créer :
*.tag.edudeploy.com A <IP_DU_VPS>
Domaine propre
Le deployeur vérifie que l’enregistrement existe :
*.tag.monetablissement.fr A <IP_DU_VPS>
6. Certificat wildcard
Principe
Un seul certificat wildcard couvre toutes les instances futures de l’établissement.
Mise en œuvre avec Caddy
Le deployeur génère le Caddyfile :
*.tag.edudeploy.com {
tls {
dns infomaniak {env.INFOMANIAK_API_TOKEN}
}
reverse_proxy resolver:2020 {
header_up Host {host}
}
}
Pour un domaine propre, le provider DNS est celui du client.
Renouvellement
Géré automatiquement par Caddy.
7. Déploiement de la stack studioE5
Le deployeur lance la stack Docker Compose complète :
cd /opt/studioe5-<tag>
docker compose up -d
Services déployés :
server: API + WebSocket + UI Next.jsresolver: reverse proxy interne vers les instancesresolver-vpn: sidecar Tailscale dans le netns du resolvercaddy: reverse proxy public + TLSheadscale: contrôleur Tailscalepostgres: base de données
8. Initialisation de Headscale
Le deployeur initialise Headscale et crée les clés nécessaires :
# Création de l’utilisateur dédié au resolver
docker compose exec headscale headscale users create resolver
# Création de la clé pré-auth réutilisable pour les agents
docker compose exec headscale headscale preauthkeys create \
--user studioe5 \
--reusable \
--tags tag:student-agent \
-e 87600h
# Création de la clé pré-auth pour le resolver
docker compose exec headscale headscale preauthkeys create \
--user resolver \
--tags tag:resolver \
-e 87600h
# Création d’une clé API Headscale valable 10 ans
docker compose exec headscale headscale apikeys create -e 87600h
Ces secrets sont stockés dans le .env du serveur.
9. Création du compte administrateur de l’établissement
Une fois la stack déployée, le deployeur (ou le hub) crée le premier compte administrateur de l’établissement via l’API du serveur nouvellement déployé.
Rôles :
admin: gestion des élèves, instances, agents.teacher: gestion limitée à certaines classes/groupes.superadmin(studioE5) : accès transverse.
L’administrateur reçoit un lien d’activation sécurisé.
10. Génération des codes d’activation
Le deployeur configure le serveur pour permettre la génération de codes d’activation.
Règles de sécurité (implémentées côté application studioE5)
- Génération avec
crypto.randomBytes - Alphabet sans ambiguïté :
ABCDEFGHJKLMNPQRSTUVWXYZ23456789 - 6 caractères
- Expiration après 60 minutes
- Invalidation après usage
- Rate-limiting : 5 tentatives par code / 5 tentatives par
nodeIdsur 15 minutes
Flux
- L’administrateur génère un code pour un élève.
- L’élève saisit le code dans l’agent.
- Le serveur valide et renvoie :
- l’identité de l’élève
- l’URL Headscale
- une clé pré-auth Headscale éphémère
- L’agent démarre automatiquement le VPN.
11. Build et mise à disposition de l’agent
Principe
L’agent est un binaire générique, mais il doit être capable de se connecter au bon serveur. Le deployeur génère un agent pré-configuré ou un installeur qui embarque l’URL du serveur de l’établissement.
Build
cd /opt/studioe5-<tag>/agent
./download-tailscale-bins.sh 1.98.4
./build.sh
Artifacts générés :
studioE5-agent-vX.Y.Z-windows.zipstudioE5-agent-vX.Y.Z.exestudioE5-agent-vX.Y.Z(Linux)
Mise à disposition
Les fichiers sont servis par Caddy depuis server/public/agent/ :
https://tag.edudeploy.com/studioE5-agent-vX.Y.Z-windows.zip
12. Activation de l’agent
Activation zéro-config
- L’élève télécharge l’agent depuis l’URL de l’établissement.
- Il extrait l’archive et lance
studioE5-agent.exe. - Il ouvre
http://localhost:7070. - Il saisit le code d’activation à 6 caractères.
- L’agent contacte le serveur, récupère la configuration et démarre le VPN.
Les détails techniques du VPN on-demand (named pipes Windows, logs, ACL, tokens, clés éphémères) sont documentés dans
SUIVI_VPN_ONDEMAND.md.
13. Création d’une instance et construction de l’URL (validation)
Le deployeur ou l’administrateur crée une première instance pour valider le déploiement.
Format d’URL
<appli>-<initiales><id-court>.<tag>.<domaine>
Exemple :
wp-jd47.ljf.edudeploy.com
Avec :
wp: type d’applicationjd: initiales de l’élève47: identifiant court uniqueljf: tag de l’établissementedudeploy.com: domaine de base
Mapping type d’application → préfixe
| Application | Préfixe |
|---|---|
| WordPress | wp |
| PrestaShop | ps |
| Moodle | mdl |
| Nextcloud | nc |
Protection de l’identité
- L’URL ne contient pas le nom complet de l’élève.
- Seules les initiales + un identifiant court opaque sont exposées.
14. Modèles de données du deployeur
Table / modèle Organization (établissement dans le hub)
{
"id": "uuid",
"name": "Lycée Jules Ferry",
"tag": "ljf",
"domainMode": "managed",
"baseDomain": "edudeploy.com",
"adminEmail": "admin@ljf.fr",
"status": "active",
"createdAt": "2026-06-25T17:28:07Z"
}
Table / modèle Deployment (déploiement sur un VPS)
{
"id": "uuid",
"organizationId": "uuid",
"serverIp": "203.0.113.10",
"serverHostname": "ljf.studioe5.edudeploy.com",
"wildcardDnsConfigured": true,
"wildcardCertificateReady": true,
"dnsProvider": "infomaniak",
"dnsProviderTokenRef": "env:INFOMANIAK_TOKEN_LJF",
"headscaleApiKeyRef": "env:HEADSCALE_API_KEY_LJF",
"status": "ready",
"deployedAt": "2026-06-25T17:28:07Z"
}
Table / modèle Student (dans l’application studioE5 déployée)
{
"id": "uuid",
"organizationId": "uuid",
"firstName": "Jean",
"lastName": "Dupont",
"initials": "jd",
"activationCode": "AB3D9F",
"activationCodeExpiresAt": "2026-06-25T18:28:07Z",
"nodeId": "vps-8fc665eb",
"nodeToken": "..."
}
Table / modèle Instance (dans l’application studioE5 déployée)
{
"id": "cmqqgrur20001lw67t2bdgzkg",
"organizationId": "uuid",
"studentId": "uuid",
"nodeId": "vps-8fc665eb",
"templateId": "wordpress-wordpress-latest",
"applicationPrefix": "wp",
"shortId": "47",
"subdomain": "wp-jd47",
"fqdn": "wp-jd47.ljf.edudeploy.com",
"port": 8001,
"status": "running"
}
15. Sécurité et RGPD
Protection de l’identité de l’élève
- L’URL publique ne contient pas le nom complet de l’élève.
- Seules les initiales + un identifiant court opaque sont exposées.
Isolation réseau
- Les agents élèves ne peuvent pas communiquer entre eux (ACL Headscale).
- Le resolver est le seul service autorisé à joindre les agents sur leurs ports d’instance.
Authentification
- Token unique par agent (
node.token). - Clé API interne pour les endpoints serveur → agent.
- Sessions NextAuth sur les routes API métier.
Clés pré-auth Headscale
- Éphémères, à usage unique, 15 minutes d’expiration.
- Non persistées côté agent.
16. Checklist de validation du deployeur
À l’issue d’un onboarding, les points suivants doivent être validés :
- L’établissement est créé dans le hub avec un tag unique.
- Le VPS est provisionné et accessible en SSH.
- Docker et Docker Compose sont installés.
- Le DNS wildcard est résolu (
*.tag.edudeploy.com→ IP du VPS). - Le certificat wildcard est obtenu et valide.
- La stack studioE5 est démarrée (
docker compose ps). - Headscale est initialisé avec les utilisateurs et clés nécessaires.
- Le compte administrateur de l’établissement est créé.
- Un code d’activation peut être généré pour un élève.
- L’agent est buildé et téléchargeable depuis le serveur de l’établissement.
- L’agent s’active avec le code zéro-config.
- Une instance peut être créée et son URL est accessible en HTTPS.
- Deux instances différentes reçoivent des URL uniques.
- Le flux HTTPS complet retourne bien HTTP 200.
17. Roadmap du deployeur
Court terme (MVP)
- Déploiement manuel ou semi-automatisé d’un nouvel établissement sur un VPS.
- Domaine géré par studioE5 uniquement.
- Build des images sur le VPS cible.
- Agent avec URL serveur hardcodée ou fournie à l’activation.
Moyen terme
- Agent générique : déterminer l’URL serveur cible à l’activation (code structuré, hub de résolution, ou champ URL).
- Script de provisionning : installation Docker, déploiement stack, génération secrets, DNS wildcard.
- Registry d’images privé : builder une fois, déployer partout.
- Support de domaines propres à l’établissement.
- Support multi-registrar DNS.
Long terme
- Hub central multi-clients : dashboard superadmin, gestion des versions, logs distants.
- Mises à jour à distance : pousser une nouvelle version du serveur et de l’agent sur tous les déploiements.
- Monitoring / support : alertes serveur down, certificat expiré, agent hors ligne.
- Tests automatisés : validation du flux activation → VPN → instance → HTTPS public à chaque déploiement.
- Console/log intégré et barre de progression dans l’agent.
- Génération automatique de codes d’activation par import CSV.