Security & Privacy
Velo takes a defense-in-depth approach to API security. This page consolidates all security requirements for operators integrating with the Velo platform.
Transport Security
All API communication must use TLS 1.2 or higher. Plain HTTP connections are rejected at the gateway.
| Requirement | Standard |
|---|---|
| Protocol | HTTPS only (TLS 1.2+) |
| Certificate | Valid CA-signed certificate |
| Cipher suites | Modern AEAD ciphers (AES-256-GCM, ChaCha20-Poly1305) |
HMAC-SHA256 Authentication
Every wallet callback includes an X-Sign header containing an HMAC-SHA256 signature of the request body. Operators must verify this signature to ensure request authenticity.
Wallet Callbacks (RGS → Operator)
X-Sign = HMAC-SHA256(API_SECRET, request_body_json)Your server must:
- Read the raw request body before JSON parsing
- Compute
HMAC-SHA256(your_api_secret, raw_body) - Compare the result to the
X-Signheader using a constant-time comparison - Reject the request if they don't match
Game Launch (Operator → RGS)
signature = HMAC-SHA256(API_SECRET, "api_key|player_id|timestamp")Reconciliation & History (Operator → RGS)
signature = HMAC-SHA256(API_SECRET, "api_key|round_id|timestamp")Timestamp Replay Protection
All HMAC-authenticated endpoints enforce a ±5 minute timestamp window. Requests with stale timestamps are rejected with TIMESTAMP_EXPIRED.
| Parameter | Value |
|---|---|
| Maximum clock drift | 5 minutes |
| Error code | TIMESTAMP_EXPIRED |
| HTTP status | 401 Unauthorized |
TIP
Synchronize your server clock with NTP to avoid false rejections. The timestamp is a Unix epoch second (not milliseconds).
Idempotency
Every financial transaction carries a unique transaction_id (UUID v4). The RGS uses this as an idempotency key:
- If a duplicate
transaction_idis received, the server returns the original response without re-processing - The
transaction_idremains stable across retries — never regenerate it - This prevents double-debits and double-credits in network failure scenarios
Timeout → Retry with SAME transaction_id → Server returns original resultExponential Backoff with Jitter
The RGS retries failed wallet requests using exponential backoff with randomized jitter:
WaitTime = min(MaxWaitTime, BaseInterval × 2^retryAttempt × Jitter)
Jitter = random(0.85 ... 1.15)| Parameter | Standard Retry | Critical Retry |
|---|---|---|
| Max retries | 3 | 10 |
| Base interval | 200ms | 500ms |
| Max wait | 1.6s | 30s |
| Operations | debit, authenticate, balance | credit, rollback, end_round |
Why jitter? If a server briefly goes offline, all connected clients retry at the same intervals → the server is overwhelmed on recovery (thundering herd). Jitter randomizes the retry timing to smooth the recovery curve.
4xx errors are never retried — these indicate logical errors (insufficient funds, invalid token), not transient failures.
Data Minimization
The Velo API transmits only pseudonymous identifiers:
| Data | Format | PII Risk |
|---|---|---|
| Player identity | UUID player_id | None — operator-assigned hash |
| Usernames | Provided by operator in auth response | Operator-controlled |
| Financial data | Amounts only — no card/bank details | None |
| IP addresses | Not logged by RGS | None |
No player names, emails, physical addresses, or payment details pass through the Velo API. GDPR and CCPA compliance burden is minimized by design.
Session Security
| Property | Value |
|---|---|
| Session TTL | 1 hour |
| Session token | Generated at game launch, validated on every wallet call |
| Token format | Opaque string (UUID-like) |
| Invalidation | Automatic on timeout, explicit on session close |
Sessions are created via the Game Launch API and linked to a specific player + operator pair. The session_token is passed on every wallet callback, allowing operators to validate that the request corresponds to an active session.
Operator Security Checklist
- [ ] Verify
X-SignHMAC on every wallet callback (constant-time comparison) - [ ] Validate timestamp freshness (reject if >5 minutes old)
- [ ] Store
API_SECRETin a secure vault (not in code or config files) - [ ] Enforce HTTPS (TLS 1.2+) on all wallet endpoints
- [ ] Implement idempotency on
transaction_id(return original result on duplicates) - [ ] Validate
session_tokenon debit/credit/rollback - [ ] Set response timeout < 2 seconds
- [ ] Handle
INSUFFICIENT_FUNDSon funded accounts (sandbox test scenario) - [ ] Never log
API_SECRETor raw HMAC signatures - [ ] Rotate API credentials periodically