Security
Last updated 25 August 2026 · Soulvaya Labs, Inc. · bellcowgm.com
Written in plain English. This document describes, accurately, how the product actually works. The corporate details — legal entity, state of incorporation, registered address and governing law — are settled and stated in the Terms of Service. Where anything here conflicts with the law that applies to you, the law wins. Questions or corrections: support@bellcowgm.com.
This page describes how Bellcow actually handles sign-in, payments, storage and third-party access. Every claim on it corresponds to something in our source code. Where a control is missing or weaker than you might assume, it says so — a security page that only lists strengths is not a security page.
No passwords. No card numbers. Read-only access.
Bellcow stores no passwords at all, because it never asks for one — not ours, not your fantasy platform's. It never sees your card details, because Stripe handles the entire payment. And it can never act on your fantasy team, because every integration is read-only.
1. Encryption in transit
The site, the app, and every API call run over HTTPS/TLS, terminated by our host, Netlify. Calls out to Stripe, Resend, Anthropic, the fantasy platforms and our data sources are all made over TLS as well. We serve two hardening headers on every response: X-Content-Type-Options: nosniff and Referrer-Policy: no-referrer, the second so that a page URL is never leaked to a third party in a referrer header.
What we do not claim: we do not currently publish a Content-Security-Policy or HSTS preload entry of our own, and we do not perform application-layer encryption on top of our storage provider's. Encryption at rest is whatever Netlify provides for its Blobs service — it is real, but it is theirs, not something we implement ourselves, and we are not going to describe it as if it were.
2. Authentication — no passwords are stored
Bellcow has no password field. Sign-in works like this:
- You enter your email address.
- We generate a random six-digit code using a cryptographic random number generator and email it to you through Resend.
- We store only a SHA-256 hash of that code, with an expiry, an attempt counter and a sent timestamp. The plain code exists in your inbox and nowhere on our side.
- You type the code. We hash what you typed and compare it to the stored hash.
- On success we delete the code record and issue your browser a session token.
The controls around that flow, as implemented:
- Codes expire after 10 minutes.
- Codes are single-use — deleted the moment they are accepted.
- Five wrong attempts and the code is destroyed; you have to request a new one. The app tells you how many attempts remain.
- A 30-second cooldown between code requests for the same address, to blunt email flooding.
- Because there is no password, there is no password to leak, reuse, phish into a reused-credential attack, or breach. There is also no password reset flow to abuse.
The trade-off, stated plainly: your email inbox is your account. Anyone with access to your inbox can request a code and sign in as you. Secure your email — ideally with two-factor authentication on the mailbox itself, which is stronger protection for your Bellcow account than anything we could add on our side.
3. Sessions
A successful sign-in issues a JSON Web Token signed with HMAC-SHA256 (HS256) using a server-side secret. It carries your email address, an issued-at time and an expiry, and nothing else — no payment data, no league data. Every authenticated API call sends it as a Bearer token, and the server verifies the signature using a constant-time comparison before trusting anything in it. An expired or tampered token is rejected outright.
- Sessions last 30 days, then expire and cannot be renewed without a fresh code.
- The token is stored in your browser's
localStorage, not in a cookie. - If the server ever returns 401, the app clears the stored session and asks you to sign in again.
Two limits we are not going to hide. First, because the token lives in localStorage rather than an HttpOnly cookie, script running on the page could in principle read it — which is why we run no third-party scripts, no analytics and no ad tags on the app. Second, there is no server-side revocation list: signing out deletes the token from your browser, but the token itself stays cryptographically valid until its 30-day expiry. If you believe a token has been copied off your device, email support@bellcowgm.com and we will invalidate the session directly.
4. Payments
Bellcow never touches your card. Checkout happens on Stripe's own hosted page. Your card number, expiry, CVC and billing details are entered on Stripe's infrastructure and never pass through, are never seen by, and are never stored on ours. Stripe is a PCI DSS Level 1 service provider; we are not in the cardholder data environment at all, by design.
What we hold is the result: whether a completed, paid checkout session exists for your email, when it was made, and when access expires. Our entitlement check queries Stripe live, so Stripe is always the source of truth about what you bought.
We also accept an optional Stripe webhook as a fast path for unlocking access. Every webhook request is verified before it is trusted: we recompute the HMAC-SHA256 signature over the raw payload with the shared signing secret, compare it in constant time, and reject any request whose timestamp is more than five minutes old — which stops replay attacks. An unsigned or badly signed webhook changes nothing. If the webhook is not configured at all, entitlements still resolve correctly by querying Stripe directly.
5. Where your data lives
Account data, leagues, preferences, entitlements, alerts and hashed sign-in codes are stored in Netlify Blobs, keyed by your email address, in separate named stores per data type. Every read and write goes through a serverless function that first verifies your session token, so one account cannot address another account's records. There is no public bucket, no directory listing, and no client-side path that reaches storage without passing authentication.
Server-side secrets — the session signing key, the Stripe secret key, the Stripe webhook secret, the Resend key and the Anthropic key — are held as environment variables on the server and are never exposed to the browser. The AI assistant is deliberately routed through our own function precisely so that the Anthropic key stays server-side.
6. Access controls
- Every endpoint that touches personal data requires a valid Bearer token and derives the account from the verified token, never from a parameter the client can set.
- Requests larger than a fixed size limit are rejected before they are stored.
- Alerts are capped at 50 records and expire after 14 days, so the blast radius of that store stays small.
- Access to production configuration is limited to the operator of the service. Bellcow is a small operation, and we would rather say that than imply a security organisation we do not have.
7. Third-party integrations
- Every integration is read-only. Bellcow never submits a lineup, waiver claim or trade. There is no code path in the product that writes to a fantasy platform.
- We never ask for a fantasy platform password. There is no field to type one into.
- Yahoo uses OAuth. Our proxy is stateless: the OAuth callback hands the access and refresh tokens to your browser and they stay there. They are never written to our storage or our logs. Pressing Disconnect deletes them from your browser.
- Sleeper is read through its public API using only public league identifiers — no credentials are involved.
- ESPN is read through undocumented endpoints, a connector our own source register flags as requiring legal review. Manual import is the supported alternative. A private ESPN league additionally needs two cookies from your own browser session,
espn_s2andSWID. These are account-level session cookies, so they are held in your browser's local storage and sent only to our ESPN reader for the length of one request — they are never written to our storage. Correction, 25 August 2026: until this date the connector was also attaching both values to the league record that syncs to our servers, which meant a private ESPN league connected before then had them stored in our database unencrypted. The flaw was found in our own pre-launch audit and fixed the same day, before Bellcow had any customers — the only accounts that ever existed were our own test accounts, so no customer credential was ever stored. We are recording it here rather than quietly fixing it, because a security page that only mentions the problems that got caught in public is not a security page. They are now stripped on the way out of the browser and again on both the read and write paths of our storage.
Bellcow Connect and server-side credentials — built, not switched on
Everything above describes what Bellcow does today, and it is accurate today: no fantasy platform credential is stored on our servers, because nothing on our servers is able to read your league while you are asleep.
We have built the thing that would change that, and it is not enabled. It is described here rather than announced later, because the change is one you would reasonably want to know about before it happens rather than after.
- What it is. A browser extension, Bellcow Connect, that would move your ESPN session cookies once, from your browser to an encrypted store on our servers, so that Bellcow could re-read your league on a schedule instead of only when you press Sync.
- How it would be stored. AES-256-GCM, with the encryption keys held outside the database that holds the records, so a copy of the database is not a copy of your credentials. Each record is cryptographically bound to the account it belongs to. Only the synchronisation service can decrypt; no page, no support tool, no analytics and no AI prompt can. Every decryption is logged with a stated reason, and no log line contains the value.
- The trade-off, stated plainly. Today, somebody who took over your Bellcow account would get your league data. If your ESPN credentials were stored here, they would additionally be able to read your ESPN account through Bellcow's own read paths, for as long as that session lasted. That is a real increase in what a Bellcow account is worth to an attacker, and it is the honest reason this is behind a gate rather than shipped.
- What we cannot do. Deleting our stored copy does not end your ESPN session. Only logging out of ESPN does that. Nobody — us included — can revoke an
espn_s2on your behalf. - What has to happen first. Three separate approvals, recorded with a named approver and the exact implementation they reviewed: a technical verification, a security review including a penetration test we have not yet commissioned, and a legal review of reading ESPN this way at all. All three must be on record before the capability can be switched on, and no configuration setting can substitute for one. An approval also expires automatically if the implementation changes after it was granted.
- Where it stands. Not published to the Chrome Web Store, not installable, and not enabled. If it ever is, you will be asked to agree to a specific notice describing exactly this, and that agreement is recorded against the exact wording you were shown.
8. What goes to the AI
The assistant sends your question and a compact league summary to Anthropic through our own server-side proxy. That payload contains no email address, no session token, no payment data and no OAuth tokens. Chat history lives in your browser tab, not in our storage. The full contents are itemised in the Privacy Policy.
9. Email
Transactional email — sign-in codes, purchase confirmations and the optional morning digest — is sent through Resend over TLS. Email is not an encrypted medium end to end, so treat a sign-in code the way you would treat a one-time passcode from your bank: use it, then let it expire. We will never email you asking for a password, a card number, or a code you did not just request. If a message claiming to be from Bellcow asks for any of those, it is not from us — forward it to support@bellcowgm.com.
10. Data handling and deletion
We collect the minimum the product needs to work, keep it only as long as it is useful, and delete it when you ask. Sign-in codes are destroyed on use. Alerts expire after 14 days. Deleting a league removes it from the server, not just from your screen. A full account deletion clears your records from every store that holds them. Retention detail is in the Privacy Policy.
11. What we do not have yet
Bellcow is a young product run by a small team. To keep this page honest, here is what is not in place today: no third-party penetration test or SOC 2 report; no bug bounty programme; no published Content-Security-Policy; no hardware-key or app-based multi-factor option beyond the emailed code; and no Chrome Web Store listing for Bellcow Connect, which is unpublished and uninstallable. Corrected 25 August 2026: this list previously said we had no server-side session revocation. We do — "Sign out everywhere" invalidates every existing sign-in token for your account, including ones issued before you pressed it. The claim was out of date rather than wrong when written, which is exactly how an honesty section quietly stops being one. None of the remaining gaps is a reason to worry about a $19.99 fantasy football tool that stores no passwords and no card data — but you should know them, rather than infer a maturity level we have not reached.
12. Reporting a vulnerability
If you find a security problem, please tell us before you tell anyone else.
Email support@bellcowgm.com with "Security" in the subject. Include what you found, how to reproduce it, and how we can reach you.
- We acknowledge security reports within 2 business days.
- We will keep you updated while we investigate and tell you when it is fixed.
- We will not pursue legal action against anyone who reports a genuine issue in good faith, tests only against their own account, avoids accessing or destroying other people's data, and gives us a reasonable window to fix it before publishing.
- We do not currently pay bounties, but we will credit you if you would like us to.
13. If something goes wrong
If we discover a breach affecting your personal data, we will investigate immediately, contain it, and notify affected users by email — and the relevant regulators where the law requires it — without undue delay, with what we know, what it affects and what you should do. We would rather send an awkward email early than a polished one late. Live service status is at /status.
Contact
Soulvaya Labs, Inc. — Bellcow GM
131 Continental Drive, Suite 305, Newark, Delaware 19713, USA
Security reports and all other contact: support@bellcowgm.com
Contact Support · Help Center · Privacy Policy