Skip to main content

Security Architecture

Traces is designed with security as a first principle, especially important for handling sensitive forensic data.

Security Principles

  1. No secrets in database - Credentials stored only in Doppler (only a secret reference is persisted)
  2. Immutable audit trail - Chain of custody events are append-only
  3. Independent hash validation - Dual-library hashing (Node crypto + @noble/hashes)
  4. SSRF protection - Every outbound request to an analyst/connector-controlled URL is validated before each hop
  5. Authenticated APIs - All sensitive routes (including connector reads) require a Clerk session
  6. Encrypted storage - R2 server-side encryption with versioning
Row-level security

Database-enforced row-level security (RLS) is planned but not yet active. Authorization is currently enforced in the application layer (Clerk session + role checks in the API route handlers).

Authentication

User Authentication (Clerk)

  • Multi-factor authentication supported
  • OAuth2 providers (Google, Microsoft)
  • Session management with secure tokens
  • Webhook for user events

API Authentication

  • JWT tokens for API access
  • Token validation on every request
  • Role-based access control (RBAC)

Authorization

Role-Based Access Control

RoleCapabilities
CustomerOwn collections, credentials, profile
AnalystAll collections, connectors, sign-offs
AdminAll analyst + user management

Application-Layer Authorization

Authorization is enforced in the API route handlers using the Clerk session and the user's role. For example, GET /api/connectors/[id] (which returns sensitive connector internals such as auth_config and runtime parameters) calls auth() and returns 401 when there is no authenticated user.

Row-Level Security (Planned)

Database-enforced RLS is a planned future layer. The intended policies look like:

-- Customers can only see their own collections
CREATE POLICY customer_collections ON collections
FOR ALL TO customer_role
USING (customer_id = current_user_customer_id());

Credential Security

Storage

  • Credentials NEVER stored in the application database
  • Only a secret reference (the secret_arn column — a legacy name) is stored in the DB
  • Stored in Doppler, encrypted application-side with AES-256-GCM (apps/web/src/lib/secrets)

Access

  • All access logged to credential_access_logs
  • Just-in-time retrieval during collection
  • Credentials cached only in memory
  • Secure deletion (cryptographic overwrite)

Lifecycle

Created → Validated → Used → Revoked/Expired

├── All access logged
└── Encryption at rest

Data Encryption

In Transit

  • TLS 1.3 for all connections
  • Certificate pinning where possible
  • Secure WebSocket for real-time

At Rest

  • PostgreSQL encryption (Supabase managed)
  • R2 server-side encryption
  • Credentials encrypted application-side (AES-256-GCM) before storage in Doppler

Hash Validation

Dual-Library Approach

Collected data is hashed with two independent implementations so a bug in one cannot silently corrupt the integrity record (apps/worker/src/collectors/api-caller.ts):

  1. Primary hash via Node's built-in crypto (createHash("sha256"))
  2. Independent validation hash via @noble/hashes (sha256 + bytesToHex)

When both agree, the record is marked validated (response_body_hash_validated on api_logs, sha256_hash_validated on collected_files). If @noble/hashes cannot be loaded, the worker falls back to the crypto hash and logs a warning.

Hash Chain

Every API response is hashed:

  • Request body hash (for POST/PUT)
  • Response body hash
  • Validated response body hash

Audit Logging

Chain of Custody Events

Immutable log of every action:

  • Collection creation
  • Status changes
  • Analyst assignments
  • Sign-offs
  • Delivery

Credential Access Logs

Every credential access recorded:

  • Who accessed
  • When accessed
  • Access type (read, validate, use)
  • Success/failure

API Logs

Comprehensive request logging:

  • Sanitized headers (no tokens)
  • Response codes
  • Timing information
  • Error details

SSRF Protection

Both the web app and the worker make outbound HTTP requests to analyst- and connector-controlled URLs (auth testing, token exchange, mTLS cert requests, endpoint tests, and the collection run itself). Without guarding, an authenticated analyst could point those requests at cloud metadata (169.254.169.254), localhost, or internal services and exfiltrate data — a classic SSRF.

Shared core guard: packages/utils/src/ssrf.ts (assertUrlAllowed, checkUrlAllowed, isBlockedIp, SsrfError). It resolves the target host and blocks requests to:

  • loopback (127.0.0.0/8, ::1)
  • link-local / metadata (169.254.0.0/16 incl. 169.254.169.254, fe80::/10)
  • RFC-1918 private ranges (10/8, 172.16/12, 192.168/16) and CGNAT (100.64.0.0/10)
  • the equivalent IPv6 ranges (ULA, mapped-private, etc.)

DNS is resolved and every answer checked, so a hostname that resolves to a private address ("DNS rebinding"-style) is also blocked.

safeFetch (apps/web/src/lib/security/ssrf-guard.ts and the worker's apps/worker/src/lib/safe-fetch.ts) is a drop-in fetch replacement that validates the target before every request and re-validates each redirect hop (a public host can 302 to 169.254.169.254). It follows redirects manually (max 5 hops) so each Location is checked before being requested. Raw node:https requests (mTLS) call assertUrlAllowed directly since they don't auto-follow redirects.

The guard can be toggled via the TRACES_SSRF_GUARD environment variable (it is disabled in the web vitest test environment). Scheme enforcement still applies even when the private-network checks are disabled.

Network Security

Firewall Rules

  • Restrict database access to application
  • Worker communicates only with required services
  • No direct internet exposure for internal services

Rate Limiting

  • Per-user rate limits
  • Platform-specific rate limiting
  • DDoS protection via Cloudflare

Secure Development

Code Security

  • Dependency scanning
  • Security linting
  • Regular updates

Secret Management

  • No secrets in code
  • Environment variables for config
  • Doppler for sensitive credential data

Compliance Considerations

Data Handling

  • Data minimization
  • Purpose limitation
  • Retention policies

Geographic

  • Data residency options
  • Regional deployment

Regulatory

  • Designed for legal/compliance use
  • Audit trail for investigations
  • Chain of custody support

Incident Response

Detection

  • Anomaly monitoring
  • Failed authentication alerts
  • Unusual access patterns

Response

  • Credential revocation
  • Session invalidation
  • Incident logging

Security Checklist

When deploying:

  • All secrets in environment variables
  • HTTPS enforced
  • Database connections use SSL
  • R2 bucket not publicly accessible
  • Clerk webhook verification enabled
  • Rate limiting configured
  • CORS policies correct
  • Security updates applied

Next Steps