Security
What we protect, how we protect it, and the tradeoffs we've made honestly.
Report a security issue
If you've found a vulnerability or potential security concern, please email us directly. We'll respond within 48 hours.
Overview
MyBacked is a record-keeping tool for poker staking. We don't hold money, process payments, or take a cut of your action. The data we do hold — session results, deal terms, settlements — sits behind standard SaaS protections: TLS in transit, encryption at rest at the database layer, and Row Level Security so users can only see data tied to stakes they're party to.
On top of that baseline, we offer Encrypted Stakes — an opt-in mode where session amounts are encrypted client-side before they ever reach our servers. This page explains how that works and where the limits are.
Encrypted Stakes — what it does
When a stake is created with encryption enabled, the dollar-value fields throughout that stake are encrypted in the user's browser using AES-256-GCM with a key the server never persists. The encrypted ciphertext is what gets stored in our database — anyone reading the raw database row, including a Supabase administrator with full database access, sees an opaque blob, not a number.
The key itself lives in IndexedDB on each participant's device. When you load a stake page, the browser decrypts the numbers locally and renders the decrypted values into the page.
What is encrypted at rest
- Session financials: buy-ins, cash-outs, payouts
- Rakeback entry amounts
- Money-movement transfer amounts
- Settlement records: per-backer profit, player profit, makeup at settlement, amount owed
- Per-slice migration values: starting P/L, starting money balance, prior all-time P/L
- Stake-level migration values: player starting money balance, all-time P/L
What is NOT encrypted at rest (visible to us in plaintext)
- Stake metadata: name, notes, status, start/end dates, currency, deal type defaults
- Slice metadata: investment percentage, deal type, profit-split percentage, markup rate
- Session metadata: date, game type, stakes description, location, tournament name, finish position, hours played, session notes
- Participant identities: display name, email, avatar, the list of who's on the stake
- Chat messages between participants on a stake
- Audit-trail entries (Edit Stake / Forgive Makeup change history) — the dollar values being changed currently appear in the audit table in plaintext, even on encrypted stakes. This is a known limitation we'll close in a follow-up.
The short version: anything that's a dollar amount on the live stake state is encrypted. Everything that describes what kind of game/deal/edit it was, or who's involved, is plaintext.
Transient processing windows
Encryption is at rest — the persisted database row is ciphertext. For most actions on encrypted stakes (logging a session, recording a regular settlement), the client encrypts the values before the network request leaves the browser, and the server never sees plaintext.
Three specific actions are exceptions where decrypted values briefly pass through our server's request-handling memory before the response returns:
- Migration settlement. The historical-record action used when you import a stake whose period was already settled off-platform. The client ships the decrypted session amounts alongside the encrypted blobs so the server can validate the engine math against what gets persisted. Plaintext exists in Vercel function memory for the duration of the request, then the request ends and that memory is reclaimed. No plaintext is logged or written to disk.
- Transfer logging. The plaintext amount is shipped alongside the ciphertext so the server can validate it's positive and build the in-app notification text the receiver sees. The amount is persisted only as ciphertext.
- Rakeback logging. Same pattern as transfers — plaintext used for the per-slice direct-cut breakdown returned to the client; only ciphertext persisted.
In all three cases the plaintext window is the duration of a single HTTP request, never written to logs or storage. A motivated attacker would need to be running code inside Vercel's request handler at the exact moment of a request to capture it — the same class of risk as any TLS-protected web app. We rate it meaningfully lower than the database-at-rest exposure the encryption is primarily designed to protect against.
Backfill for legacy data
The full list of encrypted-at-rest fields shipped in stages over several recent releases. Encrypted stakes created before each feature shipped may still hold legacy plaintext values for the newly-covered fields.
When a participant with the key opens such a stake, their browser detects the legacy plaintext, encrypts it with the per-stake key, and POSTs the ciphertext to the server. The server stores the ciphertext and zeroes the plaintext column in the same request. The conversion happens silently — there's no user-facing prompt or action.
Until someone with the key opens a given legacy stake, its pre-feature plaintext values remain in our database. We expect the backlog to drain naturally over normal usage in the first days after launch.
How the key gets to other participants
Encrypted stakes typically have multiple participants (a player and one or more backers). Every participant needs the same key to decrypt the same data. We deliver the key through three channels, in priority order:
- URL hash in invite email. When you invite a backer, the email contains a link like
https://www.mybacked.com/invite/{code}#key={...}. The fragment after the#is never sent to our server in HTTP request headers — that's a guarantee of the URL standard. The invitee's browser reads it locally and stores it in IndexedDB. - Manual paste. If the email never arrives or the link is missed, the inviter can share the key out-of-band (Signal, iMessage, in person) and the invitee pastes it on the invite page.
- Email resend on request. The invitee can request the key from the inviter; the inviter clicks "Resend invite email" on a dedicated page, and the original invite email (containing the key in the URL hash) is re-sent to the legitimate invited_email only. The resend goes through the same email channel as the original invite (with the same exposure properties described below); it does not broadcast over a shared channel that other listeners could intercept.
Known limitation: email-channel exposure
We want to be honest about a tradeoff in the email-delivery path above.
While the URL fragment never reaches ourserver, the full URL string — including the key — does pass through our email provider (Resend), the recipient's email service (Gmail, Outlook, etc.), and any backup or archive system the recipient's inbox feeds into. An attacker who later compromised any of those systems could recover the key.
On its own, that's not enough to read the data. Decrypting an encrypted stake also requires access to the encrypted ciphertext stored in our database. The realistic exposure is the long-tail joint risk: an email or Resend incident combined with a future Supabase breach could expose historical data that was encrypted at the time it was created.
We rate this as a medium-impact concern given the staking-tracking use case (not a bank, not health records, not government data) and the fact that both compromises would need to happen for any real exposure.
What you can do today
- Use manual paste delivery instead of email when you create an encrypted invite for particularly sensitive arrangements. Share the key with the invitee through Signal, iMessage, or in person.
- Delete the invite email after the invitee accepts. Once the key is in their browser's IndexedDB, the email copy isn't needed and continuing to retain it adds long-tail risk for no benefit.
- Coordinate out-of-band before sending the invite. If you can text or message the invitee directly, send the key through that channel separately and uncheck the email send option (planned hardening, see below) — or pick the contact-only invite path which doesn't auto-email.
Planned hardening
In the order we expect to ship them post-launch:
- No-email invite option. Toggle on encrypted-invite creation that suppresses the email entirely; the inviter shares the link manually through their preferred channel.
- Key rotation. Ability to change the encryption key on an existing stake, invalidating any old keys that may have leaked while preserving access for current participants.
- Split-key delivery.XOR the key into two shares and send them through different channels (email + SMS, or email + a code displayed on screen for the inviter to read aloud). Either share alone is useless. We're weighing this against UX cost and will likely make it an opt-in rather than a default.
Other security details
Authentication. Email + password via Supabase Auth. Email confirmation is required before a new account can sign in. Sessions auto-expire after 2 hours of inactivity.
Database access. Row Level Security policies restrict every database read and write to data the authenticated user is a participant in. Server-side admin operations (notifications, cron jobs, encrypted-stake aggregations) use a separate service-role credential that bypasses RLS but is only invoked from server-side code.
Payments. All payment processing happens at Stripe. We never see card numbers; we only store Stripe customer and subscription IDs.
Infrastructure. Hosted on Vercel (serverless, no persistent compute), database on Supabase (Postgres with daily 7-day backups), email via Resend. All connections are TLS-encrypted in transit. Database is encrypted at rest by Supabase.
Legal
Terms of Service · Privacy Policy · Support
Backed LLC
Las Vegas, Nevada