Architecture
Pour une présentation sans jargon, voir Comment ça marche. Cette page s'adresse aux personnes qui veulent lire ou modifier le code.
Principe : tout ce qui peut se faire dans le navigateur s'y fait. Le serveur ne garde rien, et le stockage durable est le Nextcloud de l'enseignant.
Le code
| Dossier ou fichier | Rôle |
|---|---|
server/app.ts | Le relais : liste blanche, en-têtes, limite de taille |
server/guard.ts | Les limites contre les abus |
src/shared/nextcloud.ts | Les appels à Nextcloud |
src/shared/recorder.ts | L'enregistrement au micro |
src/shared/mp3.ts | La conversion en MP3 (LAME en JavaScript) |
src/shared/drafts.ts | Les brouillons et candidats, dans IndexedDB |
src/shared/place.ts | Appareil personnel ou partagé : où vont les secrets |
src/shared/lock.ts | Le mot de passe local facultatif, Argon2id |
src/shared/idle.ts | 30 minutes sans activité : oublier ou verrouiller |
src/shared/deposit.ts | Le dépôt, seul ou en lot, et la publication sur place |
src/shared/store.ts | Classes et devoirs, et leur fusion |
src/shared/vault.ts | Le chiffrement AES-256-GCM |
src/shared/sync.ts | La sauvegarde dans Nextcloud |
src/shared/pairing.ts | Le lien d'association, Argon2id |
src/shared/version.ts | La PWA et sa mise à jour automatique |
src/shared/labels-pdf.ts | Le PDF des étiquettes |
Ce que le relais laisse passer
Le relais refuse tout ce qui ne vient pas de l'origine de la page et tout ce qui n'a pas d'identifiants. Toute autre requête reçoit 403, 404 ou 405.
| Méthodes | Chemin | Usage |
|---|---|---|
MKCOL | /remote.php/dav/files/<id>/Corico[/<classe>[/<devoir>]] | Créer les dossiers |
GET PUT | /remote.php/dav/files/<id>/Corico/<fichier> | Fichiers du dossier lui-même |
GET PUT DELETE | /remote.php/dav/files/<id>/Corico/[<classe>/[<devoir>/]]<uuid>.mp3 | Un mémo, par le nom que l'outil lui a donné |
MOVE | un mémo, ou le dossier d'une classe ou d'un devoir | Rangement : un mémo garde son nom, un dossier sa profondeur ; dans le dossier, même compte, jamais par-dessus quoi que ce soit (Overwrite: F) ; jamais le contenu sauvegardé |
POST | /ocs/v2.php/apps/files_sharing/api/v1/shares | Lien public, lecture seule, dans le dossier |
GET | /ocs/v2.php/cloud/user | Vérifier le compte |
Il ne transmet que les en-têtes utiles, jamais de cookie, et ne suit pas les redirections. Le corps est gardé en mémoire le temps de l'envoi, et sa taille est bornée à 20 Mo.
Ce qui freine les abus
server/guard.ts, en mémoire seulement, avec les adresses hachées et salées (15 minutes au plus) :
| Limite | Valeur | Réponse |
|---|---|---|
| Requêtes par adresse | 600 par minute | 429 |
| Mots de passe refusés par adresse | 10 en 15 minutes | 429 |
| Corps gardés en mémoire | 128 Mo en tout | 503 |
| La même chose, par adresse | 40 Mo | 503 |
| Nombres d'association par adresse | 5 vivants | 429 |
Une adresse IPv6 compte par son réseau /64, les compteurs repartent toutes les 15 minutes sous un nouveau secret, et aucun corps n'est lu avant d'avoir vérifié l'origine, la route, les identifiants et les limites. Un lien de partage n'est accepté que pour un nom que l'outil donne à ses sons, chaque réglage une seule fois, dans un formulaire simple, sans rien d'autre dans l'adresse.
La page dit à l'enseignant quoi faire sur ces réponses, plutôt qu'un code d'erreur (src/shared/failure.ts).
Une origine se falsifie depuis un script : ces limites sont ce qui l'arrête. Elles protègent aussi les autres enseignants, puisque Nextcloud ralentirait sinon l'adresse du serveur pour tous.
Changer la clé
Après la perte d'un appareil (src/LostDevicePage.vue). La nouvelle clé est d'abord gardée à part, la sauvegarde est fusionnée et écrite avec elle sous If-Match, et c'est seulement ensuite qu'elle remplace l'ancienne. Si la page se ferme entre les deux, la prochaine ouverture l'essaie sur la sauvegarde et l'adopte si elle l'ouvre. Les autres appareils se verrouillent jusqu'à une nouvelle association, et n'écrivent rien d'ici là. Les mémos ne sont pas chiffrés, et leurs liens ne changent pas.
Vérifié en ligne
Le 5 novembre 2026, depuis l'extérieur, sur la page et sur le relais : TLS 1.2 et 1.3 seuls, chiffrements faibles refusés, certificats Let's Encrypt, HTTP redirigé vers HTTPS, HSTS d'un an, fichiers sensibles de l'hébergeur refusés, et une CSP, nosniff et le refus d'être encadrée sur la page. Le relais ajoute à chaque réponse nosniff, Content-Security-Policy: default-src 'none' et Referrer-Policy: no-referrer. La CSP de la documentation garde 'unsafe-inline' pour les scripts, dont VitePress a besoin : elle ne contient aucune donnée, n'appelle rien, et a sa propre origine, donc elle n'atteint pas ce que l'appli garde dans le navigateur.
Déposer un mémo
Un mémo refait fait un PUT sur le même chemin, sans créer de lien. Nextcloud rattachant le lien au fichier, le QR code reste valable (vérifié sur l'ENT).
La sauvegarde chiffrée
Le contenu (classes, devoirs, grille) forme un seul fichier, Corico/donnees-chiffrees.bin : IV (12 octets) + AES-256-GCM(JSON).
La fusion réunit les éléments par identifiant. Ce qui a été retiré est noté dans removed, avec sa date, pour qu'un autre appareil ne le ramène pas : un devoir, une classe, ou devoir/élève, en négatif une fois ramené, le plus récent comptant. Une classe retirée reste si un autre appareil vient d'en faire un devoir. De deux versions d'un nom, la plus récente est gardée, par la date at de chaque élément.
Une suppression retire d'abord les fichiers (src/shared/removal.ts) : en cas d'échec, le reste demeure dans la liste et peut être réessayé. Un mémo déposé par un autre appareil au moment même de sa suppression resterait dans le dossier, inconnu de l'outil, à supprimer à la main.
Pour un même élève, c'est le mémo le plus récent qui est retenu.
L'association
- La donnée suit le
#, jamais envoyé au serveur, etpending-pairing.tsla retire de l'adresse avant que le routeur la lise. - 8 caractères en base 32 font 40 bits : Argon2id rend l'essai hors ligne de tous les codes très long.
- Le nombre du relais (
server/pairing.ts,POST /pairing) est tiré par l'appareil qui crée le lien et pris une fois par celui qui l'ouvre (POST /pairing/<id>, puis 410) : en mémoire, 10 minutes au plus, 5 vivants par adresse et 2 000 en tout. Une date dans le lien ne le permettrait pas : qui a le lien et le code le déchiffrerait hors de l'outil. Sans le nombre, rien ne s'ouvre. - L'appareil qui ouvre prend le nombre au premier essai et le garde en mémoire : un code mal saisi se ressaisit ; un rechargement le perd, et il faut un nouveau lien.
- Argon2id est chargé à la demande, en WebAssembly (
hash-wasm), d'oùwasm-unsafe-evaldans la CSP.
Modèle de menace, en bref
| Qui | Voit | Ne voit pas |
|---|---|---|
| Le relais | Mot de passe d'application, sons, JSON chiffré | Clé, noms |
| Hébergement français | Requêtes de la page | Tout le reste, sauf à modifier le code servi |
| Nextcloud de l'ENT | Sons, JSON chiffré | Clé, noms |
| Toute personne ayant le lien | Le son d'un mémo | Le reste |
Les limites assumées sont décrites dans Où vont vos données ?.