Security / Security overview
Security overview
What the software does to protect records, described technically. Version 1.0.
No compliance claim. Lulu hasn't been independently audited or certified, and this document doesn't claim compliance with any law, regulation or standard. Whether Lulu fits your organization's obligations is for your organization and its counsel to decide.
Architecture
- No server. Each installation keeps its own records on its own computer. The publisher has no copy of, and no access to, records, passphrases or keys.
- One page, two shells:
- The app is a single offline web page, run inside the operating system's web engine: WebKit on macOS, Microsoft Edge WebView2 on Windows.
- A small native shell (Swift on macOS, Rust on Windows) does file storage, key storage and unlock.
- The page reaches the shell only through a message bridge.
- No network:
- The page's Content-Security-Policy is
default-src 'none'withconnect-src 'none'. Only the page's own script (carrying the build's nonce), inline style and WebAssembly (for Argon2id) are allowed. Inline event handlers can't run, so markup that reaches the page through a record or an imported file can't run script. Imported files are refused when their record numbers or links aren't the kind Lulu makes. - The Mac and Windows apps hand only mail (and, on the Mac, phone) links to the system. No web address is opened from the page.
- The one exception, on the Mac only: with iPhone pairing included,
connect-srcallowsws://127.0.0.1:51847, the Sync helper on the same Mac, and nothing else. - The shells load nothing from the network, and the Windows shell blocks navigation away from the app's own page.
- There's no analytics, telemetry, crash reporting, advertising, accounts or update checks.
- The page's Content-Security-Policy is
Encryption
| What | How |
|---|---|
| Records | AES-256-GCM (Web Crypto). New random 96-bit IV for each file; authenticated, so any change makes the file fail to open. |
| Data key | Random 256-bit. macOS: Keychain, this device only, only while unlocked. Windows: DPAPI (current user), stored in the user's local app-data folder (not roamed). |
| Unlock | Touch ID or the Mac password (LocalAuthentication); Windows Hello (UserConsentVerifier). The app gets only a yes or no answer. |
| Recovery | The data key, wrapped with the recovery passphrase (PBKDF2-SHA256, 310,000 iterations). |
| Exports (.emrx) | Passphrase-wrapped (PBKDF2-SHA256, 310,000 iterations), AES-256-GCM. |
| Documents | Each file has its own AES-256-GCM key, kept inside the encrypted record. Random names, sizes padded to 64 KB. Checked by SHA-256 when opened. |
| OneDrive backups | A separate backup key wraps a new per-file key. The backup key is wrapped by the backup passphrase (Argon2id, libsodium) and by a printed recovery key (HKDF). Files are bound to their sequence numbers and hash-chained, so tampering, missing files and rollback are detected. The folder holds only ciphertext and a plain-text READ ME; no copy of the app is ever written there, and the shells refuse any other file name. |
| iPhone sync | P-256 key agreement with HMAC confirmation at pairing; per-phone AES-256-GCM keys; local network only. Changes made on the phone (including home programs, templates and custom exercises) are checked by the Mac like its own, with edit history and review before saving. |
| Shared PDFs | Password-protected: macOS PDFKit's standard PDF encryption; on Windows AES-256 (PDF 2.0 handler, lopdf). |
The recovery passphrase and export passphrases must be at least 10 characters. A recovery key file and exports are protected only by that passphrase (PBKDF2), so a long sentence is the strongest choice, and exports should travel separately from their passphrase. A stronger key derivation (Argon2id, as OneDrive Backup already uses) is planned with the next file-format version.
License keys
- Signed and offline: each key is an ECDSA P-256 signature by the publisher over the license details and one computer's code. The app checks it with the public key built into it, using Web Crypto, without any network connection.
- One computer per key: the computer code is the first 50 bits of SHA-256 over a fixed label and the machine ID (the Mac's IOPlatformUUID, Windows' MachineGuid). The machine ID itself is never shown or sent.
- Activation happens in a web browser, not in the app. PayPal notifies the website of each payment (with a signed webhook the website verifies with PayPal), and the buyer then enters the computer code and the email they paid with. The website never sees records.
- Never a lock on records: without a key the app offers the demo and an encrypted export; nothing is deleted.
Access and accountability
- Locking: automatic lock after inactivity (default 5 minutes), on sleep and on screen lock. Sensitive settings need a fresh unlock.
- Activity history: saves, backups, restores, shares and syncs are logged, with no record contents.
- Edit history:
- Every record keeps what each edit replaced, with undo.
- Signed notes can't change; corrections are addenda.
- Important changes are reviewed before saving.
- Kiosk: shows no records. Leaving it needs a PIN (stored as a salted hash), Touch ID or Windows Hello.
Integrity and availability
- Files on this computer:
- Every write is read back and compared.
- Backups are never overwritten; two in the same second get distinct names.
- A damaged record falls back to the newest readable backup, and the damaged file is archived, never deleted.
- OneDrive backups:
- Written only to a folder shown to hold this computer's backup key.
- The app never creates the backup folder itself.
- A folder that's temporarily unavailable puts the backup into a waiting state with bounded retries. It's never skipped, and nothing is written into a lookalike folder.
- Nothing is silently deleted:
- Removed documents are archived.
- Old OneDrive versions are removed only after the person confirms a cleanup, and go to the Recycle Bin or Trash.
Data leaving the device
Only through a deliberate action, and each one is logged:
- encrypted exports;
- password-protected PDFs;
- de-identified spreadsheets;
- emailed handouts, which carry only the handout and go only to school addresses, as a draft the person sends.
Diagnostics
A short local log for troubleshooting:
- No names or record contents: every line is scrubbed of names, emails, phone numbers and chart IDs.
- Only by hand: it leaves the device only when the person exports it.
- Crash reports: the Mac export includes the first lines of macOS's crash report for the app, with user paths removed.
Supply chain
- libsodium.js: version-pinned, with its SHA-256 checked at every build.
- EFF word list: version-pinned, SHA-256 checked at every build.
- Rust crates (Windows): pinned by
Cargo.lock, all under permissive licenses (listed in Third-party notices). - Releases: built from tagged commits by
release/release.sh, with SHA-256 checksums for every file.
Testing
- In-engine self-tests, on made-up data in scratch folders, cover:
- encryption envelopes;
- migration;
- merge and restore;
- history and undo;
- backup rotation;
- OneDrive folder failure modes;
- diagnostics scrubbing.
- Phone Sync tests (synctest, deltatest, desktest, odtest) cover the sync protocol and the desktop's handling of phone changes.
Reporting a security problem
Contact the publisher (Settings → About → Support). Please don't include student information in reports.