Infrastructure Security
How ReplayCI secures its infrastructure — encryption, isolation, network security, and access controls.
Encryption at rest
Envelope encryption (V2)
All stored artifacts use AES-256-GCM envelope encryption with per-tenant key derivation:
Root KEK (REPLAYCI_ENCRYPTION_KEY)
└── HKDF-SHA256 + tenant_id → Per-tenant KEK
└── Random DEK (per blob) → AES-256-GCM encrypted data
| Component | Algorithm | Purpose |
|---|---|---|
| KEK derivation | HKDF-SHA256 | Derives unique key per tenant from root secret |
| DEK generation | crypto.randomBytes(32) | Fresh 256-bit key per blob |
| Encryption | AES-256-GCM | Authenticated encryption with 12-byte IV |
| IV | crypto.randomBytes(12) | Unique per encryption operation |
Fail-closed decryption
If decryption fails for any reason — wrong key, corrupted data, tampered ciphertext — the system returns an error. It never falls back to returning plaintext or unencrypted data.
Key versioning
Encrypted blobs include a version prefix (v2:) that identifies the encryption scheme. V1 (direct encryption) is supported for backward compatibility but V2 (envelope) is the default for all new data.
Tenant isolation
Database: Row-Level Security
PostgreSQL RLS policies are applied across all tenant-scoped tables. Every query is automatically scoped by tenant_id through ORM client extensions:
- Read queries —
tenant_idinjected into WHERE clause - Write queries —
tenant_idvalidated before execution - Cross-tenant queries are impossible through the standard database client
A dedicated authentication client handles cross-tenant operations like login and signup — this is the only path that bypasses RLS, and it is restricted to authentication code paths.
Filesystem: Namespace separation
Artifacts are stored under tenant-namespaced paths:
/{tenantId}/runs/{runId}/blobs/{contentHash}
Combined with per-tenant encryption keys (HKDF derivation), even if a path were somehow accessed, the data would be unreadable without the correct tenant's derived key.
Cryptographic isolation
Per-tenant KEK derivation means:
- Tenant A's key cannot decrypt Tenant B's data
- Compromising one tenant's data doesn't expose others
- Key derivation is deterministic (same tenant always gets the same KEK) but isolated
Network security
TLS
All traffic to app.replayci.com and replayci.com is encrypted with TLS:
- Edge: Cloudflare (automatic certificate management)
- Origin: Cloudflare Full (strict) mode — origin server has a valid certificate
- Minimum version: TLS 1.2
Security headers
The Next.js application sets security headers via next.config.mjs:
| Header | Value |
|---|---|
Strict-Transport-Security | max-age=63072000; includeSubDomains; preload |
X-Content-Type-Options | nosniff |
X-Frame-Options | DENY |
Referrer-Policy | strict-origin-when-cross-origin |
Content-Security-Policy | Script sources restricted, no unsafe-eval |
X-XSS-Protection | 0 (modern browsers use CSP instead) |
DDoS protection
Cloudflare sits in front of both the marketing site and the dashboard, providing:
- Automatic DDoS mitigation
- Rate limiting at the edge
- Bot management
- IP reputation filtering
Application security
Input validation
All user inputs are validated with strict bounds:
| Input | Limit |
|---|---|
| Run artifact payload | 10 MB |
| Contract timeout | 30,000 ms (default) |
| Retry cap | 2 attempts (default) |
| API key name | 100 characters |
| 254 characters | |
| Password | 8–128 characters |
Numeric parameters are parsed and clamped to safe ranges to prevent overflow or injection.
SecurityGate
Every data path goes through SecurityGate scanning:
- Persist pipeline
- Export pipeline
- Upload pipeline
- Log pipeline
- Render pipeline
No bypass flags exist. A SecurityGate failure blocks the operation entirely.
CSRF protection
The dashboard uses Next.js Server Actions with automatic CSRF protection. Session cookies are set with SameSite=Strict to prevent cross-site request forgery.
Access controls
Authentication paths
| Path | Method |
|---|---|
/dashboard/*, /api/dashboard/* | Session cookie (HMAC-SHA256 signed) |
/api/v1/* | API key Bearer token |
/api/auth/* | Public (login, signup, forgot-password) |
Middleware pipeline
Every request goes through the middleware pipeline in order:
- Authentication — session or API key validation
- Email verification — API endpoints require verified email
- Rate limiting — per-tenant, per-endpoint token bucket
- Correlation ID — unique request ID for tracing
Admin operations
Cross-tenant database operations (login, signup, API key lookup) use a dedicated authentication client that bypasses RLS. This client is only used in authentication code paths — never in user-facing data queries.
Infrastructure stack
| Component | Details |
|---|---|
| Hosting | EU-based data center (Hetzner) |
| CDN / WAF | Cloudflare (proxied DNS, Full strict SSL) |
| Database | PostgreSQL with RLS enforcement |
| Application | Runs as non-root, limited permissions |
Backups
- Database: Daily automated backups with integrity checksums
- Verification: Restore and verification procedures are documented and tested
Logging and monitoring
Structured logging
All application logs use structured JSON in production:
- Automatic redaction of sensitive fields (API keys, tokens, credentials) at all nesting levels
- Correlation IDs on every request for distributed tracing
- Module-scoped loggers for organized output
What's logged
- Authentication events (login success/failure, token refresh, key validation)
- Rate limit events (bucket state, rejections)
- SecurityGate decisions (pass/block with context)
- Run submission events (receipt, validation, persistence)
- Error events with stack traces (secrets redacted)
What's never logged
- Passwords (plaintext or hashed)
- API key values
- Session token values
- Password reset tokens
- Email verification tokens
- Encryption keys