Security
Last updated: March 25, 2026
Security is a first-class concern at Engineering Framework. This page describes the technical and organizational controls we have in place to protect your organization's data. We are transparent about how the system works so you can make an informed decision about trusting us with your engineering processes.
Authentication
All user authentication is delegated to Auth0, an industry-standard identity platform. We never store raw passwords. Auth0 handles credential management, session security, brute-force protection, and multi-factor authentication (MFA) support.
- Passwords are managed and hashed by Auth0 — we have no access to plaintext credentials.
- Sessions are issued as short-lived signed JWT tokens using the RS256 algorithm and verified against Auth0's public JWKS endpoint on every request.
- The MCP server validates tokens using express-jwt with JWKS-based key rotation support, enforcing audience and issuer claims.
- Password reset emails use a secure, time-limited token issued by Auth0. The reset endpoint deliberately returns the same response regardless of whether the email address exists, preventing email enumeration attacks.
Fine-Grained Authorization
Authorization is enforced using OpenFGA, a relationship-based access control (ReBAC) system based on Google Zanzibar. Every action — reading a project, updating a checklist item, deleting a department — is checked against OpenFGA before any database operation is performed. Passing authentication alone is never sufficient to access data.
- Permissions are modeled as relationship tuples (e.g., user:alice member organization:acme). No tuple means no access.
- Every API route and MCP tool performs an explicit permission check before touching the database.
- Role templates allow organizations to define named permission bundles. Permissions are stored as a canonical JSON hash to prevent silent drift.
- The MCP server's endpoint requires both a valid JWT and an explicit access:mcp-server FGA grant — users who have not been provisioned for MCP access cannot connect, even with a valid token.
- Super-admin capabilities (system checklist management, org plan upgrades) are gated on a dedicated system-super-admin role that is never automatically assigned.
Multi-Tenant Data Isolation
All data is scoped to an organization. Every query that retrieves or modifies organizational data is filtered by organizationId, preventing one organization from accessing another's data even if an access control check were somehow bypassed.
- Database foreign keys enforce referential integrity across org-scoped records (departments, projects, checklist items, role templates).
- Cascade deletes ensure that removing an organization removes all associated data.
- Users without an organization are redirected to an onboarding wizard and cannot access any organizational features until provisioned.
Encryption at Rest
Sensitive credentials stored in the database are encrypted using AES-256-GCM, an authenticated encryption scheme that protects both the confidentiality and integrity of stored values.
- GitHub Personal Access Tokens (PATs) used for the GitHub Sync feature are encrypted with AES-256-GCM before being written to the database.
- Each encrypted value uses a unique random 96-bit initialization vector (IV) and includes a 128-bit authentication tag, preventing ciphertext manipulation attacks.
- The encryption key is never stored in the database or codebase — it is loaded exclusively from the ENCRYPTION_KEY environment variable and injected at runtime via secrets management (Vercel environment variables, fly.io secrets).
- Raw PAT values are never logged, returned in API responses, or exposed beyond the internal sync process.
Encryption in Transit
All communication between clients and our services is encrypted using TLS (HTTPS).
- The frontend is served over HTTPS via Vercel, which terminates TLS and enforces HTTPS-only access.
- The MCP server is deployed on fly.io and is accessible only over HTTPS. The server is configured to trust the fly.io reverse proxy so that req.protocol correctly reflects HTTPS for downstream security checks.
- Database connections use TLS-encrypted connections to the managed PostgreSQL provider.
GitHub Sync Security (PRO / Enterprise)
The GitHub Sync feature allows organizations to override default framework content with content from a private GitHub repository. Several controls protect this integration:
- Only organization admins on PRO or ENTERPRISE plans can configure the sync — standard members cannot access the settings page.
- PATs are validated against the configured repository before being saved, ensuring only working credentials are stored.
- Inbound GitHub webhook payloads are authenticated using an HMAC-SHA256 signature. The comparison is performed using a timing-safe byte-by-byte comparison (Node.js crypto.timingSafeEqual) to prevent timing side-channel attacks.
- The webhook secret is generated server-side using crypto.randomBytes(32) and is never derived from user input.
- Webhook content-type is validated before parsing to reject non-JSON payloads.
MCP Server Hardening
The MCP server exposes your engineering data to AI agents such as Claude Code. Additional layers protect this surface:
- Helmet.js is applied globally to set security-related HTTP response headers (including X-Content-Type-Options, X-Frame-Options, Strict-Transport-Security, and others).
- Rate limiting is enforced globally using express-rate-limit, returning standard RateLimit-* headers and rejecting excess requests with a 429 response.
- CORS is enforced with an explicit origin allowlist. Requests from unlisted origins are rejected. The allowlist is configured via environment variable at deploy time.
- Internal error messages and stack traces are suppressed in production — clients receive a generic error message without implementation details.
- An in-process TTL cache (5-minute expiry) avoids a database round-trip on every request while ensuring that role changes or account deletions propagate quickly.
- A dedicated /health endpoint is the only unauthenticated route beyond the OAuth metadata endpoints required by the MCP protocol.
Input Validation & SQL Injection Prevention
All user-supplied data is validated using Zod schemas before reaching any business logic. Invalid or unexpected input is rejected at the API boundary with a structured error response.
- Database operations are performed exclusively through Prisma ORM, which uses parameterized queries. Raw SQL is not used, eliminating SQL injection risk.
- Zod schemas enforce field types, string lengths, and allowed values for all create and update operations across the frontend API routes and MCP tool handlers.
- JSON parse errors on incoming request bodies are caught explicitly and returned as structured 400 responses, not unhandled exceptions.
Secrets Management
No secrets are stored in the codebase or version control. All sensitive configuration is injected at runtime via environment variables.
- Frontend secrets (Auth0 credentials, database URL, encryption key) are managed as Vercel environment variables and are never included in the client-side bundle.
- MCP server secrets are managed as fly.io secrets, encrypted at rest by fly.io and injected into the container at startup.
- The ENCRYPTION_KEY required for AES-256-GCM encryption is validated at startup — the server refuses to start if the key is absent or incorrectly sized.
- The server configuration layer validates all required environment variables at startup and fails fast with a descriptive error if any are missing, preventing misconfigured deployments from silently degrading security.
Reporting a Security Issue
If you discover a security vulnerability, please report it responsibly. Do not open a public GitHub issue for security concerns. Instead, email us at security@engineeringframework.dev. We will acknowledge your report within 48 hours and work with you to understand and address the issue.