- TypeScript 73.8%
- SCSS 13.2%
- JavaScript 12%
- CSS 0.7%
- Dockerfile 0.1%
|
|
||
|---|---|---|
| .forgejo/workflows | ||
| .github/workflows | ||
| .idea | ||
| .vscode | ||
| backend | ||
| containers | ||
| deploy-config | ||
| docs | ||
| frontend-checks@259e5b8c41 | ||
| messages | ||
| project.inlang | ||
| public | ||
| scripts | ||
| src | ||
| test | ||
| third-party | ||
| .env.template | ||
| .gitignore | ||
| .gitmodules | ||
| AGENTS.md | ||
| Demojahr.zurnote | ||
| flake.lock | ||
| flake.nix | ||
| frontend-checks.config.mjs | ||
| index.html | ||
| LICENSE | ||
| mise.toml | ||
| package.json | ||
| playwright.config.ts | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| renovate.json | ||
| tsconfig.json | ||
| tsconfig.node.json | ||
| vite.config.ts | ||
ZurNote.de
Client-side encrypted grade management web application built with SolidJS and TypeScript. Currently only focused on german grading systems (KMK / Linear) but you can use it for every grading system by providing your own grading table / formula.
Preview
These recordings show a fictional demo grade book on the local test server. All names, email addresses, assessments and results are made-up sample data.
Manage students and courses
View grades and weights at a glance – written and oral assessments, assessment sheets and calculated overall grades in a single matrix.
Grade using criteria – award points, apply correction marks and provide individual feedback in an assessment sheet; results can be linked to the grade list.
Choose a grading system – linear grades, KMK grade points, custom lookup tables and formulas. Classroom observation symbols are customizable too.
The workflow walks through opening a class, viewing students, creating an assessment and entering points with automatic grade calculation. It then shows how to create an assessment sheet with sample tasks, grade a student and link the results to the assessments. The workflow ends with the overall grade matrix.
Workflow as MP4 · More screenshots and recording instructions
Architecture & Encryption
- Local storage (
IndexedDB): Grade books (classes, courses, students, grades, rubrics, participation, and history) are serialized to JSON, encrypted in the browser with AES-GCM (256-bit) via the Web Crypto API, and stored as binary blobs in IndexedDB. - Key derivation: Keys are derived from the user's password using PBKDF2 (SHA-256, 600,000 iterations) and a random 16-byte salt. Decrypted state and keys exist only in memory during an unlocked session.
- Cloud sync (optional): Synchronizes the encrypted
.zurnotebinary blob (salt + 12-byte IV + ciphertext) with a PocketBase instance. Only the encrypted payload, book name, and timestamps are stored on the server. - Exports: Native backups use
.zurnote. Spreadsheet exports (.csv,.xlsx,.ods) are downloaded directly or wrapped in an AES-encrypted.ziparchive (@zip.js/zip.js) when a password is set. - Legal pages:
src/legal/impressum.example.mdandsrc/legal/datenschutz.example.mdprovide neutral templates. Copy them tosrc/legal/impressum.mdandsrc/legal/datenschutz.md(gitignored) for production builds.
Development
Tools and tasks are managed with mise (mise.toml), either directly on the host or inside an isolated QEMU MicroVM via nix-dev-sandbox (flake.nix).
Use Node 26.12.1 and pnpm 12.15.0 (mise install installs the pinned tools).
Start with AGENTS.md, the architecture and
coding standards. The
deployment guide and optional backend
describe production setup. Asset provenance is in third-party/.
Host
mise run install
mise run dev # http://127.0.0.1:5273
MicroVM
Configure .env once on the host:
cp .env.template .env # set DEV_SSH_PUBKEY, GIT_USER_NAME, GIT_USER_EMAIL
~/.ssh/config:
Host zurnote-vm
HostName 127.0.10.4
Port 2200
User dev
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
IdentityFile ~/.ssh/id_ed25519
Start the VM and run the dev server inside:
nix run --impure
ssh zurnote-vm
mise run install
VITE_HOST=0.0.0.0 mise run dev # exposed inside the VM and forwarded to the host
The VM's playwright profile provides Chromium from nixpkgs through
PLAYWRIGHT_BROWSERS_PATH; do not run playwright install inside the VM.
nixpkgs' playwright-driver must have the same version as @playwright/test
in package.json. Update both together (nix flake update, then rebuild the
VM). Until they match again, point PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH at a
Chromium from $PLAYWRIGHT_BROWSERS_PATH.
Tasks
| Command | Description |
|---|---|
mise run install |
Initialize submodules (frontend-checks) and install dependencies |
mise run dev |
Start Vite dev server on http://127.0.0.1:5273 (fixed port to preserve IndexedDB origin) |
mise run check:all |
Run static frontend checks (@dencla/frontend-checks), TypeScript typecheck, and unit tests |
mise run test |
Run unit tests (Vitest and node --test) |
mise run build |
Compile i18n messages, generate third-party license data, run all checks, and build to dist/ |
mise run build:demo |
Build with legal templates for public checkouts and CI; do not publish this artifact |
mise run test:e2e |
Test the existing build in four Chromium viewport sizes |
mise run release:prepare <version> |
Set the SemVer version in package.json, commit it and create tag v<version> (no push) |
mise run release:deploy v<version> |
Require the tag to match package.json and a clean tree, install locked dependencies, check production documents, build/test and prepare the d0b delivery archive |
mise run test:containers |
Build and test both delivery roles with rootless Podman |
mise run preview |
Serve production build on port 4173 |
Dependency installation is explicit and uses --frozen-lockfile. Checks never
reinstall packages. Install the test browser once with
pnpm exec playwright install chromium; Linux CI additionally uses --with-deps.
pnpm build requires filled local legal documents. A fresh public checkout can
use pnpm build:demo, then pnpm test:e2e. The supported appearance is light.
GitHub CI runs the demo build, unit/component checks, browser flows and an isolated PocketBase ownership test. No production book data is used.
License
Copyright 2026 Dennis Burgermeister. Licensed under GPL-3.0-or-later. Bundled third-party assets retain their individual licenses.
Betrieb
Das Deployment auf d0b verwendet den Dienst gradingcloud mit zwei rootless
Podman-Rollen: Caddy für die App und PocketBase für die optionale Cloud.
Die Version steht nur in package.json; das Release-Tag ist v<version>.
mise run release:prepare 1.0.0-beta.1 # Version setzen, committen, Tag anlegen
mise run release:deploy v1.0.0-beta.1 # prüft Tag = Version, baut und testet
mise run test:containers
git push --follow-tags # Forgejo baut das Release aus dem Tag
release:deploy ändert die Version nicht, sondern bricht bei abweichendem Tag oder
uncommitteten Änderungen ab, damit Bündel, Commit und angezeigte Version
übereinstimmen. mise run release:deploy erstellt das geprüfte Vertrag-1-Bündel;
mise run test:containers prüft beide Images. Einrichtung, Secrets, Forgejo-
Release und Umzug sind in docs/deployment.md beschrieben.




