Provenance
Getting Started

Security & Infrastructure

How Provenance protects your data and keeps the platform running.

Provenance is designed as a multi-tenant SaaS platform where every tenant's data is isolated at the database level. This page documents the security controls and infrastructure decisions that are actually implemented in the codebase — no aspirational claims.

Tenant Isolation

Every tenant gets its own PostgreSQL schema (tenant_{slug}). The API resolves the tenant from the API key or OAuth token on each request and sets search_path before executing any query. There is no shared data table between tenants — each schema is a full clone of the base schema.

Request → Auth middleware → Tenant middleware → SET search_path → Query
  • API keys are hashed with SHA-256 before storage. The raw key is never persisted.
  • Tenant resolution is cached for 60 seconds to reduce database lookups.
  • OAuth tokens are resolved by scanning only active tenant schemas.
  • Internal service tokens (used for queue ingestion) require both the token and an explicit x-tenant-slug header.

Authentication

Provenance supports three authentication methods, applied in priority order:

MethodHeaderUse Case
API Keyx-api-keySDK, CLI, service-to-service
OAuth TokenAuthorization: Bearer oat_...CI/CD, scoped machine access
Firebase JWTAuthorization: Bearer eyJ...Platform dashboard (browser)

All three resolve to a tenant schema. API keys and OAuth tokens are validated against the platform database. Firebase JWTs are verified using the Firebase Admin SDK.

Session cookies (used by the platform dashboard) are configured with httpOnly, secure, and sameSite: none for cross-origin deployments.

Authorization

OAuth Scopes

OAuth tokens carry fine-grained scopes. Every API route is mapped to a required scope based on path prefix and HTTP method (read vs write). Unmapped routes are denied by default for OAuth tokens. The admin scope grants full access.

Scope groups: interactions, dashboards, alerts, subscriptions, config, system, users.

Role-Based Access Control (RBAC)

Platform members have a role (owner, admin, member, viewer) that maps to local RBAC roles. Permissions are checked per-module and per-action on every request via the requirePermission middleware.

API keys bypass RBAC — they represent machine-to-machine access with full tenant permissions.

Request Protection

Rate Limiting

Three tiers of rate limiting are applied using express-rate-limit:

TierDefault LimitApplied To
General100 requests / 15 minAll /api routes
Strict20 requests / 15 minSensitive operations (custom functions)
Queue10 requests / 1 minQueue processing endpoints

All limits are configurable via environment variables. Rate limit headers (RateLimit-*) are included in responses.

Input Validation

All POST/PUT endpoints validate request bodies using express-validator:

  • String length limits on titles and descriptions
  • UUID format validation on path parameters
  • Pagination bounds (max 100 per page by default)
  • Object type checks on nested fields

Invalid requests return 400 with structured error details.

CSRF Protection

State-changing requests from browser sessions require proof of intent. The platform BFF enforces this via a custom header check — requests with Authorization, x-session-token, or x-requested-with headers pass through. The core API has CSRF middleware available but disabled by default since API key and JWT authentication are not vulnerable to CSRF.

Security Headers

Helmet.js is applied to every response on both services:

  • Content-Security-Policy — restricts script, style, and image sources to 'self'
  • Strict-Transport-Security — HSTS with 1-year max-age, includeSubDomains, preload
  • X-Frame-Options — prevents clickjacking
  • X-Content-Type-Options — prevents MIME sniffing
  • Referrer-Policy — limits referrer information

Data Protection

Sensitive Data Redaction

All log output passes through a redaction filter. Any key matching patterns like password, secret, token, api-key, or cookie is replaced with [REDACTED] before being written to logs.

Error Sanitization

In production, stack traces are stripped from error responses. Only the error message and status code are returned. In development, stack traces are included for debugging.

Database Security

  • All queries use parameterized statements ($1, $2, etc.) — no string concatenation in SQL.
  • Database connections use SSL/TLS in production (rejectUnauthorized: true).
  • Connection pooling via pg.Pool with automatic client release.
  • Plan limits are enforced at the database level via PostgreSQL triggers (ERRCODE P0002), not application code — they cannot be bypassed by the API layer.

CORS

Both services use an explicit origin whitelist. The CORS_ORIGINS environment variable defines allowed origins as a comma-separated list. Requests from unlisted origins are rejected. Credentials (cookies, Authorization headers) are allowed only for whitelisted origins.

Infrastructure

Hosting

Provenance runs on Render:

ServiceTypeURL
Core APIWeb Serviceprovenance.onrender.com
Platform BFFWeb Service(internal)
Marketing SiteStatic Siteprovenance-web.onrender.com
PostgreSQLManaged DatabaseRender PostgreSQL

Health Checks

Both services expose a /health endpoint that verifies database connectivity:

{
  "status": "healthy",
  "database": "connected"
}

Returns 503 if the database is unreachable.

Monitoring

The core API exposes Prometheus-compatible metrics via express-prom-bundle at the /metrics endpoint. This includes request counts, latencies, and status code distributions.

Cold Starts

Render free-tier services spin down after inactivity. The first request after a cold start takes longer while the service boots. This applies to the API and platform BFF. The marketing site is a static export and is not affected.

If you're evaluating Provenance for production use, Render paid plans keep services running continuously with zero cold starts.

Database

PostgreSQL 12+ with:

  • Connection pooling (configurable pool size)
  • SSL/TLS encryption in transit
  • Schema-per-tenant isolation
  • Automatic session table creation (user_sessions)
  • Migration runner for schema updates across all tenant schemas

Queue Reliability

The notification system uses a database-backed queue (not an external message broker). Failed notifications retry with exponential backoff up to 3 attempts. All delivery attempts are logged in the audits table. The SDK uses QStash for managed queue ingestion with automatic retries and credential rotation.