Security
What protects your portfolio, specifically
Generic assurances are worth nothing on a page like this. Below is what the system actually does, described precisely enough that you could ask a hard question about any line of it.
Your broker connection cannot trade
This is the property that matters most, and it is structural rather than a policy. vest101 asks for API credentials scoped to reading positions, transactions and dividend history. There is no order-placement code in the product to be exploited, misconfigured or turned on by mistake.
- Read-only API access to Trading 212 and Interactive Brokers.
- No order, transfer or withdrawal calls anywhere in the codebase.
- Revoking the key at your broker takes effect immediately; the source is then reported as failing to sync rather than showing a stale figure.
Credentials and addresses are encrypted at rest
Broker API keys and Bitcoin addresses are encrypted in the database with AES-GCM. Because AES-GCM uses a random IV, two encryptions of the same value differ — which breaks the equality lookups and uniqueness constraints a database needs — so lookups go through a separate keyed blind index instead of decrypting rows to search them.
- AES-GCM encryption for broker credentials and Bitcoin addresses.
- Keyed blind index for lookups, so finding a row never requires decrypting others.
- Decryption happens at the repository boundary; no layer above it ever holds the ciphertext or the key.
Your data is scoped to you, in the type system
Every table holding user data carries a user reference, and every repository function that touches one takes the user id as a required argument. That is deliberate and it is enforced by the compiler: an unscoped query does not typecheck, so it cannot be written by accident and then reviewed past.
- Required user id on every data access path — a missing one is a build failure.
- No optional “convenience” overloads that would let the requirement be skipped.
- The internal service API the AI layer calls validates the same requirement rather than trusting its caller.
The origin only answers Cloudflare
The server that runs vest101 is not directly addressable. It sits behind Cloudflare with Authenticated Origin Pulls enabled, so a TLS handshake that does not present Cloudflare’s client certificate is refused before any request is read.
- Mutual TLS between Cloudflare and the origin; direct connections are rejected at the handshake.
- HSTS, nosniff, frame-deny and a strict referrer policy on every response, including errors.
- Per-IP rate limits at the edge on the credential endpoints, keyed to the real visitor address, plus the application’s own limiter behind them.
- The internal service API is not routed from the internet at all — it is reachable only between containers on the host.
Payment details never reach us
Checkout runs on Stripe’s own hosted page. vest101 stores the resulting subscription state and nothing else — no card number, no expiry, no CVC, and nothing that could be used to charge you.
- Card data is entered on Stripe and never transits vest101.
- Access is evaluated against the period you actually paid for on every request, not against a status flag, so a lost webhook cannot silently extend or revoke access.
- An hourly reconciliation pass re-checks subscription state against Stripe to correct any drift.
Backups are encrypted, and restoring them is rehearsed
The database is backed up on a schedule to encrypted, passphrase-protected archives held separately from the application’s own credentials. A backup nobody has ever restored is a hope rather than a backup, so restoring one is a written procedure rather than an assumption.
- Scheduled encrypted database backups with their own passphrase.
- Postgres data checksums enabled, so silent corruption surfaces while a good backup still exists.
- Restore is a documented drill, not an untested script.
A misconfigured deploy refuses to serve
The failure mode this system is most careful about is not a crash — it is a deploy that succeeds and is quietly wrong. Two independent gates exist for that: one before anything is built, and one as the first statement the server executes, before it contacts the database.
- A production boot with a missing or unsafe setting throws and crash-loops rather than serving.
- It reports every problem at once instead of failing on the first, so a bad configuration is fixed in one pass.
- Settings whose development default is convenient but catastrophic in production — a disabled captcha, an email path that reports success while sending nothing — are refused explicitly.
What we do not claim
vest101 is a small, independently run product. Being precise about the edges is part of the same promise as the rest of this page.
- There is no third-party security audit or penetration test to point at.
- There is no SOC 2, ISO 27001 or equivalent certification.
- There is no formal bug-bounty programme — but a report sent to the address below will be read and answered by the person who wrote the code.
- No system is unbreakable. Use a unique password, and keep your broker API keys scoped to read-only at the broker as well.
Reporting a vulnerability
If you have found something, please report it privately before disclosing it publicly. You will get a reply.