Security & encryption
Trace ingests the most sensitive artifact your QA pipeline produces: full DOM recordings, network bodies, screenshots, video, and console logs from applications that often contain PII-shaped test data. This page covers what you must configure and what Molar enforces by default.
TRACE_ENCRYPTION_KEY
Required for every Trace deployment — local, Docker, and Helm.
What it protects
The encryption key secures at-rest secrets in Trace's settings store:
- LLM provider API keys (Anthropic BYOK aliases)
- Integration tokens (Slack, webhook secrets)
- Encrypted fields in local
trace-db.json(standalone)
Provider credentials are stored encrypted and returned by alias only in API responses — never the raw secret.
Format and generation
| Requirement | Value |
|---|---|
| Length | 32 bytes (256 bits) |
| Encoding | Hex string (64 hex characters) |
Generate:
openssl rand -hex 32
# Example: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
Local development
TRACE_ENCRYPTION_KEY=0123456789abcdef0123456789abcdef npm run dev
The standalone README uses a fixed dev key for smoke tests — never use that key in production.
Production
| Environment | How to set |
|---|---|
| Docker Compose | deploy/docker-compose.trace.yml → env var |
| Kubernetes | Secret TRACE_ENCRYPTION_KEY → pod env (see Helm deployment.yaml) |
| Cartographer | Platform secrets manager + settings sync |
Stability: The key must remain stable across restarts. Rotating it invalidates stored provider credentials — plan a re-entry workflow for BYOK keys before rotation.
Startup behavior
If TRACE_ENCRYPTION_KEY is missing or wrong length, the standalone API refuses to start:
TRACE_ENCRYPTION_KEY is required and must be stable
This is intentional fail-closed behavior.
Authentication
Trace uses Molar platform authentication — the same sign-in as app.molar.it. There is no separate Trace IdP.
| Surface | Auth mechanism |
|---|---|
| Browser dashboard | HttpOnly session cookies (Better Auth) |
/v1/traces API (browser) | dashboard API or shared-session validation to FastAPI |
| CI / automation | Org-scoped API keys with explicit scopes |
Public share /r/{shortId} | Unguessable capability URL — no session |
| MCP / IDE | OAuth 2.1 + PKCE (mcp.molar.cloud) |
Legacy Cartographer localStorage JWT is being replaced (CART-10-WEB) — do not build new integrations on it.
API key scopes (Trace)
| Scope | Allows |
|---|---|
traces:read | List, detail, events, blob fetch |
traces:write | Pin, restore, ingest |
share:write | Create org-only share links |
share:public | Create public-read links (separate scope) |
replays:trigger | Layer 2 enqueue |
chats:write | Debugger send messages |
mender:write | Promote to fix |
Standalone local tests use x-molar-org header for deterministic tenant scoping — not a production auth mechanism.
Tenant isolation
Prime directive: Every read and write is scoped to org_id from the authenticated principal — never from a client-supplied parameter.
Global blob store risk
Blobs are content-addressed without org prefix for dedup efficiency. SHA-256 of a known payload is computable — the hash is not a secret. Confidentiality depends entirely on the API authorization layer before minting presigned URLs.
Every GET /v1/traces/{id}/blob/{sha256} verifies:
- Caller's org owns a trace referencing that blob
- Blob SHA is in trace manifest
- Presigned URL TTL is short (default 3600s)
Row-level security
Postgres queries include WHERE org_id = :current_org on traces, trace_replays, trace_chats. UUID IDs alone do not grant access.
Public share links
| Property | Detail |
|---|---|
| Entropy | 9-char base62 (~51 bits) |
| Default visibility | org-only (opt-in to public-read) |
| Default expiry | 7 days (max 90) |
| Embed | /r/{shortId}/embed — no Debugger chat |
Risks: Links leak via Slack, Linear, email forwards, referrer headers. If masking missed a field, a forwarded link is a data breach with no login wall.
Mitigations:
- Mask inputs/text by default in scenario privacy config
- Hide Debugger + source SHA on external shares
- Revoke: flip to
org-only, delete trace, or wait for expiry - Audit log on every share create/revoke
MFA step-up required to create public-read links on some enterprise plans.
PII and privacy defaults
Scenario frontmatter controls capture-time redaction:
privacy:
mask_inputs: true
mask_text: ".pii, .email"
block: ".secrets-panel"
network_redact:
- "$.user.email"
- "$.user.phone"
Org settings → Privacy set defaults for new scenarios.
rrweb maskTextSelector, maskAllInputs, blockSelector applied at record time — redaction cannot be undone in stored blobs.
Layer 2 sandbox
Layer 2 executes customer Docker images and scenario edits — remote code execution by design.
Controls:
- Isolated worker pool per region
- NetworkPolicy: no cloud metadata endpoint, no lateral movement to Molar API
- Unprivileged container user
- Per-org concurrency and CPU/memory caps
- Audit:
triggered_by,triggered_via, timestamps - MFA step-up on replay trigger (enterprise)
Debugger security
| Risk | Control |
|---|---|
| Prompt injection from trace content | Tool allowlist; no autonomous side effects |
| Confused deputy via user OAuth | GitHub App token for source reads — not login token |
| Cost exhaustion | Per-org monthly Debugger budget (cents) |
| Cross-tenant exfil | Object-level auth on every tool call |
Side effects (replay, Mender promote) require deterministic can() + human UI confirmation.
rrweb iframe sandbox
DOM replay runs in a sandboxed iframe:
sandbox="allow-scripts allow-same-origin"
Replayed DOM must not read parent session cookies or access org APIs.
Encryption in transit
TLS 1.2+ everywhere. Presigned S3 URLs are HTTPS-only. SSE-S3 or SSE-KMS at rest on object store (provider-dependent).
Compliance posture
| Standard | Status |
|---|---|
| SOC 2 Type II | Target posture from day one |
| GDPR / CCPA | In scope — captured traces may contain PII |
| PCI-DSS | Out of scope for cardholder data (Clones simulate payments) |
| HIPAA / BAA | Enterprise annual plans — contact sales |
Data export: Settings → Organization → Data & privacy. Retention: Workers & ingestion tier policies.
Audit logging
Security-relevant events emit immutable audit records:
- Auth success/failure
- Authorization denials
- Share link create/revoke
- Layer 2 trigger
- Debugger tool calls
- Export/download
- Admin retention changes
On-prem: forward to your SIEM via webhook (Settings → Integrations).
Checklist
| ☐ | Action |
|---|---|
| ☐ | Set TRACE_ENCRYPTION_KEY from openssl rand -hex 32 |
| ☐ | Store key in secrets manager — not in git |
| ☐ | Enable privacy defaults (mask inputs, network redact) |
| ☐ | Restrict share:public scope on CI API keys |
| ☐ | Review public share expiry policy |
| ☐ | Install GitHub App with minimum repo scope |
| ☐ | Set Debugger monthly budget cap |
For security reviews or compliance questionnaires, email support@molar.it.