Notes that are encrypted before they ever leave your device.
An offline-first notes app for Android, Windows, macOS and the web. End-to-end encrypted and zero-knowledge: the server stores ciphertext and never holds a key that opens it.
🌐 Live demo · ⬇️ Download · 📦 Deployment guide · 🗺️ Work plan
Most notes apps encrypt "in transit and at rest" — which means the provider holds the key. I wanted the other thing:
- The server can't read my notes, even if someone dumps the database
- It still works with no connection, and syncs when there is one
- The same notes on phone, desktop and browser
- A lock on individual notes, for the ones you don't want visible over your shoulder
So I built it.
- Offline-first — every note is written to a local SQLite database first; the cloud is a replica, not the source of truth
- End-to-end encryption — titles and content are encrypted with AES-256-GCM before they touch the network (details)
- Multi-device — sign in anywhere and your key follows you, unwrapped locally with your password
- Recovery phrase — a BIP39 12-word phrase opens your notes if you forget your password; without it, nobody can
- Per-note lock — hide a note's content behind its own password, recoverable with your account password
- Three note types — plain text, markdown with a rendered view, or a list of copyable fields (details)
- Copy anything — the whole note in one tap, or a single line at a time
- Sync that retries — edits and deletions made offline are queued and replayed instead of being silently lost
- Local search — filtering happens in memory over decrypted notes, because the server only has ciphertext to match against
- Automatic updates — desktop and mobile builds check a published
version.jsonand offer the new release - Dark / light — follows the system theme
The threat model is: the server, a database dump, and anyone holding your device without your password. Once you sign in on a device you trust, everything is decrypted and the encryption is invisible.
| What | Where it lives | Encrypted? |
|---|---|---|
| Note title | Local SQLite + Supabase | ✅ AES-256-GCM |
| Note content | Local SQLite + Supabase | ✅ AES-256-GCM |
| Master key | Supabase user_keys |
✅ Wrapped twice: under your password and under your recovery phrase |
| Master key (cache) | OS secure storage | |
| Note lock password | Local + Supabase | ✅ Salted PBKDF2 hash, never the password itself |
| Timestamps, sync flags, note type | Local + Supabase | ❌ Metadata, in the clear |
How the key works. A random 256-bit master key encrypts your notes. That key is never uploaded raw: it is wrapped with a key derived from your password (PBKDF2-HMAC-SHA256, 150 000 iterations, random salt) and, separately, with one derived from your recovery phrase. Supabase stores only those two ciphertexts. Signing in downloads the wrapped key and unwraps it locally — your password never leaves the device either.
What this costs you. Lose both your password and your recovery phrase and the notes are unrecoverable. That is the point.
What the per-note lock is not. It is an interface gate over a note that is already encrypted with your master key, not a second layer of cryptography. It stops a glance at an unlocked device; it does not stop someone who has your account password. The trade-off is deliberate: a lock that is genuinely unbreakable is also genuinely unrecoverable.
Pick a type from the editor menu. Switching type never re-encrypts the note.
| Type | What it does |
|---|---|
| Plain text | What you type is what you see |
| Markdown | Write markdown, toggle to a rendered, selectable view |
| Copyable fields | Every line becomes a row with its own copy button — for the notes that are really a pile of values you keep pasting somewhere |
Flutter · Drift (SQLite, WASM + OPFS on the web) · Supabase (Postgres, Auth, Row Level Security) · cryptography (AES-GCM, PBKDF2) · bip39 · flutter_secure_storage
Grab a build from the Releases page, or use the web version — nothing to install, same encryption.
- Android —
.apk - Windows — Inno Setup installer
- macOS — unsigned, so the first launch needs right-click → Open
Requires the Flutter SDK (Dart 3.10+).
git clone https://github.com/MACGalaviz/isan.git
cd isan
flutter pub get
flutter runThat runs against this project's Supabase instance. To point it at your own:
- Create a Supabase project.
- Run the SQL files in
db/, in order, in the Supabase SQL editor:supabase_setup.sql— thenotestable and its RLS policiessupabase_encryption.sql— theuser_keystable and the encryption columnssupabase_note_types.sql— thenote_typecolumn
- Replace
urlandanonKeyinlib/main.dartwith your project's URL and publishable key.
The publishable key is safe to ship: every table is behind Row Level Security, and the rows it can reach hold ciphertext anyway.
flutter build apk --release # Android
flutter build windows --release # Windows (then installers/isan_script.iss)
flutter build macos --release # macOS (needs a Mac)
flutter build web --release # WebSigning, installers and the auto-update flow are documented in docs/DEPLOYMENT.md.
After changing lib/db/database.dart, regenerate the Drift code:
dart run build_runner build --delete-conflicting-outputsflutter test21 tests over the crypto: AES round-trips, tampering and wrong keys, key derivation, the session key, the two-slot master-key scheme, and the per-note lock hash. The sync layer is not covered — it needs Supabase and platform storage.
lib/
db/ Drift schema and generated code
models/ Note model and note types
screens/ Home, editor, auth, profile, password reset
services/
security/ Key manager, derivation, encryption, session key, note lock
... Database, Supabase, update checker
test/ Crypto and note-lock tests
db/ Supabase schema, idempotent, safe to re-run
docs/ Deployment guide and work plan
installers/ Inno Setup script for the Windows installer
Feature-complete on the platforms above. What is left is internal — central state management and tests for the sync layer — and it is tracked in the work plan.
iOS is not planned: distributing it needs a paid Apple Developer account, and this app does not justify one.
MIT © Miguel Cabañas