SigNoteSigNote
Documentation

Security ๐Ÿ›ก๏ธ

SigNote is built around a simple promise: the server cannot read what you don't want it to. This page is the short tour of how that promise is kept โ€” across sign-in, the three note tiers, your encryption profile, and what happens if you forget your passphrase.

If you want the implementation-level details, follow the links into Encryption and the Threat model.


Why you can trust SigNote

Trust in a notes app should not come from marketing. It should come from properties that are verifiable by anyone who cares to look. SigNote is designed around a small set of those properties:

1. The server never sees your passphrase

Your passphrase is processed entirely in your browser. It is fed into PBKDF2 with 600,000 iterations of SHA-256 to derive a 32-byte deviceShare. The passphrase itself is never sent to the server, never logged, and never written to disk.

2. The server never sees a key that can decrypt your notes

Encryption keys for Secrets and Seals are built from two shares:

MEK = deviceShare XOR serverShare

The server stores only serverShare. Without your passphrase, serverShare alone is useless โ€” it cannot reconstruct the Master Encryption Key (MEK). The MEK only exists, briefly, in your browser's memory while you have the app open and unlocked.

3. The server never sees plaintext for Secrets and Seals

For Tier 2 (Secrets) and Tier 3 (Seals), the note body is encrypted with AES-GCM 256-bit in your browser before it leaves the device. The same is true for file attachments. The server stores ciphertext and an IV. That's it.

4. Your sign-in cannot be phished into another site

Sign-In with Ethereum (SIWE) signs a message that is cryptographically bound to the app's domain. A signature obtained on evil-signote.com will be rejected by the real SigNote server.

5. Standard, audited primitives only

All cryptography uses the browser's built-in Web Crypto API: PBKDF2, HKDF, and AES-GCM. There are no custom crypto libraries, no hand-rolled ciphers, no "trust us" black boxes.

6. You can verify everything yourself

SigNote is open source. The code that runs in your browser, the code that runs on the server, and the code that handles every byte of your encrypted data โ€” all of it is public. See the Open source and verifiable section below.


Sign-in: who you are

SigNote has several ways to prove your identity, and none of them is a password:

MethodWhat it isWhat the server gets
Email codeA six-digit code mailed to your addressYour email address, and a hash of the code
Google OAuthStandard OAuth 2.0 flow with GoogleEmail, name, profile picture
Sign-In with Ethereum (SIWE)You sign a domain-bound message with your walletA signature and your wallet address
PasskeysA domain-bound key held by your device or password managerA public key and usage counter

Key properties:

  • No passwords. No method involves a password that SigNote stores or transmits.
  • No password reuse risk. Passkey and wallet private keys never reach SigNote, Google credentials never reach SigNote, and a mailed code is stored only as a keyed hash and dies after one use.
  • Anonymity is available. SIWE involves no email address at all, so an account created with a wallet identifies you to nobody.
  • Domain-bound SIWE. A signature for one origin cannot be replayed against another.
  • Sessions are short-lived. JWT sessions are valid for 7 days, then expire.
  • Sign-in is separate from encryption. Logging in does not unlock your Secrets or Seals โ€” that requires your passphrase, every new tab.

โ†’ Full breakdown: Sign in


The three tiers

Different notes have different sensitivity. Encrypting every grocery list is overkill, and putting a recovery phrase in a searchable plaintext field is dangerous. SigNote lets you pick the right level per note:

TierWhat the server storesSearchableKey model
1 โ€” NotesPlaintext title + bodyTitle and bodyNone
2 โ€” SecretsPlaintext title, ciphertext bodyTitle onlyOne shared session key (per user)
3 โ€” SealsPlaintext title, ciphertext body, wrapped per-note keyTitle onlyUnique per-note key, wrapped by MEK

Tier 1 โ€” Notes (plaintext, by design)

Tier 1 notes are stored as plaintext. This is intentional: it enables full-text search across titles and bodies. Anything you put in a Tier 1 note is readable by anyone with database access โ€” including you, and including the people running the server you use.

Use Tier 1 for everyday, non-sensitive content.

โ†’ Notes

Tier 2 โ€” Secrets (one session key)

Secrets are encrypted client-side with AES-GCM 256-bit, using a secretBodyKey derived from your MEK via HKDF (info string signote-v1/secret-body). After one unlock, all Secret bodies decrypt in the background โ€” fast to browse.

Trade-off: a single key covers all your Secrets. If that key is ever leaked from your active session, every Secret is exposed.

โ†’ Secrets

Tier 3 โ€” Seals (per-note keys)

Each Seal gets its own 32-byte Note Encryption Key (NEK), generated when the note is created. The body is encrypted with the NEK; the NEK is then wrapped with a sealWrapKey derived from your MEK and the note ID. Seals are never decrypted in bulk โ€” you open one at a time.

The note ID is used as Additional Authenticated Data (AAD) on both the body and the NEK wrap. This cryptographically binds each ciphertext to its specific note: ciphertext from one Seal cannot be substituted into another.

A 60-second auto re-encryption timer also closes an open Seal if you walk away without editing.

โ†’ Seals

File attachments

File attachments to Secrets and Seals are encrypted in the browser before upload: a Secret's with the account's attachment key, a Seal's with that Seal's own note key, the same one as its body. The server only ever stores encrypted bytes, alongside each file's name, type and size in plaintext. โ†’ File uploads

Tags and version history

Tags are plaintext metadata on every tier โ€” like titles, they are never encrypted, so don't put sensitive information in a tag name. Version history inherits the tier's encryption: for Secrets and Seals, version snapshots are stored as ciphertext only, encrypted with the same keys as the current note body.


The encryption profile

The "encryption profile" is the small bundle of material the server needs to help you re-derive your MEK on a new device or new tab. It contains:

FieldWhat it is
salt32 random bytes used in PBKDF2
kdf paramsHash, iteration count (600,000), output length
serverShare32 random bytes (one half of the MEK)
keyCheckA known constant encrypted with a key derived from the MEK

The keyCheck is how the app knows whether you typed the right passphrase: it tries to decrypt keyCheck with the candidate MEK. Success means the passphrase was correct. Failure tells you nothing about the actual key โ€” only that the attempt didn't work.

None of the items above can decrypt your notes on their own. They are useful only in combination with your passphrase, which only lives in your head.

โ†’ Encryption deep dive


Locks: when your data goes back into the safe

Lock typeTriggerWhat's clearedRe-prompts passphrase?
Soft lockTab hidden / window minimisedMEK in memoryNo โ€” uses sessionStorage
Hard lockInactivity 5 min ยท sleep/resume ยท explicit Lock buttonMEK + deviceShare in sessionStorageYes
Tab closeTab/window closedMEK (tab dies) + deviceShare (sessionStorage)Yes (new tab)
Sign-outYou click sign outMEK + deviceShare + local Authenticator vaultYes

โ†’ See the lock matrix in the Threat model.


Recovery: what if you forget your passphrase?

This is where most encrypted-notes apps either lie ("we'll send you a reset email") or shrug ("sorry"). SigNote does neither.

The honest part

Because the server cannot decrypt your notes, the server cannot reset your passphrase. There is no email-based password reset, no admin override, no backdoor. If your passphrase is the only thing on Earth that can produce your deviceShare, and you lose it, your deviceShare is lost too โ€” and so is your MEK.

The pragmatic part: a recovery file

SigNote offers an opt-in recovery file you can download after setting up your encryption profile. The file contains your deviceShare (base64-encoded), the schema type/version, your user ID, and a timestamp. With it, you can regain access even if you forget your passphrase.

Here's how recovery actually works:

  1. You sign in normally.
  2. You upload the recovery file at /recover.
  3. The browser:
    • Reads your deviceShare from the file.
    • Fetches your serverShare from the server.
    • Reconstructs the MEK: deviceShare XOR serverShare.
    • Verifies the MEK against the server-stored keyCheck โ€” if it doesn't match, the recovery file is for an old passphrase and is rejected.
  4. You choose a new passphrase. The app derives a new deviceShare, computes a new serverShare so the MEK stays the same, and updates the encryption profile.

After recovery, your notes are unchanged โ€” only the passphrase that unlocks them has been rotated.

Why a recovery file isn't a backdoor

  • It's opt-in. If you never download it, it doesn't exist.
  • It contains only your deviceShare โ€” exactly the share the server already does not have. Anyone who steals only the recovery file still cannot decrypt your notes: they would also need to sign in to your account and fetch serverShare.
  • Conversely, anyone who only compromises the server still cannot decrypt your notes: they would also need your recovery file.
  • The threat model becomes: an attacker needs both account access and the recovery file. This mirrors the original two-share design, just with the file substituting for memorised passphrase knowledge.

How to store it

A recovery file is a key. Treat it like one:

  • โœ… A reputable password manager.
  • โœ… An encrypted drive or hardware key.
  • โŒ A cloud notes app. โŒ Email. โŒ Chat apps.
  • โŒ Any place that's covered by the same login as SigNote.

โš ๏ธ If you change your passphrase, any older recovery file becomes invalid the moment it fails the keyCheck step. Re-download a fresh backup after passphrase rotation.

โš ๏ธ The same is true, and more so, after changing your encryption keys: that replaces the Master Encryption Key itself, so every recovery file made before it is permanently dead โ€” including one downloaded minutes earlier. The wizard makes you save and re-upload the new file before it will activate anything, and that file is the only one that works afterwards.

Don't want a recovery file?

You don't have to download one. If you'd rather rely on memory alone โ€” accepting that a forgotten passphrase means permanent loss of your encrypted data โ€” that's a valid choice and SigNote supports it.


Open source and verifiable

You don't have to take this page's word for any of it. SigNote is open source. Every claim above is testable against the actual code โ€” and anyone with basic technical skills can do it.

๐Ÿ‘‰ github.com/irondsd/signote

What to look at if you want to verify

ClaimWhere to read the code
All crypto uses the Web Crypto API, no custom librariessrc/lib/crypto.ts
Passphrase is hashed with PBKDF2 (600k iterations) locallysrc/lib/crypto.ts โ€” deriveDeviceShare
MEK is deviceShare XOR serverShare, in memory onlysrc/lib/crypto.ts โ€” xor32, importMEK
The server never receives a plaintext Secret/Seal bodysrc/server/routers/secrets.ts, src/server/routers/seals.ts
Per-note Seal keys are wrapped with a note-ID-derived keysrc/lib/crypto.ts, src/controllers/ for seals
SIWE rejects signatures with mismatched domainsrc/lib/siwe.ts, src/config/auth.ts
Recovery file structure and verificationsrc/lib/recoveryBackup.ts, src/app/(main)/recover/page.tsx
Lock timers, sleep detection, sessionStorage handlingsrc/contexts/EncryptionContext.tsx
Key rotation: staging, atomic activation, cleanupsrc/server/rotation/, src/lib/rotation/

Cheap ways to convince yourself

  • Open DevTools โ†’ Network before saving a Secret. The request body is JSON containing an iv and ciphertext, never your plaintext.
  • Search the repo for "passphrase". You will not find it being sent in any network request โ€” only being read from a local input.
  • Read src/lib/crypto.ts end to end. It's a single file, intentionally small, and is the only place crypto happens.
  • Run it yourself. The self-hosting guide gets you a full local instance, so you can audit what's actually deployed.

Trust, but verify โ€” and improve

If you find a bug, a weakness, an unclear claim, or anything you want to challenge: open an issue or a pull request on GitHub. Security of this kind only improves when people read the code. Reading it is welcome and encouraged.


Where to go next

  • ๐Ÿชช How sign-in works โ†’ Sign in
  • ๐Ÿ”’ Tier 2 details โ†’ Secrets
  • ๐Ÿ›ก๏ธ Tier 3 details โ†’ Seals
  • ๐Ÿ” Full crypto deep-dive โ†’ Encryption
  • ๐Ÿงญ Threat model and trade-offs โ†’ Threat model
  • ๐Ÿš€ Run it yourself โ†’ Self-hosting guide