OpenSend docs
Security
OpenSend is an email platform, so the security model centers on tenant isolation, API-key protection, signed webhooks, secret encryption, rate limiting, and explicit operator-controlled integrations.
API keys
API requests use bearer tokens. Store API keys only in server-side environments and rotate them when they leave the boundary you control.
Recommended operator posture:
- Treat API keys like production credentials.
- Store hashes or encrypted values at rest; never commit real keys.
- Scope keys to the organization/tenant that created them.
- Revoke unused keys and rotate leaked keys immediately.
Tenant isolation
Dashboard and API routes are tenant-scoped through Better Auth organizations and server-side ownership checks. Production changes that touch auth, tenants, API keys, or persistence need real route/database proof, not client-side mocks.
Webhook signing
Outbound webhooks are HMAC signed with Svix-compatible headers. Consumers should verify signatures before processing events, and operators should rotate endpoint signing secrets if an endpoint is compromised.
WEBHOOK_SECRET_ENCRYPTION_KEY encrypts webhook signing secrets at rest. Docker Compose passes it into both the app and ingester. .env.example includes a local-only placeholder for evaluation; production deployments must replace it with a generated secret:
openssl rand -hex 32Ingester and job endpoints
The ingester exposes SES/SNS event routes and internal job routes. Keep the ingester public only where providers need to reach it.
/events/sesreceives SES/SNS events and verifies provider signatures./jobs/*endpoints are scheduler/worker control routes and requireAuthorization: Bearer ${INGESTER_JOB_TOKEN}./events/inboundrejects production requests unlessINGESTER_INBOUND_TOKENis configured and sent by the inbound provider.
Use unique 32+ character secrets for INGESTER_JOB_TOKEN and INGESTER_INBOUND_TOKEN in shared or production deployments.
Rate limiting
API rate limiting is enforced in Next.js middleware. Local single-process evaluation can use the default memory behavior. Shared and production deployments should use Redis:
RATE_LIMIT_BACKEND=redis
REDIS_URL=rediss://default:<password>@your-cache-endpoint:6379When Redis-backed rate limiting is selected, keep Redis private to the runtime network and use TLS endpoints.
Secret management
Never hardcode provider tokens, OAuth secrets, database passwords, API keys, Stripe secrets, Cloudflare tokens, AWS credentials, or webhook secrets in source, images, scripts, or screenshots.
The local BETTER_AUTH_SECRET placeholder in .env.example exists only for localhost evaluation. Production startup checks reject that placeholder when the configured app URL is not localhost.
For production:
- Generate secrets with a cryptographically secure generator such as
openssl rand -hex 32. - Store them in a secrets manager.
- Inject them into the app, ingester, scheduler, and migrator at runtime.
- Rotate secrets after incidents, personnel changes, or accidental exposure.
Telemetry boundary
Self-hosted OpenSend does not send version pings, anonymous analytics, error reports, or license checks to OpenSend-operated vendors unless an operator configures those variables. See Privacy.
Vulnerability reporting
Please report suspected vulnerabilities privately by emailing security@namuh.co. Include the affected version or commit, impact, reproduction steps, and whether any secret or tenant boundary may be involved.
Do not include real customer data, production credentials, or exploitable details in public issues.