← Back to GreeksView

Security architecture

You are about to hand an options tool the keys to your brokerage account. Before any of the analytics matter, you should know exactly where those keys sit, what crosses the network, and what we can and cannot see. This page answers that plainly — including the parts that are less flattering than the marketing version.

AES-256-GCM at rest In your browser, with a key that JavaScript cannot read.
Never stored on our servers Not in a database, not in a log, not in a backup.
They do cross our server As relay headers on each request. Held in memory, never written.
Paper keys work fully Every analytic runs on paper. Start there.

Where your keys live

Your Alpaca key and secret are stored in your own browser, never on our servers. They are held under AES-256-GCM encryption via the browser's native WebCrypto implementation — the same authenticated cipher used for TLS 1.3.

What makes that meaningful is where the encryption key sits. It is generated once per browser profile and stored in IndexedDB with extractable: false, which means the browser will use it to encrypt and decrypt but will never hand the raw key bytes back to JavaScript — not to our code, and not to anything else running on the page. localStorage holds only the ciphertext envelope (ovenc1.<iv>.<ciphertext>), with a fresh random IV per write.

The practical consequence: ciphertext copied out of your browser profile cannot be decrypted anywhere else. Someone who exfiltrates your localStorage gets an envelope they cannot open. Plaintext exists only in memory, for as long as the tab is open.

The honest edge case. WebCrypto and IndexedDB require a secure context. On the hosted app that is always true — it is served over HTTPS. If you self-host over plain HTTP, the vault cannot initialise, the app logs a warning to the console, and secrets fall back to unencrypted storage rather than failing shut. Serve over HTTPS or localhost.

What actually crosses the network

This is the part most tools gloss over, so here it is precisely. Alpaca's API does not permit cross-origin calls from a browser, so the hosted app routes Alpaca requests through a proxy on our server. Your key and secret travel with each of those requests, in request headers.

1Your browser decrypts the key in memory Plaintext never touches disk on your machine.
2The request goes to our proxy with the key in headers apca-api-key-id and apca-api-secret-key, over TLS.
3Our server copies those two headers onto a call to Alpaca Held in a local variable for the life of one request. Never written to a database, a log line, an error report or a backup.
4Alpaca's response is passed straight back The request ends; the credentials go out of scope with it.

So the accurate claim is "never stored on our servers", not "never leaves your browser". We would rather you read that here than discover it in your network tab and wonder what else was overstated. There is no account on our side that holds your keys — delete your GreeksView account and there is nothing of Alpaca's to delete, because there never was.

The one place a key could have been stored by accident

Pro accounts can sync preferences — favourites, journal entries, alerts — across devices. That endpoint is the one place a secret could plausibly have been written to our database by mistake, so it refuses the attempt outright: a payload whose keys match a secret-key pattern (csd.config, finnhubKey, aiChatApiKey, aiKey_*) is rejected with a 400 rather than silently filtered. Belt and braces — the client never sends them either.

If you want the keys to genuinely never leave your machine

Two supported routes, both in Settings → Alpaca:

Start with paper keys

Every analytic in GreeksView runs on paper credentials. Gamma exposure, gamma flip, IV skew, term structure, max pain, the screeners, the P/L lab — all of it works against an Alpaca paper account, with the same option chains and the same Greeks.

So there is no reason to lead with live keys. Generate paper keys in the Alpaca dashboard, run the whole desk for a week at zero financial risk, and decide whether we have earned a live key only after you have seen the tool work. Paper is the default in the app, and switching to live is a deliberate, separate toggle.

Why your own keys instead of OAuth

Alpaca supports OAuth for third-party apps, and we deliberately did not use it.

OAuth would make us the custodian of a long-lived access token for your account, stored in our database, refreshed by our servers, and revocable only through a flow we control. That is a better experience for us and a worse position for you: it creates exactly the server-side store of account credentials that this architecture exists to avoid, and it makes our database a target worth attacking.

Bring-your-own-key inverts that. You mint the credential, you hold it, and you destroy it from Alpaca's own dashboard the moment you want us gone — no request to us, no waiting for us to honour a revocation, no trusting that we actually deleted the row. Revocation you control unilaterally is worth more than the convenience of a login button.

QuestionAnswer
Where are keys stored?Your browser only, encrypted with AES-256-GCM.
Do you have a copy?No. Nothing on our side persists them.
Do they cross your server?Yes — as relay headers, in memory, per request.
Are they logged?No. Credentials are never written to logs or error reports.
How do I revoke?Regenerate or delete the key in Alpaca. Effective immediately, without us.
Can I avoid your server?Yes — your own proxy, or self-host.

The rest of the stack

Found a hole in any of this? We would genuinely rather hear it from you than from someone else. See the vulnerability disclosure policy for scope and how to reach us.

This page describes the shipped architecture. If you find it does not match what the application actually does, that is a bug in one of the two and we want to know — tell us.