# Five keys come out of one 12-word phrase, and each has its own label

> LifetimeLabsDev open sourced the whole PrivacyNotes client on 31 August 2026 and published the cryptographic core beside the threat model so the privacy claims can be checked. A 128-bit BIP-39 mnemonic becomes a 64-byte seed, and every key is derived from it by HKDF-SHA256 under a separate info string, so no two uses ever share key material.

**LifetimeLabsDev/PrivacyNotes.app** — End-to-end encrypted notes, tasks, files, passwords, journal and bookmarks. Keyed by a 12-word BIP-39 phrase: no email, no password, no account to leak. The full client source for web, desktop and mobile is published, along with the encryption (XChaCha20-Poly1305, HKDF-SHA256, Ed25519) and the threat model, so the privacy claims are falsifiable.

- Repository: https://github.com/LifetimeLabsDev/PrivacyNotes.app
- Website: https://PrivacyNotes.app
- Stars: 472 · Forks: 37
- Language: TypeScript
- License: AGPL-3.0
- Published: 2026-09-20 · Updated: 2026-09-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/lifetimelabsdev-privacynotes-app

## One seed, five keys, five domain labels

The identity story is short and the interesting part is what happens after it. A 12-word BIP-39 mnemonic carrying 128 bits of entropy is generated on your device and turned into a 64-byte seed through the standard BIP-39 PBKDF2 path. From that seed, every key is derived by HKDF-SHA256, and the detail that matters is that each derivation carries its own domain-separated info string so that no two uses share key material. Five of those labels are enumerated. A signing string produces an Ed25519 private key whose public half is your user ID, so identity is a key rather than a row in a users table. An encryption string produces the 32-byte symmetric key for notes, attachments and images. A local at-rest string produces a key that seals note content inside the device's own storage and is never stored and never transmitted. An auth password string produces the secret for the anonymous account that carries your session. And a fingerprint pepper string produces the value that hashes device-grouping signals so the server sees only hashes.

## Two of those keys never leave the machine at all

The two most interesting entries in that table are the ones describing keys that are not transmitted. The local at-rest key seals note content in the device's own storage, and its description is explicit that it is never stored and never transmitted, which means the server has no copy and cannot be compelled to produce one. That is a materially different claim from at-rest encryption performed by the server, and it is the kind of distinction that gets blurred in marketing copy. The second is the fingerprint pepper, which exists to hash device-grouping signals so the server never sees the raw values. Both are consequences of deriving keys lazily per purpose rather than using one key everywhere, which is the same property that makes revocation possible for one class of data without invalidating another. There is a third entry in the same spirit that is not a key at all but a related mechanism: the auth password secret, used for the anonymous account that carries your session. That one does travel, because the server has to verify it, which is a fair trade for the account existing at all in a design whose premise is that you have no account.

## A 24-byte nonce is what makes random generation safe here

Notes are encrypted with XChaCha20-Poly1305 under a random 24-byte nonce, and the reasoning behind the nonce length is given directly rather than left to the reader. A 24-byte nonce is what makes random generation safe without collision risk, in contrast with AES-GCM's 12 bytes. That comparison is the substantive point: with a smaller nonce and a random generator you eventually reuse one, and a reuse under the same key is catastrophic for confidentiality in a way that no amount of ciphertext randomness repairs. The plaintext payload is specified as well, and the list is broader than a title and a body. It includes title, body and tags, plus metadata such as whether the note is trashed and whether it is starred, so state flags are inside the encrypted payload rather than leaking as server-visible columns. Attachments and images use the same cipher through a second file that runs about fifty lines, meaning one construction covers text and binary rather than two schemes that would need separate review.

## Signatures bind to a session, so a captured one cannot be replayed

Account binding is done with signatures rather than with a shared secret, and the shape of the signature is what does the work. Public key to account binding uses Ed25519 signatures over structured challenges, expressed as a link string carrying an authentication user identifier. Device registration and revocation use the same structure, which means one mechanism covers three operations rather than three bespoke flows. The property claimed for this design is specific: every signature binds to the session's authentication user identifier, so a captured signature cannot be replayed into a different session. That is the difference between a bearer token and a challenge response, and it is the kind of detail that only shows up in a published cryptographic core. Reading it is possible because the repository ships the file the application actually uses, described as around 560 lines and commented step by step, with the canonical copy inside the shared package in the client tree and the root copy there so the link stays short.

## Custodial mode is the exception, and it is preselected by social login

The central claim is that under self-custody your phrase never leaves your device, and the repository is careful about the boundary of that claim. Self-custody is what every phrase sign-up uses. Its exception is custodial mode, and the sentence about the exception is placed immediately after the claims table rather than in a footnote, which is the correct place for it. What matters practically is the trigger: a sign-up with Google, Apple or GitHub preselects custodial mode. So the mode you get is not chosen by a settings page after the fact, it is chosen by which sign-up path you took, and a user who reaches for the convenient social button first has silently taken the weaker guarantee. If you are evaluating this app, that single fact outweighs any cipher choice, because it determines whether the phrase ever crosses the network. The claim that notes are encrypted before reaching the server is not affected by the mode, since that path is the same either way.

## The repository exists so the claims can be falsified

The framing is unusually direct about why the code is public. Privacy claims need proof, and the parts you actually have to trust are named as the client applications, the cryptographic core and the threat model, described as the real files that ship rather than summaries. Three claims are listed with a place to check each one: the derivation strings in the live bundle for the phrase claim, the XChaCha20 path in the crypto file for the encryption claim, and a verification document tier for watching your own notes leave in the browser's network tab. The stated consequence is the point of the whole exercise: if any of those stops holding, the claim is broken and you can prove it. The documents are split by audience. The security document covers what is protected against, what is not, and what the server can see, written for users. The threat model covers trust boundaries, cryptographic detail and known limitations including the unflattering ones, written for auditors, and it is the recommended starting point for a reviewer. There is also a verification document, a notice file, a contribution guide and a documentation directory. The server-side claim has a concrete illustration attached to it, which is the sort of thing most privacy pages assert and none demonstrate: real rows from the notes table in production, showing a public key, a ciphertext and a nonce, with no title, no body and no tags. That is what the storage claim looks like when it holds, and it is checkable against the live system rather than against a diagram. The repository was opened up on 31 August 2026, which makes it a young publication of a system that has been running, so the interesting question for a reviewer is not whether the code was written carefully but whether the running deployment matches the code, which is exactly what the network tab check is for. Releases have been frequent since, with versions in the 0.5 range cut through September 2026.

## Four zero-dependency packages, and a scratch directory to test them

The cryptography comes from four packages, named explicitly so a reviewer can audit them rather than the app. They are the BIP-39 mnemonic package, the hashes package, the Ed25519 package and the ciphers package, all from the same two families of well known audited implementations. Each is described as having zero dependencies of its own, written to be auditable, and deployed in production by major wallets. The zero dependency claim is the part that matters for review, since a package that pulls in a transitive tree cannot be audited by reading one file. The repository also asks you to run the derivation yourself rather than only read it, in a scratch directory outside the checkout:

```bash
mkdir -p /tmp/pn-verify && cd /tmp/pn-verify
npm install --silent @scure/bip39@^2 @noble/hashes@^2
```

The stated framing is that this runs the same path the app uses, from the same audited packages, in a directory you can delete afterwards. That is the right shape for a cryptographic claim: nothing here asks you to trust the build, only to read the code and run it yourself. The full command that follows installs those two packages and drives the derivation inline, and the copy of it in the README is cut off partway through, so the exact invocation is there to be pasted rather than reconstructed. That is a small blemish on an otherwise unusually careful document, and worth noting only because the surrounding text invites you to copy the whole block. If you do, you will find the trailing part missing rather than silently wrong. The other half of the review offer is the client source itself, which can be built with a package install and a build command inside the application directory, so a reviewer who wants to confirm the crypto is wired in on every path can read the call sites rather than take the word for it.

## Conclusion

Read this if you are deciding whether to trust an end-to-end encrypted notes app, because this repository is set up for exactly that: the production crypto file rather than a reference implementation, a threat model written for auditors, and a browser tab you can watch for yourself. It is also a good template for how to make privacy claims checkable. Three things to weigh before you rely on it. Custodial mode, because signing up with Google, Apple or GitHub preselects the mode in which your phrase can leave your device, and that is the exception to the central claim rather than a detail. The scope of what the server can still see, which the security document covers and which no amount of client code can hide. And the licence, since the client is AGPL-3.0, so anything you build on it inherits copyleft obligations.

## FAQ

### What is PrivacyNotes.app and how does it identify me?

An end-to-end encrypted notes, tasks and journal app for macOS, Windows, Linux, Android, iOS and the browser, with a password vault and file attachments under the same key. Instead of an account you get a 12-word BIP-39 phrase carrying 128 bits of entropy, generated on your device, so there is no email, no password and no account to leak.

### What happens to my phrase if I use Google or Apple sign-in?

A sign-up with Google, Apple or GitHub preselects custodial mode, which is the stated exception to the claim that your phrase never leaves your device. That exception holds for every phrase sign-up, so the mode is determined by which sign-up path you took rather than by a setting you can change later.

### How can I verify that PrivacyNotes encrypts my notes?

Three checks are named. The server stores only ciphertext is verified by watching your own notes leave in the browser's network tab using the verification document. The other two claims are checked by reading the production crypto file and, for the key derivation, the derivation strings in the live bundle.

### Which encryption does PrivacyNotes use?

Notes, attachments and images are encrypted with XChaCha20-Poly1305 under a random 24-byte nonce, which is what makes random generation safe without collision risk unlike AES-GCM's 12 bytes. Keys are derived from the phrase seed with HKDF-SHA256 under separate domain-separated info strings, and account binding uses Ed25519 signatures.

### Which platforms does PrivacyNotes run on?

Signed apps for macOS, Windows, Linux, Android and iOS, or it runs in the browser. The full client source for web, desktop and mobile has been public since 31 August 2026, under the AGPL-3.0 licence, with the client, the cryptographic core and the threat model in the same repository.

## Sources

- [License: AGPL-3.0](https://github.com/LifetimeLabsDev/PrivacyNotes.app/blob/main/LICENSE)
- [LifetimeLabsDev/PrivacyNotes.app on GitHub](https://github.com/LifetimeLabsDev/PrivacyNotes.app)
- [Project website](https://PrivacyNotes.app)
- [README](https://github.com/LifetimeLabsDev/PrivacyNotes.app/blob/main/README.md)
- [Releases](https://github.com/LifetimeLabsDev/PrivacyNotes.app/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/lifetimelabsdev-privacynotes-app
