Security

Your keys cannot
move your money.

That is a structural statement, not a policy one. A key that can withdraw or transfer funds is refused before it is stored, secrets are decrypted only in memory for the moment they are needed, and every state-changing request lands in a hash-chained log that nothing in the product can edit.

AES-256
envelope
TLS 1.2+
in transit
0
withdrawal scopes
SHA-256
chained audit

Envelope encryption

API secretentered once, in the browser
Data keyunique per tenant
Master keyserver environment · never in a database or backup
Ciphertext at restuseless without both keys

a7f3·9c21·4e8b·d05a·6f7e·13c9·88b2·e4d1

The secret is decrypted only in memory, for the moment it is needed. It is never written to a log, never returned by an API, and never visible in the product again after it is entered — the dashboard shows a four-character hint.

Where your keys live

A trading key is the only thing you hand over, so it is worth being exact about what happens to it.

Entered once

The secret goes from the form to the vault over TLS and is encrypted with AES-256-GCM before the response returns. It is never placed in a session and never shown again — the dashboard shows a four-character hint.

Decrypted only in memory

A per-tenant data key, itself wrapped by a master key that lives only in the server's environment, is unwrapped for the moment the secret is needed — today, to ask the exchange what the key may do, since live routing is locked.

Replaced in one step

Attaching a new key for a venue replaces the old one, and revoking takes a key out of use immediately while keeping its record for the audit trail. The master key itself can be rotated: data keys are re-wrapped under the new one, and older backups still restore with the previous key.

Never in a log

Secrets are redacted in the response filter and in the logger rather than at each call site, so a new endpoint cannot leak one by omission.

What the key is allowed to do

Encryption protects a secret at rest. Scope protects you from what the secret can do if it ever leaks.

Key scope, asked of the exchange

  • Read accountaccepted
  • Spot and margin tradingaccepted · unused while live routing is locked
  • Futures tradingaccepted · unused while live routing is locked
  • Withdraw fundsrefused before the key is stored
  • Internal transferrefused before the key is stored
  • Universal transferrefused before the key is stored

The exchange is asked what the key may do before it is stored, and a key that can move funds out is refused rather than merely unused — as is one whose permissions the exchange will not confirm. Binance only, the one venue connected today.

IP restriction

The same check reads whether the key is restricted to trusted IP addresses at the exchange, and flags it when it is not. Restrict it to your server's address and a leaked key is unusable anywhere else.

One key per venue

Each venue holds one key per tenant, so revoking one never interrupts another.

Read-only keys

A key without trading permission is accepted and marked read-only. Paper trading needs no key at all — it runs on public market data.

No implicit trust

Being on the same network is not a credential. Every request authenticates, whatever address it comes from.

Public edgeAuthenticated requestRole and scopeKey vaultkeys

Key vault

Exchange secrets are unwrapped only in memory, only when they are needed, and responses and logs pass a redaction filter before anything is written or returned.

The platform runs as one application behind nginx, so these are checks inside it rather than separate services — each one still refuses a caller the previous one let through.

A record nobody can quietly edit

An audit log that can be amended is a formality. This one chains, and it can leave the server.

Append-only audit log

chain intact
timeactoractionfromtoprev hash
14:02:11yousettings.updatedaily loss 3.0%2.5%9c21f0…
13:47:52youkey.attach…Qx7a…M2c94e8bd0…
11:20:04youPOST /controls/pause-all—200a7f39c…
09:58:33api-key:researchPOST /v1/strategies/…/promote—40913c988…
09:14:07tradingview-webhookPOST /webhook—2006f7e13…

Illustrative rows. Each entry carries the hash of the one before it, so a deleted or edited row breaks the chain and the check in Settings → Security finds the break. Database triggers refuse updates and deletes, and nothing in the product exposes a way to amend an entry.

What is recorded

Every state-changing request — settings, key attach and revoke, pauses and resumes, API keys, webhooks, manual closes — and a before-and-after record for settings and keys. Automatic halts and paper fills live in the trade ledger instead.

What is stored with it

Actor, how they authenticated, source address, user agent, the route and its result, and for changes the previous value and the new one.

Where it can go

Downloadable at any time, and shipped on a schedule to an HTTPS endpoint you choose, so the record survives the server it was written on.

Where it runs

One host you control: nginx in front, one application behind it, and every database on a volume that is backed up and restore-checked daily.

edge

nginx

TLS, HTTP to HTTPS, the API host

Certificates renew in place

app

One application

Engine, dashboard, /v1 API, webhooks

Single worker, restarts on failure

data

SQLite on a volume

Databases, vault, audit log, backups

Daily backups · encrypted with the vault key when set

Separation

  • Per-tenant data keys — unwrapping one tenant's secrets tells you nothing about another's
  • The trading engine is reachable only with the operator role; other accounts are refused before any handler runs
  • API keys and webhooks are scoped to their tenant; API keys are stored only as hashes, exchange keys only encrypted
  • Backups are encrypted with the vault's master key; a restore goes to a separate directory, never over live data

Operational posture

  • Deploys rebuild the containers from the repository; nothing is patched by hand on the server
  • Secrets come from the server's environment file at start-up; none are in the repository or the image
  • A Content-Security-Policy with a fresh nonce on every response: only this site's own scripts run, and no page can be framed by a site you have not allowed
  • The app refuses to start in production with a default session secret or without real sign-in configured
  • Trading fails closed: stale market data, a breached loss limit or a pause stops new entries rather than guessing
  • A security checkup in the dashboard lists every protection above as on or off, with the fix for each one that is off

Security is a claim you should be able to check, and all of the code behind this page is public. If something here is not specific enough to verify, that is a defect in the page — open an issue and it will be made concrete or removed.

Reporting a vulnerability

Privately, through GitHub's Report a vulnerability form on the repository. Only the maintainer and you can see the report; the fix and the advisory are published together, with credit unless you ask otherwise. Never open a public issue for a vulnerability while it is unpatched, and leave real keys and account identifiers out of the report. SECURITY.md in the repository has the scope, and /.well-known/security.txt points to the same place for automated tools.