4.8 KiB
4.8 KiB
Randomayzer — Production Guards & Security Architecture
This document details the production integrity guards, concurrency safeguards, rate limiting, and idempotency semantics implemented in Randomayzer.
1. Idempotency Hardening & Request Fingerprinting
Contract
The API supports the standard Idempotency-Key HTTP header on state-mutating endpoints:
POST /api/giveaways(Giveaway creation)POST /api/giveaways/[id]/participants(Participant import & enrichment)POST /api/giveaways/[id]/snapshot(Participant snapshot locking)
Semantics
- Scoped Storage Key:
Keys are composite and scoped by
operation,giveawayId, andidempotencyKey:format: ${operation}:${giveawayId || 'global'}:${key}This prevents cross-endpoint and cross-giveaway key collision. - Request Fingerprinting:
Every request computes a canonical SHA-256 fingerprint:
requestFingerprint = SHA256(canonicalStringify(requestPayload)) - Replay vs Conflict Handling:
- Same Key + Same Request: Returns the previously cached status code and response body without re-executing.
- Same Key + Different Request: Throws HTTP
409 Conflictwith error codeIDEMPOTENCY_KEY_REUSED.
- Key Validation & TTL:
- Maximum key length is 128 characters (exceeding length returns
400 VALIDATION_ERROR). - Default TTL is 5 minutes with proactive cleanup and upper memory bounds.
- Maximum key length is 128 characters (exceeding length returns
2. Rate Limiting & Client Identity Resolution
Centralized Client IP Resolution (src/lib/client-ip.ts)
- Untrusted Proxy Mode (Default):
When
TRUST_PROXY !== 'true', user-suppliedX-Forwarded-For,X-Real-IP, orCF-Connecting-IPheaders are strictly ignored to prevent IP spoofing attacks. The direct socket connection IP is used. - Trusted Proxy Mode (
TRUST_PROXY=true): When deployed behind a verified reverse proxy (e.g. Nginx, Cloudflare, AWS ALB),TRUST_PROXY=truemust be set. The resolver:- Enforces a maximum header length of 1024 characters (oversized headers are rejected as malformed).
- Parses multi-value proxy chains (
client, proxy1, proxy2), extracting and validating the leftmost IP. - Normalizes IPv4, IPv6 (including IPv4-mapped IPv6
::ffff:192.0.2.1and loopbacks::1). - Validates IP syntax against IPv4/IPv6 standards.
Memory Limiter vs Multi-Instance Deployments
- The built-in
SlidingWindowRateLimiteris optimized for single-instance, serverless dev, and test environments. - In multi-instance or horizontal cluster deployments, rate limiting must be offloaded to an edge layer (e.g., Cloudflare Rate Limiting, Nginx limit_req) or a shared distributed cache (Redis/Valkey).
3. Winner Count Contract (Zero Under-Delivery)
Contract
Before conducting any draw, the system enforces:
\text{winnersCount} + \text{reserveWinnersCount} \le \text{eligibleParticipantsCount}
If the requested winners count plus reserve exceeds the locked snapshot's eligible participants:
- The system returns HTTP
400 VALIDATION_ERROR(or409 CONFLICT). - The system NEVER silently clamps or reduces the winners count.
4. Draw Concurrency & Terminal Retry Contract
Atomic Conditional Execution
- Draw execution (
POST /api/giveaways/[id]/draw) requires the giveaway to be inSNAPSHOT_LOCKEDstatus with an existing snapshot. - The state transition from
SNAPSHOT_LOCKEDtoDRAWNoccurs atomically inside a database transaction (updateMany({ where: { id, status: 'SNAPSHOT_LOCKED' } })). - In a concurrent race (e.g. 20 or 100 simultaneous draw requests), exactly one request acquires the lock and transitions to
DRAWN. All competing requests receive409 Conflict.
Terminal State Replay (DRAW_ALREADY_COMPLETED)
- Once a giveaway is in
DRAWNorPUBLISHEDstatus, subsequent draw attempts return:{ "success": false, "error": { "code": "DRAW_ALREADY_COMPLETED", "message": "Giveaway has already been drawn and finalized. Repeat draws are not permitted." } } - Clients and UI treat
DRAW_ALREADY_COMPLETEDas a terminal final status.
5. Summary of Environment Variables
| Variable | Type | Default | Description |
|---|---|---|---|
NODE_ENV |
string | development |
Environment mode (production, test, development) |
TRUST_PROXY |
boolean (true/false) |
false |
Enable only behind trusted upstream proxies |
ALLOW_MEMORY_IDEMPOTENCY |
boolean | false |
Silence production warning for memory idempotency in single-instance |
ALLOW_MEMORY_RATE_LIMITER |
boolean | false |
Silence production warning for memory rate limiter in single-instance |
USE_VK_MOCK |
boolean | false |
Explicitly enable VkMockProvider (forbidden in production unless true) |
VK_SERVICE_TOKEN |
string | undefined |
VK Application Service Access Token |