What we hold, and what we deliberately cannot
The pre-launch holds no money, so the interesting question is not custody. It is what a venue with anonymous logins knows about you, what an attacker would get, and which of our own decisions we would regret under pressure.
What we store
| Thing | Stored | Note |
|---|---|---|
| Wallet address | Yes | It is the account identifier. Nothing else about the wallet is read, and no transaction is ever requested. |
| Account key | Only a hash | Scrypt with a per-account salt. The key itself is shown once and never leaves your device again. |
| Email and password | Only if you choose email | Password hashed with scrypt. Email is used for the verification code and nothing else. |
| Positions and history | Yes | Positions, orders, fills, funding, balances and the event log, on disk on our server and on the chain of records. |
| Analytics, trackers, ad pixels | None | There is no third-party script on the terminal beyond the charting library and the font files. |
| IP addresses | Transiently | Only in web-server logs and as a rate-limit counter on the auth routes. |
How signing in without an email works
The server issues a nonce and a human-readable message. Your wallet signs that exact message; nothing is broadcast and no funds move. The server verifies the signature and, from it alone, learns the address.
- Solana signatures are ed25519 and verify natively in the runtime, with no third-party code in the path.
- Ethereum signatures are recovered with our own keccak256 and secp256k1 implementation, written specifically so that no dependency sits between a signature and an account. It is checked against published test vectors and against an independent signing path.
- Account keys are 32 bytes from the system random source, shown once. We store the hash, so the key cannot be reset, emailed or recovered, and the interface says so before you create one.
What an attacker would get
Compromising the venue server would expose positions, order history and the hashes described above. Deposit addresses are sealed records; the key that can move money lives in the vault, a separate process that signs only what the running venue has recorded, holds a hot float capped per chain and pushes everything above it to cold. A total compromise of the venue box costs the float, not the book, and every payout is approved by a person before the vault signs it.
Operational posture
- Single host, firewalled to the web ports and key-only SSH, with automatic certificate renewal.
- A watchdog on the host checks the API, the engine tick, the price feeds, the error rate, disk, memory and the certificate every two minutes, and pages a human when any of them breaks. What each alert means and what to do about it is written down in the runbook.
- Rate limiting on every authentication route.
- Session tokens are random 24-byte values with an expiry, revocable by signing out.
- No third-party analytics, so there is no vendor to leak your session.
- The terminal is served as static files; only the API touches state.
- The API runs as its own unprivileged user under a locked-down systemd unit: read-only system, no capabilities, no new privileges, a syscall filter. State and config are the only writable paths.
- A Content-Security-Policy on every page: no third-party scripts, inline scripts allowed only by hash, no framing. The chart library and the two-factor QR are served and drawn by us.
- Every account event and every operator action is appended to a hash-chained journal that anyone can recompute from genesis.
- State lives in one SQLite file in write-ahead mode; the journal is an append-only text chain. Both are backed up every night, fourteen copies kept on the box and one pulled to a second machine.
Internal audit, 1 September 2026
An internal review of the authentication, cryptography, engine and infrastructure surfaces, with each conclusion tested against the running venue rather than asserted. It is not an external audit and we do not describe it as one. Everything below was fixed the same evening and re-tested live.
| Severity | What was wrong | What changed |
|---|---|---|
| critical | The degradation drill needed no credential. Any visitor could degrade a market for every trader on the venue, and before the margin fix earlier that day this was a remote mass-liquidation button. | Requires an account and is rate limited. Later the same evening the drill was scoped to the account that runs it, so it cannot touch anyone else at all. |
| high | X-Forwarded-For was trusted where nginx never set it, so a client could take a fresh rate-limit bucket per request, and honest clients shared one bucket that could lock out the whole venue. | nginx overwrites the header with the real peer; the server reads the last hop. |
| high | Account creation had no limit of any kind. | Eight per address per hour. |
| high | Email verification codes came from a predictable generator, guesses were not counted, and a correct guess returned a session. | Codes from a cryptographic source, six wrong guesses burn the code, constant-time comparison, resend throttled. |
| medium | Password hashing ran on the venue's only thread: while a password was checked, prices did not tick and nothing was liquidated. An unknown address also returned faster than a known one. | Every password path is asynchronous and spends the same work either way. |
| medium | No HSTS, no framing protection, no nosniff, no referrer policy, and nginx advertising its version. | All added. |
| medium | Paper deposits had no lifetime cap; one account had reached $3.1m. | $250k per call and $900k over the life of an account. |
| medium | State files holding session and bearer tokens were world-readable. | Owned by the service user, unreadable to anyone else. |
| medium | The API ran as root. | Its own user, locked-down unit, see the posture above. |
| medium | The two-factor QR was fetched from a third-party image service with the secret in the URL. | Drawn locally by our own encoder, verified against a reference decoder. |
| medium | The event stream accepted a bare account id as the key to that account's margin report. | A short-lived ticket issued to an authenticated session. |
| low | Signature malleability accepted on wallet sign-in; a bearer token compared with a non-constant-time equality; a rate-limiter map that could grow without bound. | EIP-2 enforced, constant-time comparison, the map is swept. |
Verified good in the same review: account isolation holds against forged and cross-account tokens, the hand-written keccak256 matches the published vectors, no secrets in git history, TLS 1.3, the operator configuration unreadable to other users.
Reporting something
If you find a problem, tell us before you tell anyone else, and please do not test against other people's accounts. Reports go to security@openfield.markets. There is no bounty yet; there will be one before there is money at stake.