Analysis
Corico helps a teacher give spoken feedback on a test: they record a memo for each paper, and the student listens to it by scanning a QR code stuck on it.
Journey
- The teacher chooses a class and a test.
- For each paper, they choose the student, record, validate. On a phone or on a computer.
- The tool uploads the sound to the ENT's Nextcloud and creates a public share link, which expires on June 30 of the school year.
- At the end, it generates the PDF of a sheet of labels.
- The teacher sticks each QR code on the student's paper. The student listens with no account and no app.
Decisions
- Storage: the teacher's ENT Nextcloud, with an app password. Checked on
nc-lycees.netocentre.fr: a public link plays on a phone without signing in to the ENT. The whole journey was tested there on November 4, 2026, from a computer: folder, neutral name, read-only link, expiry on June 30. - Labels: three formats to choose from, 35 × 35 mm by default, 48.5 × 25.4 mm and 38.1 × 21.2 mm. The student's name is on the label, under the QR code on one line, first name first and the last name cut with "…" if need be, or beside it, as long formats have no room between labels. The three grids are measured on real sheets. All can be set in the tool with a check sheet.
- Sensitive links: a memo's link is protected neither by a sign-in nor by a personal code. The tool says so wherever a QR code is shown and on the page that generates the PDF, before the download (
src/LinkWarning.vue). - Students: a class list pasted or imported (CSV, text), the columns of the names found by their titles; only the names are read in.
- Correcting and removing: an assignment, a class and a student can be renamed or removed; deleting an assignment or taking a student out deletes the memos from Nextcloud (
docs/guide/managing.md). This is also the end-of-year clean-up. - Local data: the browser works as on files of the teacher's disk. There is no account and no signing out: Nextcloud is linked once, and the password is only asked again if it is refused. A button erases this device's data.
- Backup: lists and tracking are in one encrypted file of the
Coricofolder, read at opening and sent after each change. A write only goes over the version read (ETag); otherwise both versions are merged. A file that cannot be read (another key) is never overwritten without an explicit request. - Drafts: a memo recorded is kept at once in the browser (IndexedDB), without being sent, and survives a reload, or leaving the page while recording. Students are done in any order; drafts are sent one by one or all at once.
- Personal or shared device: asked at sign-in and pairing. A personal device keeps the access, the key, the lists and the drafts in the browser, sealed when the teacher sets a local password (Argon2id over a random AES-GCM key, which seals everything; 8 characters at least, locked again after 30 minutes without activity). A shared one keeps them in the page's memory only, not even in
sessionStorage, which a browser's session restore gives back: a reload signs out, the browser asks before, updates wait, and 30 minutes without activity forget everything unless drafts are not sent. - Recording again: the new recording stays apart until the teacher, having listened to both, validates it to replace the old one. Nothing is lost by a wrong click.
- Publishing in place: a memo already sent and done again is written over the same file. Nextcloud ties a link to the file and not to its content, so the link and the printed QR code stay the same. Checked on the ENT on November 5, 2026: an old QR code plays the new sound.
- PDF:
pdf-lib, as Kolekti, with Fira Sans embedded for accented names and extended Latin alphabets. A QR code of 28 mm, the rest of the label being its quiet zone. A name too long is made smaller, then cut. - Encryption: every file but the sounds is encrypted in the browser (AES-256-GCM, Web Crypto) with a key created at the first sign-in.
- Pairing a device: a link, or the QR code of that link, holds the Nextcloud credentials and the key, after the
#so that it is never sent to the server. This content is encrypted with a random code of 8 characters, in base 32 with no ambiguous character, derived by Argon2id, to pass on through another channel, and with a random number the relay keeps in memory 10 minutes at most and gives once (server/pairing.ts): past that, the link and its code open nothing. One code, one salt and one number per pairing. - Audience: the teacher first, then colleagues with their own account.
- File names: sounds have random names, in a folder per class and, in it, one per assignment, named after them (
src/shared/folders.ts);Mémos en lignein a class with one QR code for the year. Renaming moves the folder; memos sent before the folders are tidied into them at opening. Two classes, or two assignments of a class, cannot share a name, nor a folder.
Architecture
Browser → corico.infobrisson.fr static page (French hosting)
→ api.corico.infobrisson.fr Node relay (VPS)
→ nc-lycees.netocentre.fr the ENT's NextcloudThe relay is needed: tested on November 4, 2026, the Nextcloud (34.0.5) sends no CORS header, so a web page cannot call it directly. What it sees and what it does not keep is described in the privacy page.
- Stack: that of MinOS and Kolekti (Vue 3, Vite, TypeScript, Vuetify, Vitest, yarn 4, ESLint, Prettier). The relay uses Hono, like Kolekti's server.
- Request bodies: kept in memory while being sent, to pass them on with their size rather than as a stream: some PHP servers lose streamed bodies. Their size is bounded.
- Audio format: every memo becomes an MP3 in the browser when it is sent (mono, 64 kbit/s, about 500 KB per minute), as every phone plays one, whereas what browsers record differs:
.oggin Firefox, Opus in Chromium, which refuses AAC. The encoder is@breezystack/lamejs(LGPL-3.0), a port of LAME in plain JavaScript, so the page needs no WebAssembly for it. - PWA: a service worker keeps the app's files, never any data. It looks for a new version when coming back to the page and every 5 minutes, and the page reloads as soon as nothing stands against it (
src/shared/version.ts). - Deployment: the page and the documentation from a computer (
yarn deploy), pre-compressed in Brotli and sent byrsyncin place, the forge's CI only checking style and tests, without dated revisions; the relay by an Ansible role of the server's configuration, as Kolekti.
One QR code per student for the year
A request of Romain's, an option per class, the QR code per assignment staying the default. A student's QR code is a public file in student.qr, written over at each publication, so its link never changes; memos of such an assignment (assignment.yearly, set at its creation) are files with no link. src/shared/year-qr.ts.
- What is online is per student, in
qr.assignmentId: nothing else records it, and what remains to do after a failure is worked out from it. Publishing an assignment gives two tones to a student with no memo in it: an old memo would be taken for this one. An old memo can be put back for one student. - A new QR code creates a new file playing the same thing, then deletes the old one, whose link dies with it. If it cannot be deleted, the new file goes and the failure is told: a code thought cut would be worse.
- The relay does not change: publishing is reading a file of the folder and writing another.
- Earlier versions of a file: Nextcloud keeps them, as for a memo republished in place. They are meant for the account's owner, and the public page should only offer the player and the download. Checked by Romain on the real ENT on November 5, 2026: a republished link, opened in a private window, gives no access to earlier versions.
Printing before marking
A request of Romain's: a teacher at school with the printer, who marks at home and hands back the next morning. Since a file can be written over with its link unchanged (publishing in place), a place is reserved in advance: a file holding two short tones and its public link, marked reserved in the content (reserveMemos in src/shared/deposit.ts). Such a memo counts as neither sent nor done, but can be printed; sending writes over it. Two devices reserving apart would leave one file unknown to the tool, as with any memo made apart.
Editing and merging memos
A request of Romain's.
- Several memos for one paper: a teacher who forgot something adds a recording. It stays apart, and sending is refused until they are merged, in the order chosen, or one is kept. More than two are merged one at a time.
- Cutting: a passage is removed from a memo, before or after a merge, with an undo until the version is kept.
- The sound while it is worked on: a draft is kept as WAV, which loses nothing however often it is cut (about 5 MB per minute), and only becomes an MP3 when sent (
src/shared/sound.ts). Drafts made before this are MP3s, and stay so if nothing is done to them. - After sending: the memo is fetched from Nextcloud and becomes a draft again, published in place. Its MP3 is decoded and encoded once more: one more generation of compression, which has not been judged by ear yet.
Out of scope, for good
- Other ENT: a decision of Romain's, the tool will never be developed for other ENT than the one it is configured for. The relay talks to one Nextcloud and its network filter lets it reach only that one.