Skip to content

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 fichierRôle
server/app.tsLe relais : liste blanche, en-têtes, limite de taille
server/guard.tsLes limites contre les abus
src/shared/nextcloud.tsLes appels à Nextcloud
src/shared/recorder.tsL'enregistrement au micro
src/shared/mp3.tsLa conversion en MP3 (LAME en JavaScript)
src/shared/drafts.tsLes brouillons et candidats, dans IndexedDB
src/shared/place.tsAppareil personnel ou partagé : où vont les secrets
src/shared/lock.tsLe mot de passe local facultatif, Argon2id
src/shared/idle.ts30 minutes sans activité : oublier ou verrouiller
src/shared/deposit.tsLe dépôt, seul ou en lot, et la publication sur place
src/shared/store.tsClasses et devoirs, et leur fusion
src/shared/vault.tsLe chiffrement AES-256-GCM
src/shared/sync.tsLa sauvegarde dans Nextcloud
src/shared/pairing.tsLe lien d'association, Argon2id
src/shared/version.tsLa PWA et sa mise à jour automatique
src/shared/labels-pdf.tsLe 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éthodesCheminUsage
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>.mp3Un mémo, par le nom que l'outil lui a donné
MOVEun mémo, ou le dossier d'une classe ou d'un devoirRangement : 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/sharesLien public, lecture seule, dans le dossier
GET/ocs/v2.php/cloud/userVé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) :

LimiteValeurRéponse
Requêtes par adresse600 par minute429
Mots de passe refusés par adresse10 en 15 minutes429
Corps gardés en mémoire128 Mo en tout503
La même chose, par adresse40 Mo503
Nombres d'association par adresse5 vivants429

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, et pending-pairing.ts la 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-eval dans la CSP.

Modèle de menace, en bref ​

QuiVoitNe voit pas
Le relaisMot de passe d'application, sons, JSON chiffréClé, noms
Hébergement françaisRequêtes de la pageTout le reste, sauf à modifier le code servi
Nextcloud de l'ENTSons, JSON chiffréClé, noms
Toute personne ayant le lienLe son d'un mémoLe reste

Les limites assumées sont décrites dans Où vont vos données ?.