Every layer of HeyStackIQ โ from authentication to background jobs to email delivery โ was designed with security and privacy as defaults, not afterthoughts. Here is exactly what we do and how.
Authentication & Sessions
Every session uses a signed JWT access/refresh token pair. Access tokens expire quickly โ refresh tokens rotate on every use, so a stolen refresh token is immediately invalidated the next time the legitimate session checks in.
Dual-token JWT architecture
Access tokens (short TTL) and refresh tokens (rotated on use) are signed with separate secrets. Compromise of one secret does not compromise the other.
httpOnly cookie storage
Tokens are stored in httpOnly cookies โ they are invisible to JavaScript running in the browser. This eliminates the entire class of XSS token-theft attacks that plague localStorage-based auth.
bcrypt password hashing
Passwords are hashed using bcryptjs before storage. The plaintext password is never persisted, logged, or transmitted after the initial request.
Email verification flow
Accounts must verify their email address before accessing the platform. Unverified accounts cannot enroll campaigns or trigger outreach.
Authorization & Tenant Isolation
Every database query is scoped to a workspaceId before execution. There is no query path in the system that can return data across tenant boundaries โ it is enforced architecturally, not by policy.
Multi-tenant isolation by design
workspaceId is a mandatory filter on every Prisma query. Cross-tenant data access is impossible without bypassing authentication middleware โ there is no admin backdoor that could accidentally expose it.
Role-based access control
Four roles: Owner, Admin, Member, Viewer. Sensitive operations (billing, team management, approval queue, failed job replay) require Owner or Admin role, enforced in Express middleware before the handler body runs โ not inside it.
Self-modification guards
Users cannot change their own role, cannot remove themselves from a workspace, and cannot demote an Owner. These are hard checks, not UI affordances โ they are enforced at the API layer.
Scoped approval & escalation queues
Approval requests and escalation queues are workspace-scoped. Members only ever see their workspace's queue โ there is no "global" view.
Data Protection & Encryption
Third-party API keys (Apollo, HubSpot, Resend) are encrypted before they hit the database. Failed background jobs are persisted to a dead-letter queue with all secrets automatically redacted before storage.
Connector credential encryption
Every API key stored for a connector (Apollo, HubSpot, Resend) is encrypted with AES using the ENCRYPTION_KEY environment variable before being written to the database. The key is never stored alongside the ciphertext.
DLQ secret redaction
Background job payloads can contain sensitive identifiers. Before a failed job is persisted to the dead-letter queue database table, any field whose name contains "apikey", "token", "password", "secret", "credential", or "accesstoken" is replaced with [REDACTED].
Error sanitization
API error responses return a plain_language field for user display. Internal stack traces, SQL errors, and system paths are never included in responses sent to the client.
Idempotency keys on email delivery
Every email send job carries a unique idempotency key. Retried jobs do not re-send duplicate emails โ the key is checked before delivery, preventing contact abuse from infrastructure retries.
Network & Transport Security
The API enforces HTTPS in production via Fly.io configuration. CORS is configured with an explicit allowlist โ wildcard origins are not permitted. A global rate limiter protects every endpoint from abuse.
HTTPS-only in production
fly.toml sets force_https = true โ all HTTP traffic is redirected to HTTPS at the edge before it reaches the application. TLS termination is handled by Fly.io's managed certificates.
CORS origin allowlist
The ALLOWED_ORIGINS environment variable explicitly lists permitted origins. Requests from any other origin are rejected by the CORS middleware before reaching any route handler. Wildcard (*) origins are never used.
Global rate limiting
200 requests per IP per minute across all endpoints. Standard RateLimit-* headers are returned so clients can back off gracefully. The limit applies before authentication โ so unauthenticated probing is also rate-limited.
Automated health checks
Fly.io polls /health every 15 seconds. Unhealthy instances are automatically replaced โ there is no manual intervention required to recover from a crashed node.
Privacy & GDPR Compliance
GDPR legal bases and consent status are tracked per contact at the database level. The auto-enrollment engine checks consent status before enrolling any account โ withdrawn consent is a hard block, not a soft preference.
Consent status & legal basis tracking
Every contact record carries a ConsentStatus (UNKNOWN, GRANTED, WITHDRAWN) and LegalBasis (LEGITIMATE_INTEREST, CONSENT, CONTRACT, EXISTING_CUSTOMER) field. These are schema-level fields, not freeform notes.
Enrollment compliance gate
The auto-enrollment engine evaluates consent before enrolling any opportunity. Accounts with WITHDRAWN consent are blocked from enrollment entirely โ this is enforced in the eligibility check service, not the UI.
Frequency & contact limits
Workspace-level maxEnrollmentsPerDay and maxEmailsPerDay caps are enforced by the enrollment engine. These limits cannot be bypassed by manually triggering enrollments from the UI.
Cooldown enforcement
Each account has a configurable re-contact cooldown period. Accounts contacted within the cooldown window are blocked from re-enrollment regardless of their signal score.
Observability & Operational Controls
The platform maintains a full activity event log of every autonomous action taken. Failed background jobs are persisted before Redis evicts them, and can be replayed by admins. Plan-gated workers prevent suspended workspaces from sending any outreach.
Full autonomous action timeline
Every AI decision โ enrollment triggered, approval requested, escalation assigned, email sent, reply analyzed โ is written to the ActivityEvent table. The Autonomy โ Timeline view shows the complete audit trail.
Persistent dead-letter queue
When a background job exhausts all retry attempts, it is written to the FailedJob table before Redis evicts it. Admins can inspect the payload (with secrets redacted) and replay the job from the UI or API.
Plan-gated worker execution
Every worker job checks the workspace's operational state before executing. Suspended, expired, or over-limit workspaces cannot send emails, trigger enrollments, or run AI scoring โ enforced before job logic runs, not after.
Error monitoring via Sentry
Sentry is integrated in both the API server and the background worker. Unhandled exceptions are captured with full context and routed to your configured DSN. Sentry is opt-in via environment variable โ it is never active without explicit configuration.
We secure the platform. You secure your configuration. Set strong secrets in JWT_ACCESS_SECRET, ENCRYPTION_KEY, and ALLOWED_ORIGINS. Rotate API keys if a connector is disconnected. Review your escalation and approval queue regularly. We will flag anomalies โ you make the final call.
We are happy to share architecture diagrams, walk through our data flows, or discuss specific compliance requirements for your use case.