Security

Authentication, sessions, API keys, encryption, where AI calls go, subprocessors, and compliance status — as implemented in the code today.

This page tells you how Spectrace authenticates users, stores secrets, sends data to AI providers, and which third parties process your data, so you can evaluate it before you connect a repository.

Every statement here describes the application code as of the lastVerified date. Where a property belongs to a hosting provider rather than to Spectrace's code, the page says so.

Authentication

Spectrace uses Supabase Auth for user identity. Two sign-in methods are live:

  • Email and password. Sign-up creates the user unconfirmed and emails a verification link that expires after 24 hours. Only a SHA-256 hash of the verification token is stored.
  • OAuth with Google or GitHub. Sign-in uses Supabase's native OAuth flow with PKCE (Proof Key for Code Exchange, a protection against authorization-code interception) and returns through /auth/callback.

Not available today: SAML or enterprise single sign-on (SSO), SCIM provisioning, Microsoft sign-in (wired but hidden), and two-factor authentication. Do not plan an adoption around any of these; ask for current status.

Sessions

A session is a set of Supabase auth cookies managed by @supabase/ssr. Middleware re-reads the cookies on every matched request, refreshes an expired session, and writes the refreshed cookies back to the response. Requests to /dashboard, /projects and /editor without a valid session redirect to /login.

Session lifetime and idle timeout are configured in the Supabase project, not in Spectrace code. There is no session list or "sign out everywhere" control in the product.

Editor sign-in

The VS Code extension signs in with a one-time authorization code that expires after 120 seconds. Only the SHA-256 hash of the code is stored, and the Supabase tokens held against it are encrypted with AES-256-GCM until the extension redeems the code. Redemption is a single atomic database update, so a code cannot be redeemed twice.

API keys

An API key is a spc_k1_… string that authenticates the Spectrace MCP (Model Context Protocol) server, the endpoint AI agents call (hosted POST /api/mcp and the spectrace-mcp process). REST routes under /api/v1/* do not accept API keys; they accept a Supabase session token and are used by the VS Code extension.

  • Keys are 32 random bytes, hex-encoded, behind the prefix.
  • Only the SHA-256 hash of the full key is stored. The plaintext is shown once at creation and cannot be retrieved later.
  • Keys carry an optional expiry, chosen at creation, and can be revoked in Settings → Developers.
  • For the hosted endpoint (POST /api/mcp) Spectrace re-reads the key owner's organization membership on every request; a user removed from the organization is rejected even if the key row still exists. The spectrace-mcp stdio process validates the key and membership once at start-up, so removal takes effect when that process restarts.

See Manage API keys for the full lifecycle.

Secrets at rest

Integration tokens

OAuth tokens for GitHub, Linear, Jira and Slack are encrypted at the application layer before they are written to the database. The cipher is AES-256-GCM with a random 16-byte IV and an authentication tag; the stored value is iv:authTag:ciphertext in hex.

The encryption key is derived by SHA-256 from an environment variable, one per provider: GITHUB_TOKEN_ENCRYPTION_KEY, LINEAR_TOKEN_ENCRYPTION_KEY, JIRA_TOKEN_ENCRYPTION_KEY, SLACK_TOKEN_ENCRYPTION_KEY. Linear and Slack fall back to the GitHub key when their own is unset; Jira does not. Rotating a key makes existing tokens undecryptable, and affected users must reconnect the integration.

Hashed-only values

These are stored as SHA-256 hashes and never in plaintext: API keys, VS Code authorization codes, sign-up verification tokens, and account-deletion verification tokens. Beta access codes are stored in plaintext in the access-request table; only the access log records a hash of the code that was attempted.

Provider secrets

GitHub App private key, webhook secrets, OAuth client secrets, AI provider keys, and similar values live in the hosting environment's secret store. Spectrace code reads them from environment variables and does not persist them.

Transport and database

The application connects to Postgres with ssl: "require" when NODE_ENV is production. TLS termination for browser traffic and disk-level encryption of the database are properties of the hosting providers (Vercel and Supabase), not of Spectrace code, and this page does not assert their specifics.

The application sets these headers on every route: X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy denying camera, microphone, geolocation and browsing-topics, X-XSS-Protection: 1; mode=block. No Content-Security-Policy or HSTS header is set in code.

Rate limiting

Middleware limits /api/* requests per minute: 10 per IP for /api/auth/*, 300 per bearer token when an Authorization: Bearer header is present, otherwise 100 per IP. The counter is in-memory and per instance, so the effective limit across all instances is higher. Health and webhook endpoints are exempt.

Where AI calls go

Spectrace calls AI models through one provider seam (packages/ai/src/provider). The chat provider is chosen by the AI_PROVIDER environment variable: OpenAI by default, or Anthropic or Google when configured. Four call sites in the web app — ambiguity analysis, requirement enhancement, commit-message generation and PR progress hints — call OpenAI directly with gpt-4o-mini regardless of that setting.

Embeddings (numeric vectors used for semantic search and for the related-code context handed to the PR verifier; the requirement a pull request is verified against is resolved from references and traceability links, not embeddings) always go to OpenAI, model text-embedding-3-small, regardless of the chat provider.

What is sent

Depending on the feature you use, prompts contain: text, URLs and documents you import; requirement titles, descriptions, user stories and acceptance criteria; test-case steps; conversation transcripts from the clarifier; and, for PR verification, the PR description, the diff, and repository code context. Embedding requests contain requirement text and chunks of repository source code (path, language, symbol name and content). Those code chunks are also stored in the code_chunks table.

What is scrubbed

Before untrusted content enters a prompt, scrubSecrets replaces matches of a regex list with [REDACTED:<type>]: AWS access keys, GitHub tokens, Slack tokens, OpenAI keys, Stripe live keys, Google API keys, JWTs, PEM private-key blocks, and long values assigned to names such as password, secret, api_key or bearer. The content is then wrapped in an <untrusted> block and the system prompt instructs the model to treat it as data, not instructions.

This scrubbing is applied by the requirements extractor, requirement enricher, PR verifier, architecture proposer and test-case generator. It is not applied to embedding inputs, so requirement text and source-code chunks reach OpenAI unscrubbed, and it is not applied by the smaller agents (ambiguity analysis, AC suggestion, Gherkin and Playwright generation, cycle composition, project-context inference, clarifier) or the four direct OpenAI call sites. Treat the regex list as a safety net, not a guarantee.

Spectrace does not log raw prompts or model responses; its AI logger hashes any string longer than 128 characters. Spectrace's code sets no zero-data-retention or no-training options toward providers. Whether providers train on your data is governed by their terms for the account in use, and this page makes no claim about it.

Provenance

Enriched requirements, PR verifications, architecture proposals, and test cases generated from the requirement page carry a provenance record: provider, model, agent name, a prefix of the prompt hash, safety-layer version, and generation time. It is stored alongside the artefact in the ai_provenance column so you can tell which model produced what. Test cases generated by the background job or by the MCP spectrace_testcases_generate tool, requirements created by import, and ambiguity analyses do not carry a provenance record.

Subprocessors

These third parties are evidenced by code or configuration in the repository. Hosting regions are not published here; ask for them.

ProviderRole
SupabaseAuthentication, Postgres database, file storage for attachments
VercelWeb application hosting
Trigger.devBackground jobs: embeddings, PR verification, enrichment, import, account purge, Slack digests
OpenAIDefault chat provider and all embeddings
Anthropic, GoogleChat provider only when AI_PROVIDER selects them
SentryError monitoring in production; session replay masks all text and blocks media; authorization, cookie and API-key headers are scrubbed server-side
Upstash RedisOptional cache for authorisation lookups and membership data
StripeBilling
Brevo or ResendTransactional email, selected by EMAIL_PROVIDER
GitHub, Linear, Jira, SlackIntegrations you connect; data flows both ways once connected

No product-analytics SDK (page views, feature usage) is present in the code.

Compliance

SOC 2: not certified today. The privacy page states "SOC 2 Type II in progress with completion expected Q3 2026"; that is the privacy page's statement, and this docs page does not confirm the timeline. Ask for current status before relying on it.

There is no DPA document, SOC 2 report, SECURITY.md, or security.txt in the repository. To report a security issue or request documents, use the Send feedback widget in the dashboard.