Security

What it canand cannot do.

Tallyduck reads payment accounts. Before anyone connects one, this page says exactly what that means, how the keys are held, and how one account is kept from reading another.

Tallyduck is not open yet. Everything below describes how the product is built, and it holds nobody's credentials today.

It cannot move money

This is the part worth checking rather than believing. Tallyduck asks for read access and nothing else, and there is no code in it that could move money even if a key allowed it. Every call it makes to Stripe, PayPal or Wise is a read: balances, payments, profiles. The only requests that are not reads are the OAuth token exchanges PayPal requires in order to hand out an access token, which is authentication, not a transfer.

There is no transfer endpoint, no payout call, no refund call. Not disabled, not behind a flag. Absent.

The key you generate stays yours. Stripe and Wise both let you issue a key restricted to reading, and that is the key to give. Payoneer has no API at all, so nothing is connected there: you upload a statement and Tallyduck reads the file.

How the keys are held

Platform credentials are encrypted with AES-256-GCM, and the key that encrypts them is not the key stored beside them.

Each account gets its own data key. That key is itself encrypted by a master key which lives in the server environment and is never written to the database. Reading the database alone therefore yields nothing usable: the rows are ciphertext and the key that opens them is not in there.

GCM rather than a plain cipher because it authenticates as well as encrypts. A record altered in storage fails to decrypt rather than decrypting into something wrong.

Wrapped keys carry the master-key generation that produced them, so the master key can be rotated one account at a time instead of every credential in the database being re-encrypted at once with the service down.

One account cannot see another

Isolation is enforced by the database, not by remembering to write the right query. Every route that touches account data runs inside a transaction bound to that one account, and the database refuses rows belonging to anyone else.

The difference matters: a filter a developer forgets is a leak, and a rule the database enforces is not something a developer can forget. A query written without the right scope returns nothing rather than returning somebody else's payments.

Passwords and sessions

Passwords are hashed with bcrypt at twelve rounds. They are never stored, never logged, and cannot be read back out, including by us.

Sessions use short-lived access tokens that expire in minutes rather than days, so a token that leaks is worth very little by the time anyone finds it.

This website

No analytics script, no advertising pixel, no third-party tracker, and nothing stored on your device. That is why there is no cookie banner to click: there is nothing to consent to.

The one third party the page loads is Cloudflare's anti-spam check on the waitlist form, which is configured so that it sets no cookie. Everything else, including the fonts, is served from this domain.

The site is served over HTTPS only, declares a content security policy that blocks scripts from anywhere but itself, refuses to be framed by another site, and sends no referrer to third parties.

The waitlist is deliberately a separate system from the product database. Unsubscribing can never delete an account, and deleting an account can never silently drop you off the list. Neither cascades into the other, by construction.

Reporting something

[email protected] reaches a person, not a queue. If you think you have found a way to read data that is not yours, say so plainly in the subject line and it goes to the top of the pile.

Last updated 17 August 2026 · Covers the site and the product as built

Tallyduck. ← Tallyduck