This page is specific on purpose. "Industry-standard encryption" tells you nothing; the algorithm, the key length and where the key lives tell you whether to trust us. Every figure below is read from the code by a test, so the page cannot quietly drift from what runs.
Credentials you connect
Three kinds of secret can be handed to us, all optional:
| Secret | Given when | Used for |
| Google refresh token | You connect Search Console or Google Analytics | Fetching your search terms or importing GA4 history on your behalf |
| Payment provider API key | You connect Stripe, Lemon Squeezy, Paddle or Polar | Reading payments to attribute revenue to visits |
| Payment webhook secret | Same connection | Verifying that incoming payment events really came from the provider |
How they are stored
AES-256-GCM, at rest, with a per-record random nonce.
The cipher is AES in Galois/Counter Mode with a 256-bit key. GCM is authenticated: a stored value that has been altered — by a fault or by hand — fails its authentication tag and is refused rather than decrypted into garbage.
Every encryption draws a fresh 96-bit nonce from the operating system's random source. Two identical tokens stored twice produce two unrelated ciphertexts.
The stored value carries a version prefix, the nonce, the authentication tag and the ciphertext, hex-encoded. The database enforces that shape with a check constraint, so a plaintext credential cannot be written to the column at all — a bug that tried would be rejected by the database, not discovered later.
Where the key lives
Outside the database. The 256-bit key is an environment variable on the application server and is never written to the database, to logs, or to any file in the repository. Reading a database backup, or the database itself, yields ciphertext that cannot be opened without the server's key.
Google tokens and payment-provider secrets use two separate keys, so a compromise of one does not open the other.
When they are decrypted
Only in server memory, only for the duration of one API call — refreshing a Google access token, fetching a report, verifying a webhook signature. The decrypted value is passed to the outbound request and discarded. It is never logged, never returned to the browser, and never cached.
Revocation
Disconnecting an integration revokes our access at the provider and deletes the stored credential. If you revoke access on Google's side first — from your Google account's list of connected apps — Google sends us a signed security event and we delete the credential at once; we subscribe to Google's Cross-Account Protection for exactly this. Should that notice not arrive, our next attempt to use the credential fails and we delete it within the hour. See the privacy policy for what stays (fetched data) and what goes (the credential).
Credentials we never hold
Your password. Sign-in is handled by our authentication provider; we see a session, never a password. Signing in with Google leaves us no lasting Google token at all.
Card numbers. Checkout runs on Stripe's pages; we store an identifier that points at your Stripe customer record and nothing from the card.
Visitor identities. The tracking script sends no name, email or cross-site identifier, and the collector never stores an IP address — only a salted hash that rotates daily. See what the script sends.
Access to your data
Every table is protected by row-level security: a query from your session can return only rows belonging to organisations you are a member of, and the database enforces this — not application code.
Site-level scoping restricts a team member to the sites you choose, again enforced by the database.
API keys are stored as SHA-256 hashes; the key itself is shown once at creation and cannot be recovered from our records.
Staff do not read customer data except to investigate a fault you have reported, and then only the data needed for that fault.
Reporting a vulnerability
Write to security@truestat.io. We answer within two working days and do not pursue researchers acting in good faith.