Security design

Privacy & security gives the short version. This page is the long one, written for people who read threat models before they trust an app with their finances. It describes what is shipped today (October 2026), not what we plan to build, and it ends with the limitations we know about.

Five principles

  1. No account, no server, no permission needed. A local book never talks to anything. Everything else is opt-in.
  2. Encrypt first, then move. Nothing leaves your device unless it is already ciphertext under a key only your devices hold.
  3. We hold no keys. There is no recovery service, no escrow and no backdoor. If you lose your password and your backups, your book is gone. We think that is the right trade for a finance app.
  4. Standard, audited primitives. Argon2id, AES-256-GCM, Ed25519, WebAuthn, SQLCipher — from maintained, independently audited libraries. No home-made cryptography.
  5. Say what it can't do. Security claims without limits are marketing. The last section of this page lists ours.
**Where your data lives.** A local book sends nothing anywhere. The two optional paths, a cloud copy and a shared book, only ever carry ciphertext.
Where your data lives. A local book sends nothing anywhere. The two optional paths, a cloud copy and a shared book, only ever carry ciphertext.

Your book at rest

Each book is one file, encrypted with a key that only exists on your device.

In the browserOn iOS and Android
ContainerAn AES-256-GCM-encrypted image of the database, kept in the browser's own storage for this site onlyAn SQLCipher 4 database (AES-256-CBC with HMAC-SHA-512 integrity)
Data keyA random 256-bit key generated when the book is createdDerived from your password with 600,000 rounds of PBKDF2-SHA-512
Password → keyArgon2id, 64 MiB of memory, 3 passes, a random 16-byte salt per recordPBKDF2 inside SQLCipher (see limitations)
Changing the passwordRe-wraps the data key in milliseconds; the book itself is not re-encryptedRe-keys the database
Other ways to unlockA passkey: the WebAuthn PRF extension turns a passkey plus a random per-book salt into a key that unwraps the data key. User verification is required every time, and the unwrapped key lives only in memoryBiometrics: the OS keychain or keystore holds the unlock secret, bound to this device and to the biometric set enrolled at the time. Excluded from device backups

Every random value — keys, salts, nonces — comes from the operating system's cryptographic random generator. Every encryption uses a fresh nonce. The app checks new passwords for strength and refuses weak ones.

**Unlocking in the browser.** A random data key encrypts the book. Each way you can unlock, password or passkey, holds its own wrapped copy of that key.
Unlocking in the browser. A random data key encrypts the book. Each way you can unlock, password or passkey, holds its own wrapped copy of that key.

A book can also be created without a password. That is a deliberate choice for people who want convenience over protection on a device they trust: such a book is obfuscated, not encrypted, and the app says so where you make the choice.

What leaves your device

A local book: nothing. There is no analytics, no telemetry and no tracking of what you do with your money.

Crash reports are off unless you turn them on, and even then the app asks you at the moment of the crash. A report waits on your device until you choose to send it, and before it is shown to you it is scrubbed: book names, amounts, e-mail addresses, tokens and the addresses of any relay or cloud server are removed.

A cloud copy (optional, premium) uploads the encrypted book file to a folder in your Google Drive or Dropbox account. It is the same bytes under the same key as the book on your device, so the cloud provider holds ciphertext it cannot open. We do not run a storage server. The app signs in with authorization-code OAuth and PKCE; on iOS and Android the refresh token lives in the keychain or keystore. Passkey records are never uploaded.

A shared book (optional, premium) sends encrypted change events to a sync relay. The next section is about that relay.

The sync relay: what it sees and what it can't

Collaboration keeps one book in sync across devices through a relay. The relay is designed as an honest-but-curious service: assume it stores everything it receives and reads everything it can. Here is what that amounts to.

**What the relay sees.** It reads the outside of every envelope and forwards it. It cannot open one.
What the relay sees. It reads the outside of every envelope and forwards it. It cannot open one.
The relay can seeThe relay cannot
An opaque book identifier (a hash of the owner's public key)Read any account, transaction, payee, category, note or amount
Each device's public key, its role (owner or member) and the label you gave itForge a change that your devices would accept as genuine
How many change events there are, their sizes and when they arriveLearn or guess the book password from anything it holds, except by brute force against a weak password (see limitations)
The schema version and the key epoch (a counter that increases when the password is rotated)Add a device, remove a device or rotate keys

Only you can run the roles it cannot: the book's owner device holds a private certificate authority. It issues a signed token to each device it approves, and signs the revocation list the relay enforces. The 24-word recovery phrase you are shown when you set up sharing is the seed of that authority. It lets you manage devices from a new phone; it does not decrypt data, because the data key comes from the book password, which the phrase never contains.

On the wire. Every change event is encrypted with AES-256-GCM under a key derived from the book password with Argon2id; the authenticated header binds the event to the book and its sequence number, so events cannot be replayed into another book or reordered unnoticed. Every request to the relay is signed by the device's own Ed25519 key, with the method, path, host, body hash, timestamp and a one-time nonce under the signature, and the relay keeps a replay cache.

Pairing. An invite is a one-time 128-bit token that expires after an hour. The link carries it in the URL fragment, which browsers never send to servers, so it does not land in anyone's access log. The joining device proves it knows the book password without sending it, both sides show a six-character safety code to compare, and the owner approves with one tap. The joiner also verifies that the authority the relay hands it matches the one in the invite, so a malicious relay cannot substitute its own.

**Pairing a device.** Four steps; the password never travels, and both sides compare a safety code.
Pairing a device. Four steps; the password never travels, and both sides compare a safety code.

Running your own. The relay is a single small binary. We publish signed releases (Sigstore cosign), a one-shot installer that verifies the signature before installing, and a container image. An invite link carries the relay address, so a book paired through your relay never contacts ours. If you would rather not run one, the hosted relay at sync.another.money is open during the beta and runs with the same code, rate limits and request-size caps.

The web app itself

A web app's weakest point is the browser, so the hosted app at app.another.money ships with:

Backups, exports and leaving

Known limitations

These come from two internal reviews (July and October 2026) that drove most of what is described above. We publish them because you cannot evaluate a security claim without its edges.

  1. Shared-book confidentiality is the strength of the book password. All devices on a shared book derive the same key from the same password. There is no forward secrecy, and removing a device does not change the key until you also rotate the password. A revoked device that kept a copy of the relay's ciphertext could try to brute-force a new password offline. Use a long passphrase for a shared book. A per-epoch group key wrapped to each device is the planned replacement.
  2. The web data key does not rotate on a password change. Changing the password re-wraps the same key. If you believe a password and a copy of your encrypted book were both taken, export the book and import it into a new one. A real re-key path is planned.
  3. On iOS and Android the database key is derived straight from the password through SQLCipher's PBKDF2, with no separate data key. Biometric unlock therefore stores the unlock secret rather than a wrapped key, and a stolen database file can be attacked at GPU speed against a weak password. The planned fix is the same envelope the web app already has, with the data key held by hardware-backed storage.
  4. The relay sees metadata. Device labels, timing and sizes are visible to whoever runs the relay. Run your own if that matters to you.
  5. The browser is the trust boundary. A compromised browser, a malicious extension or a device someone else controls can read what you see on screen. The content security policy limits what a web page can do to itself; it cannot protect you from the machine it runs on.
  6. No external audit yet. The reviews so far were internal. An independent review is on the roadmap; the findings will be published here.

Reporting a security issue

Please report security problems privately rather than in a public issue. Use GitHub's private vulnerability reporting on the community repository. We will acknowledge within a few days, fix what needs fixing, and credit you here if you wish.

Report an issue

Found a bug, or something confusing? Tell us. Your report becomes a public issue on the project's issue tracker, readable by anyone, so please don't include names, amounts or other personal details.

We attach the page you're on, your browser and your language. No contact details, account or financial data are sent.