Privacy 🔏
This page describes what SigNote stores, what it never stores, and who else sees any of it. It's written to be checkable: every claim here corresponds to code you can read, and the table at the bottom tells you where to look.
The short version:
- Your notes are yours. Tiers 2 and 3 are encrypted in your browser; the server only ever holds ciphertext.
- There is no password, anywhere. An email address is optional too — sign in with a wallet and SigNote never learns one. Nothing is sold or shared for advertising.
- What is collected is small and boring: enough to sign you in, enough to show you your own devices, and product analytics that never contain note bodies.
⚠️ Placeholders. The controller entity, contact address, and governing jurisdiction below still need to be filled in before this page is treated as a published policy. Everything factual about data handling is accurate as of the current code.
Who this covers. These practices describe the hosted SigNote instance. If you run your own, you are the operator of your data and this page describes only the defaults you inherit from the code.
What SigNote stores 📦
Your account
| Field | Where it comes from |
|---|---|
| Internal user ID | Generated at first sign-in |
| Display name | Your wallet address, or your Google profile name |
| Provider identity | One row per linked method: a wallet address, a Google account ID, or an address |
| Email address | Only once you prove you control it — an emailed code, or Google saying it is verified. A wallet-only account has none. |
| Passkey credential | Public key, usage counter, device/sync metadata, and the nickname you give it. The private key never reaches SigNote. |
| Raw provider profile | The profile object Google returned, stored as-is |
| Sign-in codes | A keyed hash of the live code, never the code — deleted once used or expired |
| Created / updated stamps | Timestamps |
There is no password field anywhere in the schema, because there is no password. → Sign in
Your notes
| Tier | Title | Body | Also stored |
|---|---|---|---|
| 1 — Notes | Plaintext | Plaintext | Colour, pattern, pinned, archived, expiry, burn flag |
| 2 — Secrets | Plaintext | Ciphertext + IV | Same metadata |
| 3 — Seals | Plaintext | Ciphertext + IV | Same metadata, plus the wrapped per-note key |
Three things are plaintext on every tier, and it's worth repeating:
- Titles. Searchable by design, and never encrypted.
- Tags. Names and colours, on all three tiers. → Tags
- Metadata. Colour, pin state, timestamps, expiry.
Don't put a secret in a title or a tag name.
Tier 1 titles and bodies are additionally kept in a full-text search index so search can work server-side. Tier 2 and 3 search matches titles only, because the bodies are ciphertext.
Version history stores past revisions in the same shape as the current note — plaintext for Tier 1, ciphertext for Tiers 2 and 3.
Your files
File bytes live in S3-compatible object storage. For Secrets and Seals they are encrypted in your browser before upload; for Notes they are stored as-is. → File uploads
The metadata is always plaintext, on every tier: filename, byte size, MIME type, and which note it belongs to. A file called passport-scan.pdf announces itself by its name even when its contents are encrypted.
Your encryption profile
salt, the KDF parameters, serverShare, and keyCheck. None of these can decrypt anything on their own — that is the entire point of the two-share design. → Security
Your sessions
Each active sign-in gets a row so you can see and revoke your own devices:
| Field | Why |
|---|---|
| Session ID, provider | Identify the session |
| Client (web or desktop) | Label it in your device list |
| IP address | Shown to you; stored in full, not truncated |
| User agent, browser, OS | Human-readable device label |
| Created, last seen, expiry | Session lifetime |
Sign-in nonces and desktop sign-in attempts also record an IP address for the few minutes they exist, to make replay harder.
What SigNote never stores 🚫
- Your passphrase. It is processed in your browser and never transmitted.
- Your
deviceShare. It lives insessionStorageon your device only. - Your Master Encryption Key. It exists in browser memory, briefly, and nowhere else.
- Plaintext Secret or Seal bodies, or plaintext of the files attached to them.
- Your wallet's private key. Signing happens in your wallet; SigNote sees a signature.
- Payment details. There is nothing to pay for.
Because of the first three, nobody can reset your passphrase — not you, not an admin, not a court order served on the server. → Recovery
Analytics 📊
SigNote uses PostHog (US region) for product analytics. Requests are proxied through this site's own /ingest path rather than going to a third-party domain directly.
You are identified to PostHog by your internal user ID and display name. For a Google account that display name is the name Google gave; for a wallet, it's your address.
What is captured:
| Kind | Examples |
|---|---|
| Named product events | sign_in_started, vault_unlocked, vault_locked, search_opened, file_uploaded, passphrase_changed, recovery_completed, account_erased |
| Errors | Exception messages and stack traces, so crashes get fixed |
Page views, click/form autocapture, rage clicks, heatmaps, dead clicks, Session Replay, and network request body/header recording are disabled in the application code. Those local settings are fail-closed: a PostHog dashboard configuration cannot turn them back on. Every remaining event also has URL-derived properties removed before it is sent, so search terms and note route identifiers do not become analytics properties.
Note bodies, titles, tags, and Secret or Seal content (encrypted or decrypted) are never sent to analytics. Named event properties are deliberately coarse — file_uploaded, for instance, records only a MIME category and whether the file was encrypted.
What lives on your own device 💾
| Store | What's in it | Cleared when |
|---|---|---|
sessionStorage | deviceShare | Tab closes, hard lock, sign-out |
| Browser memory | The MEK and any decrypted bodies | Lock, tab close, 5 min idle |
| IndexedDB | Cached server responses plus the encrypted Authenticator cache and its non-extractable trusted-device key | Sign-out/session termination; response cache also expires after 7 days |
| Cookies | The session JWT (7 days) and PostHog's analytics cookies | Expiry or sign-out |
Decrypted Secret and Seal bodies are held in memory only — they are never written to IndexedDB. → Threat model
Who else is involved 🤝
Running the hosted instance means a handful of other companies process some of this data:
| Provider | What they handle |
|---|---|
| Vercel | Hosting and request logs |
| Supabase | The Postgres database |
| S3-compatible storage | File attachment bytes |
| PostHog (US) | Product analytics |
| Resend | Delivering email — sign-in codes and notifications |
| OAuth sign-in — only if you choose it | |
| WalletConnect | The connection to your wallet — only if you use one |
Nothing is sold, and nothing is shared with advertisers. There is no ad network in this app.
How long it's kept ⏳
| Data | Retention |
|---|---|
| Notes, secrets, seals | Until you delete them |
| Deleted notes | Purged about an hour after deletion by the cleanup sweep |
| Notes with an expiry or burn flag | Purged about an hour after they expire or are read |
| Version history | A capped number of recent revisions per note |
| Sessions | Deleted at expiry (7 days) or when you revoke them |
| Sign-in nonces, desktop attempts | Minutes |
| File attachments | With their note; orphans are collected by the same sweep |
| Analytics events | Per PostHog's project retention — not removed by account erase |
Your choices 🎛️
- Pick the right tier. Anything you don't want the server to be able to read belongs in a Secret or a Seal, not a Note.
- Sign in with a wallet if you'd rather not hand over an email address at all.
- Turn off optional email. Product news and new-sign-in alerts are both switchable under Notifications. Sign-in codes are not optional — switching those off would lock you out.
- Revoke a session from your devices list the moment something looks unfamiliar.
- Erase selectively. You can erase your seals, secrets, notes, files, or your encryption profile independently — or the whole account, which deletes the user row and every linked identity.
- Run your own instance if you'd rather not trust anyone else's. → Self-hosting guide
Account erase is immediate and irreversible. It does not reach into analytics events already collected, or into provider-side backups that exist for disaster recovery.
Children
SigNote is not directed at children and no account is knowingly created for anyone under the age required by local law.
Changes
Material changes to this page will be noted in the repository history, which is public — the file is src/docs/1.privacy.md and every revision to it is in the git log.
Contact
Placeholder: controller entity, postal address, and a contact email for privacy requests.
For anything about the code itself, open an issue: 👉 github.com/irondsd/signote
Verify it yourself 🔍
| Claim | Where to read the code |
|---|---|
| Exactly which columns exist, on every table | src/db/schema.ts |
Passphrase, deviceShare and MEK never leave the browser | src/lib/crypto.ts, src/contexts/EncryptionContext.tsx |
| Which analytics events are sent, and their properties | Search the repo for posthog.capture |
| Analytics configuration and privacy invariants | instrumentation-client.ts |
| What the cleanup sweep deletes, and when | src/controllers/cleanup.ts |
| What account erase actually deletes | src/controllers/erase.ts |
| What is persisted to IndexedDB | src/providers/QueryPersister.tsx |
| Session fields shown in your device list | src/controllers/authSessions.ts |
Where to go next
- 🛡️ The security guarantees behind all this → Security
- 🧭 What SigNote does not protect against → Threat model
- 🔐 The cryptography in detail → Encryption
- 🚀 Run it yourself → Self-hosting guide