Ward/Port
DEPTH 000
Open the dashboard
Security

The trust boundary is the product

WardWallet's whole design is one line the server can never cross. Here is exactly where each secret lives, and why a breach of our infrastructure cannot move a player's funds.

A fully compromised server can refuse a transaction. It cannot authorize one more. Not by policy — by construction.

Player key

Generated on the device, never sent

secp256k1, created in the browser or the Unreal plugin, sealed in the OS keystore (DPAPI on Windows, Keychain on macOS/iOS) under AES-256-GCM. The server only ever receives transactions that are already signed.

Signed policy

The key that sets the rules stays with you

The policy is signed with an Ed25519 key that never leaves the studio's browser. The server holds the public key, verifies, and enforces. It cannot widen what you signed.

Three checks

Verified on both sides of the line

Rules are checked at prepare (server), before signing (device), and at broadcast (server). The player can confirm the call matches the published policy — they don't have to trust us.

Recovery

A code only the player holds

120-bit code → PBKDF2-HMAC-SHA256, 600,000 iterations, 16-byte salt → AES-256-GCM. The hosted backup is an opaque blob; against a database dump alone an attacker faces AES over a 120-bit secret.

Anti-SSRF

Outbound URLs are fenced

Every webhook and JWKS URL is HTTPS-only, no credentials in the URL, public IPs only, IP re-validated at dial time, redirects refused — the “webhook URL” field can't become an internal network scanner.

Sponsored gas

Serialized, capped, from your wallet

Top-ups are serialized with an in-process lock plus a Postgres advisory lock on (chain, address); a regression test fires twenty concurrent requests and exactly two pass — the cap. The transaction still comes from the player's own address.

Where every secret lives

Player keysecp256k1, on the device, in the OS keystore (AES-256-GCM)
Policy signing keyEd25519, in the studio's browser — never on the server
Wallet backupPBKDF2 600k → AES-256-GCM under a 120-bit code the player alone holds
Sign-in codesargon2id, 64 MiB per call, never stored in the clear
App secretsDerived by HKDF-SHA-256 from one master key held outside the database

Operational hardening

Open the dashboard Read the docs