← Back to Stock Scorer

What Stock Scorer stores about you

This is a description of what the software does, not legal advice, and no lawyer has read it. If you run an instance for other people you are the one answerable for it — the wording, the retention periods, the lawful basis, and whether any of it is enough where you live. What this document can honestly do is tell you exactly what the code stores, so that whatever you publish is true. It was written by reading the schema, and the export button below produces the same list; if the two ever disagree, the export is right and this file is out of date.

Every table named here is in app/db.py. Nothing is hidden in a service somewhere else: there is one SQLite file, on the machine the operator runs this on.

If you never make an account

Nothing about you is stored by this site. The dashboard works fully signed out; that shared workspace is stored under user id 0 and belongs to nobody. Your browser is given no cookie by this site until you sign in.

Analytics are a separate question, and you are asked before anything happens. If this instance has Google Analytics configured, a banner appears on your first visit. Until you press Accept, nothing is fetched from Google, no identifier is set, and no visit is reported. Press *No thanks* and that stays true for good — the answer is remembered so you are not asked again.

If you do accept, Google Analytics receives the pages you look at, roughly where you are (from your IP address, which it truncates), which site you arrived from, and your browser and screen size. It sets identifiers in your browser to recognise the same visitor across pages and visits. Google is a separate company and processes this under its own terms. Two of its settings are switched off here: allow_google_signals and allow_ad_personalization_signals, which are the ones that feed advertising products rather than visitor counts.

You can change your mind at any time — *Change your analytics choice* at the foot of every page. That forgets the answer and reloads, so nothing further is sent.

Nothing about your account, your list or your scores is ever sent to Google. Analytics sees a page being visited, not who you are here or which companies you follow.

If you make an account

WhatWhereEncrypted?
Usernameusers.usernameNo — it is looked up on every request
Passwordusers.passwordNot stored. Only a PBKDF2-HMAC-SHA256 hash, 600,000 iterations, unique salt
Email addressusers.emailYes, AES-256-GCM, when SCORER_SECRET_KEY is set
Email lookup keyusers.email_indexA keyed HMAC, so the address can be found without being readable
Google account idusers.google_subNo — looked up on every sign-in. Only if you choose Google
When you signed upusers.created_atNo

The password is never stored, in any recoverable form, and neither are recovery codes. They are one-way hashes. Nobody running the server can read your password, and no restore from backup can produce it.

What you make with it

WhatWhereEncrypted?
Companies you followwatchlistsNo — every score is joined against it on every read
Weights, anchors, which measures countsettingsNo
Saved setupsfavourites.payloadYes
Computed scoressnapshotsNo — arithmetic on public filings

So "user 7 follows NVDA" is readable to anybody holding the database file. That is stated plainly rather than buried: the ticker is what every read joins on and what every fetch works from, and a column that must be searched cannot be hidden from the thing searching it.

If you report a bug or ask for a feature

WhatWhereEncrypted?
Whether it is a bug or a featurereports.kindNo
What you wrotereports.bodyNo
When you sent itreports.created_atNo
Your account id, only if you were signed inreports.user_idNo

That is the whole row. Nothing is collected that you did not type — not your browser, not your address, not the page you were on, not which companies you follow. A bug report usually carries all of that, and it would make the report easier to act on; it is left out because this document lists every stored field and collecting quietly would make it false.

Send one signed out and there is nothing in it that points at you. Send one signed in and your account id is stored, so the operator can reply — that is the only reason it is there.

Reports are not deleted when an account is deleted. The account id is cleared, so what remains is an anonymous note about the software. A bug is not personal data about you, and losing the report would mean losing the reason a fix was made.

What is kept to keep the account safe

WhatContentsKept for
sessionssha256 of the cookie, never the cookie30 days, or until you sign out
known_devicessha256 of a browser cookie, first and last seen, last IP address, browser stringUntil the account is deleted, or you use *Sign out everywhere else*
login_failures`IP address \username`, and a timestamp15 minutes for sign-ins, 1 hour for sign-ups, everything dropped after 24 hours
email_tokenssha256 of the link that was mailed1 hour. The used row is kept so an old link can be told "already used" rather than "invalid"
mail_queueRecipient and body, both encryptedDeleted the moment it is sent. A message that could not be delivered is kept so somebody can see why

The IP address in known_devices is there for one purpose: so a sign-in from a browser the server has not seen can tell you where it came from. It is what makes the notice worth reading.

What is counted about visits

WhatWhereEncrypted?
The datevisits.dayNo
Which pagevisits.pathNo
How many times it was asked forvisits.viewsNo

Three columns, and there is no fourth. No address, no cookie, no session, no browser string, and nothing derived from any of them. One row says "on this date, this page was read this many times" — and there is no way, from this table, to tell whether that was one person or a hundred.

That is the whole design rather than an omission. The question it answers is whether the site is growing, and a count per day answers it exactly. Storing an identifier as well would buy a unique-visitor figure and cost the only property this table has: there is nothing in it about you, so there is nothing to leak, nothing to hand over, and nothing to delete when you ask.

Requests the dashboard makes to itself are not counted, and neither are crawlers. The user agent is read to tell one from the other, and then dropped.

No analytics service is involved in this. It is a counter in the same SQLite file as everything else, readable only by whoever runs the server.

The log

If the operator has set SCORER_LOG_FILE, security events are written to disk and rotate after 5 MB, five files kept — so roughly the last 25 MB of events, and no longer.

Written: an account being created, deleted or exported (by user *id*), a sign-in lockout, a rate limit being hit, mail failing. IP addresses appear on the events where the address is the whole point.

Never written: email addresses, usernames, session cookies, reset links, passwords. A log is the one place personal data escapes by accident, because it gets pasted into issues and kept long after the database row was deleted.

The web server in front of this keeps its own access log, which is not covered here and usually does record addresses and pages. That one is the operator's to configure.

Who else sees anything

WhoWhat they getWhen
SEC EDGARA ticker or company id, and the operator's contact address in the request headerOnly when the operator refreshes. Never triggered by a visitor on a published instance
Yahoo FinanceA ticker symbolWhen a price is refreshed
Google AnalyticsPages visited, approximate location, referring site, browserOnly after you press Accept. Never before, and never if you decline
Google sign-inThat you are signing inOnly if you choose *Sign in with Google*
The mail providerYour address, and what the message saysOnly when a message is actually sent to you
The payment providerThat a browser opened the donation page: its own cookies, and your address if you choose to give one *to them*Only if you press Donate. Never on arrival, and never if you do not

The SEC and Yahoo are asked about *companies*, and the question is the same whoever asked it — neither is told who you are or that you have an account here. Google Analytics is told that a page was visited, and nothing about your account or your list.

The donation page is loaded on a click, not on arrival. Until you press Donate, nothing is fetched from the payment provider, no frame is opened and it is told nothing. Press it and its page opens here, under its own terms and its own cookies — the referrer is withheld, so it is not told which page you came from, and it is the only outside host this site will load at all. No payment is ever handled here. There is no field on this site that takes a card number, no such column in the database, and no credentials for any payment provider: what you give, and what they know about it, is between you and them. If you transfer to a bank account instead, that happens in your own bank and this site never sees it.

Nothing is sold. There is no advertiser here: this is a program that reads filings. What Google does with what it collects is governed by its own terms, which is the trade you are being asked to accept or decline.

What you can do about it

Stored SEC filings and share prices are not deleted with an account. They are public data, shared by everybody using the instance, and not one person's to take away — somebody else following the same company would lose it too.

What this cannot protect you from

Encryption at rest covers a leaked *file*: a backup in the wrong place, a copied database, a resold disk. It does nothing against somebody who has taken over the running server, because the process must read these fields to work. Anyone who tells you otherwise is selling something. Whether the disk itself is encrypted, and who can log into the machine, is up to whoever runs it.