← 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 — the key pair, or the OAuth bearer token. 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 OAuth doorway

There is a second way to connect that involves no keys at all: Connect with Alpaca. You authorize on Alpaca's own page — GreeksView never sees your Alpaca password — and a scoped access token takes the keys' place. The token's whole life, precisely:

1You click Connect and read the disclosure The redirect carries a state value: a ten-minute signed token bound to your GreeksView session, so a callback forged for someone else is refused before anything happens.
2You approve on app.alpaca.markets Your Alpaca password goes to Alpaca, and nowhere else.
3Our server exchanges the one-time code for the token The only step that needs the app's client secret, which lives in server environment variables. The token is held for the life of one redirect — never a database row, never a log line.
4A one-time handoff delivers it to your browser Burned on pickup, then encrypted into the same AES-256-GCM vault your keys would use. From here on it is treated exactly like a pasted secret — including the sync API's outright refusal to accept it.
5Requests carry it as a bearer header Through the same relay as key headers, with the same handling: in memory for one request, then out of scope.

Two behaviours worth knowing. Pasted keys always outrank the token — entering keys is a deliberate act, so it wins whenever both exist. And Disconnect removes the token from this browser only: to revoke GreeksView's access to your account entirely, remove the app in your Alpaca dashboard. We would rather say that here than let a Disconnect button imply more than it does.

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.

The last two of those patterns are history rather than housekeeping. GreeksView no longer asks anyone for a model API key and has no field anywhere that would accept one, so nothing in the product can create a key of either shape today. The screen stays because a browser that saved one before that changed still has it, and a guard you keep costs nothing while a guard you remove protects nobody.

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.

Keys or OAuth — no custody either way

Alpaca supports OAuth for third-party apps, and at first we deliberately did not use it. The standard OAuth shape would make us the custodian of a long-lived access token for your account — stored in our database, refreshed by our servers, 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 this architecture exists to avoid, and it makes our database a target worth attacking.

So when we did add Connect with Alpaca, we refused every one of those properties. The access token follows the same rules as a pasted key: handed to your browser once, encrypted into the same AES-256-GCM vault, never a database row or a log line on our side. There is no refresh machinery — Alpaca's tokens are long-lived, and if you revoke one, the first rejected request clears it from your browser and asks you to reconnect. Revocation happens on Alpaca's own dashboard, effective immediately, without asking us.

Bring-your-own-key remains for anyone who prefers to mint and hold the credential themselves: you create it, you hold it, you destroy it from Alpaca's dashboard the moment you want us gone — no request to us, no trusting that we actually deleted a row. If you have both, pasted keys take precedence. Either doorway, the property that matters is the same: revocation you control unilaterally, and nothing worth stealing on our servers.

QuestionAnswer
Where are credentials stored?Your browser only — API keys and OAuth tokens alike, 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?Keys: regenerate or delete them in Alpaca. OAuth: remove GreeksView from Alpaca's authorized apps. Either is 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.