Security
How DebugBundle protects your data: token hashing, automatic redaction, webhook signing, transport encryption, and scope separation.
Security Model
DebugBundle is designed with defense-in-depth for sensitive debugging data. Every layer — from the SDK to storage — enforces security defaults.
Token Security
Hashing at Rest
All tokens (project and member) are hashed with SHA-256 before storage. The API never stores plaintext tokens. When you create a token, the plaintext is shown exactly once.
Token Previews
Token previews (first and last 4 characters) are stored separately for identification. This allows you to distinguish tokens in listings without exposing them.
Scope Separation
| Token Type | Can Ingest Events | Can Read Incidents | Can Manage Resources |
|---|---|---|---|
| Project token | Yes | No | No |
| Member token | No | Yes | Yes |
Project tokens are deployed with your application code. If compromised, the attacker can only send noise — they cannot read any incident data, manage webhooks, or access billing.
Automatic Redaction
The SDK and ingestion pipeline automatically redact sensitive fields from captured data:
Default Redacted Fields
password, secret, token, authorization, cookie, ssn, credit_cardThese fields are replaced with [REDACTED] before the event ever leaves the SDK.
Custom Redaction
Add your own fields to the redaction list:
DebugBundle.init({
projectToken: 'local',
redactFields: [
'password', 'secret', 'token', 'authorization',
'cookie', 'ssn', 'credit_card',
'api_key', 'private_key', 'stripe_token',
],
});Redaction Guarantees
- Redaction happens in the SDK before events leave the process
- Redaction is recursive — nested objects are traversed
- Redaction is applied to keys — field names matching the list have their values replaced
- The ingestion API applies a second pass of server-side redaction as defense-in-depth
Webhook Signing
All webhook payloads are signed with HMAC-SHA256:
- A unique signing secret is generated when you create a webhook
- Every delivery includes an
X-DebugBundle-Signatureheader - Your endpoint should verify the signature using timing-safe comparison
- See Webhooks for verification code examples
The signing secret is shown once at webhook creation and never again. If compromised, delete and recreate the webhook.
Transport Security
| Path | Protocol | Encryption |
|---|---|---|
| SDK → API | HTTPS | TLS 1.2+ |
| SDK → Relay → API | HTTPS | TLS 1.2+ (same-origin relay avoids CORS; cross-origin relay requires preflight handling) |
| SDK → Local Disk | File I/O | Filesystem permissions |
| API → Worker | Internal queue | Encrypted at rest (S3 + Postgres) |
| API → Webhook endpoint | HTTPS | TLS 1.2+ required |
Browser SDK
The Browser SDK supports relay transport (recommended) to avoid exposing your project token to ad blockers and network interceptors. Events route through your own backend, which forwards them to the DebugBundle API using the project token server-side.
Data Storage
| Data | Storage | Encryption | Retention |
|---|---|---|---|
| Raw events | S3 (or compatible) | Encrypted at rest (SSE-S3) | Tier-based in hosted DebugBundle: Free 7 days, Solo 14 days, Team 30 days. Self-hosted instances control their own retention. |
| Processed incidents | Postgres | Encrypted at rest | Tier-based in hosted DebugBundle: Free 7 days, Solo 30 days, Team 90 days. Self-hosted instances control their own retention. |
| Tokens | Postgres | SHA-256 hashed | Until revoked |
| Session data | Redis | In-transit encryption | Session lifetime |
| Local events | .debugbundle/local/events/ | Filesystem permissions | Until clean |
Local-Only Storage
In local-only mode, captured events, local incident state, bundles, and reproductions stay on the machine or storage volume where you run the SDK and CLI. DebugBundle Cloud does not receive those files, and connecting the project later does not backfill or upload existing local-only artifacts.
For self-managed servers, treat .debugbundle/local/ and .debugbundle/bundles/local/ like operational debugging data: keep them on persistent storage if you need retention across restarts, restrict filesystem access, and run debugbundle process where those files are available.
Retention
Hosted DebugBundle uses fixed tier-based retention today. Retention is not user-configurable per project or per organization in the hosted product.
Hosted Tier Retention
| Data type | Free | Solo | Team |
|---|---|---|---|
| Raw event blobs | 7 days | 14 days | 30 days |
| Incidents, bundles, and reproductions | 7 days | 30 days | 90 days |
How Retention Works
- Raw events are persisted in object storage and cleaned up according to the raw-event retention window for the owning tier.
- Incident records, the current retained bundle artifact, and the current retained reproduction artifact follow the incident retention window for the owning tier and are cleaned up together when that incident expires.
- Bundle history is not retained in V1. When a bundle is regenerated, DebugBundle keeps the latest version rather than storing historical snapshots.
- Cleanup is enforced by scheduled worker jobs. Deletion is automatic rather than a manual support process.
- Cleanup may lag slightly behind the exact cutoff time, but hosted retention is enforced within 24 hours of the tier limit.
Important Detail About Occurrences
DebugBundle does not keep every raw occurrence forever inside the retention window. To control storage cost and noise, the system retains raw event blobs only for sampled occurrences that matter for debugging, such as first, latest, post-deploy, and highest-severity cases. Other occurrences can remain as summarized metadata for grouping, counting, and bundle logic without preserving every raw payload.
Self-Hosted Retention
Self-hosted deployments control their own retention policy. DebugBundle does not currently provide a hosted-style per-project retention settings surface; in self-hosted environments, operators own the infrastructure and retention choices.
Input Validation
All external inputs are validated using Zod schemas at the API boundary:
- Request bodies are parsed with strict schemas (unknown fields rejected)
- Query parameters are coerced and validated
- Path parameters are validated
- The ingestion endpoint validates each event individually and returns per-event errors
Invalid inputs receive a 400 response with a machine-readable error code. Internal error details are never exposed to clients.
Rate Limiting
Event ingestion is rate-limited per project token to prevent abuse:
| Tier | Events per Minute |
|---|---|
| Free | 60 |
| Solo | 300 |
| Team | 1,000 |
Rate-limited events are rejected with "rate_limited" in the errors array. The SDK handles backoff automatically.
No Sensitive Data in Logs
The API and worker processes:
- Never log plaintext tokens
- Never log raw event payloads
- Never expose stack traces to API clients
- Log only structured metadata (event counts, incident IDs, timestamps)
Self-Hosted Security
When self-hosting DebugBundle, your data never touches third-party infrastructure:
- All components (API, Worker, Postgres, Redis, S3) run in your Docker Compose stack
- No phone-home, no telemetry, no external API calls
- You control encryption keys, network policies, and access controls
- See the Self-Host Guide for deployment instructions
Next Steps
- Webhooks — Webhook setup and signature verification
- Authentication — Token management
- Pricing — Tier limits, retention windows, and allowance differences
- Troubleshooting — Common issues and fixes