Concepts
Security model
The isolation, authentication, and storage guarantees Prometheus makes — and the threat models they address.
Prometheus protects three things, in this order: other tenants' data, your secrets, and your source. This page lays out the mechanisms, what they assume, and where the boundaries are.
Authentication
Two surfaces, two mechanisms:
| Surface | Credential | Lifetime | Notes |
|---|---|---|---|
Worker / MCP (api.prom.codes) | API key (bearer) | until revoked | Hashed with Argon2id; cleartext shown once. |
Dashboard (app.prom.codes) | Magic link → session cookie | 60 days, sliding | Cookie is HttpOnly, Secure, SameSite=Lax, scoped to .prom.codes. |
There are no passwords. The dashboard uses a managed magic-link
provider for the sign-in flow; the session cookie it issues is
distinct from the prom_tenant cookie that tracks the active tenant.
Authorisation — row-level security
Every domain table (workspaces, files, symbols, references,
embeddings, index_runs, api_keys, tenant_members,
tenant_invitations, tenant_audit_log, ...) carries a tenant_id
column and a Postgres CREATE POLICY enforcing it. Reads and writes
go through a connection that has set:
SET LOCAL request.jwt.claim.tenant_id = '...';
A key scoped to tenant T cannot read a row tagged T' — not
via the worker, not via the MCP server, not via direct Postgres if
the key ever leaked. Integration tests in CI round-trip an insert
under two distinct tenant identifiers and assert cross-tenant reads
return zero rows. The suite breaks the pipeline before deploy on a
regression.
Inside a tenant, the dashboard adds a coarser role check
(owner / admin / member) for mutating UI actions; see
Members & invitations.
Secrets at rest
| Secret | Storage | Reversible? |
|---|---|---|
| API key cleartext | never persisted | n/a |
| API key hash | api_keys.hash (Argon2id) | no |
| Invitation token cleartext | only in the email | n/a |
| Invitation token hash | tenant_invitations.token_hash (SHA-256) | no |
| Session cookies | signed JWT, expires after 60 days | no |
prom_tenant cookie | signed payload | tampering is rejected |
| Source files | tmpfs on the worker, deleted after the run | n/a |
The worker disk is tmpfs. Clones never touch persistent storage.
Data in transit
- All public endpoints are HTTPS-only (HSTS). Plain HTTP redirects to HTTPS at the edge.
- The indexer worker talks to Postgres over TLS using the managed certificate of the EU Frankfurt cluster.
- The embedding hop goes out over TLS to the configured provider. Region mode controls whether that provider is US-default or EU-strict — see Region mode.
Data sovereignty
| Stage | Region |
|---|---|
Web + dashboard (prom.codes, app.prom.codes) | EU Frankfurt edge |
Worker (api.prom.codes) | EU Frankfurt |
| Postgres (symbols, embeddings, audit) | EU Frankfurt |
Embedding hop — default and eu-permissive | US (provider) |
Embedding hop — eu-strict | EU Frankfurt (operated by us) |
Storage is always EU. The single optional out-of-region hop is the
embedding call, which carries chunk text only — never symbol
names, file paths, or commit metadata. Switch to eu-strict to
eliminate that hop entirely.
Audit trail
Every meaningful mutation writes a row to tenant_audit_log:
- Tenant create / rename
- Workspace create / rename / delete
- API key create / revoke
- Member invite / invite-revoke / invite-accept / remove
- Tenant switch (cookie level)
The audit log is append-only at the application layer; there is no UI to edit or delete rows. The same surface backs the Audit page and the CSV export, both filtered by tenant via RLS.
What we do not store
- Commit history or author identity.
- The plaintext of any secret — API keys, magic-link tokens, invitation tokens.
- Embedding-provider responses beyond the resulting vectors and a
request-scoped audit row in
eu-permissivemode. - Files outside the configured
include/excludeglobs.
Reporting a vulnerability
Mail security@prom.codes with the
details. We acknowledge within one business day (CET) and aim for a
remediation timeline within three business days for high-severity
issues. We do not currently run a paid bug bounty, but we publicly
credit reporters in /changelog with their consent.