Access control
Multi-factor authentication, least privilege, production access, joiners and leavers, credential handling and service accounts.
Required on every account, with no exception process
- Multi-factor authentication is the standing requirement on every administrative account — Cloudflare, Supabase, GitHub, the domain registrar and the mail provider. The enrolment state of each account is recorded in the access-control evidence and checked at every access review; an account found without a second factor is enrolled the same day. No account with production reach may be entered with a password alone.
- The factor is an authenticator app on every platform — time-based one-time codes. SMS is not used as a factor anywhere.
- Recovery codes are held on paper and in an end-to-end-encrypted drive, separate from the password manager, and a second holder is named in the access review.
- There is no MFA exception process.
Four principals, and what each can do
| Principal | Can | Cannot |
|---|---|---|
| Anonymous reader (Terminal, Atropos) |
Read the public record through the applications | Execute any database function, reach any administrative route, or obtain a licence-restricted record |
| API key holder | Read the record within the scope and rate limit of the key | Write anything, reach administrative routes, or obtain records excluded by the licence filter |
| The application | Serve the public record on the owner-role connection; serve a customer's rows only through a transaction-scoped role that cannot bypass row-level security | Read another organisation's rows on the customer plane, or bypass a policy there |
| Engineering owner | Deploy, run migrations, and read and change production configuration and data | Recover a stored secret or an issued API key in plaintext — neither is retrievable by anyone |
The database enforces its own half rather than relying on the application:
row-level security is enabled on 92 of 92 tables, and the public role has
execute permission on 0 of the 201 functions. Both measured 11 October 2026
from /api/v1/facts.
One holder, with grant dates available on request
- Production access is held by the engineering owner and by nobody else. The list is supplied under NDA with grant dates. An automated operating agent assists with operation under the owner's identity and credentials, on the owner's endpoint; it holds no credential of its own and is not a second holder.
- Administrative endpoints are gated twice — an identity-aware proxy at the edge, then a bearer token inside the application. Compromising one does not grant access.
- There is no standing shell access. No SSH, no bastion, no jump host, because there is no server. Administrative action is an authenticated HTTP call or a platform console operation, both logged by the platform.
- No shared accounts. Every human principal is individually identified; automation uses service credentials not attached to a person.
- The development machine is production access. It holds the Worker's secrets in a git-ignored file and reaches the production database, because there is no second copy of the data to develop against; it is held by the one production access holder, full-disk encrypted, and covered by this page. The test suites use fixtures.
Quarterly, and on any event that changes who should have what
- Quarterly, every account on every platform is reviewed against the question of whether that principal still needs that level of access. The first review was recorded on 12 September 2026; the next falls in December 2026.
- Immediately after a departure, a role change, or an incident.
- API keys are reviewed alongside the account they belong to. A key with no request in 90 days is flagged as dormant in the workspace and revoked at the next review unless its owner claims it.
- Each review is recorded with the date, the reviewer and what changed. These records form part of the evidence pack.
Both directions, with stated deadlines
Vaethra has one engineer and has had no joiner and no leaver. The procedure below is the committed one; it applies from the first, and each step is recorded when it is taken.
Onboarding
- Confidentiality undertaking signed before any access is granted.
- Background check where the role carries production access, to the extent applicable employment law permits.
- Individual accounts created with MFA enforced from first login.
- Access granted at the minimum level for the role. Production access is not granted by default and is not granted during onboarding.
- Security responsibilities set out in the engagement terms.
Offboarding
- Within one business day of a planned departure, and immediately on an involuntary one, every account is disabled.
- Any shared credential the person could have seen is rotated, not merely revoked from them.
- Device access is removed and company data removed from personal devices.
- A written record is kept of what was revoked and when.
Where each kind of secret is held
| Credential | Stored | Recoverable |
|---|---|---|
| Application secrets — provider keys, shared secrets | Platform secret store, encrypted, injected at runtime | No. A lost secret is rotated rather than recovered. |
| Customer API keys | SHA-256 digest in the database | No. Shown once at creation. |
| Database credentials | Cloudflare Hyperdrive's configuration and the platform secret store. Never in the repository. | No. Rotated, never recovered. |
| Human account passwords | A password manager with MFA on the manager itself | By the holder only |
| MFA recovery codes | Encrypted offline store | By the holder only |
- No secret is present in the repository, in a tracked configuration file, or in build output. Every push is scanned for credential patterns in the pipeline, and the scan is the first gate the deploy waits on.
- Rotation is annual as a minimum, and immediate on any suspicion of exposure, on a departure, or on a provider's advice.
- Customer key revocation clears the edge cache immediately, so a revoked key stops working on the next request.
Three, each scoped to a single function
| Account | Scope |
|---|---|
| Application to database | Two pooled connections held by Cloudflare Hyperdrive: the owner role for the public record, and a role that cannot bypass row-level security for the customer plane. |
| Deployment | A scoped platform token held by the CI system, able to deploy the Worker and the Pages projects and nothing else. It cannot read data. Issued 12 September 2026. |
| Provider API keys | Read-only credentials issued by upstream data providers, each scoped to that provider's own data |
None is attached to a person, so none breaks when a person leaves. All three are in the rotation schedule and each is separately revocable.
How your side is identified, limited and logged
Everything above is about our access to the system. This section is about yours, because a security review asks both and the page previously answered only one.
Identity
Access to the API is by API key. Keys are issued and members are administered in the workspace, which has accounts — email sign-in with a second factor, and roles enforced by the database. The API itself has none: an integration carries a key, not a session, so there is no account takeover, no session fixation, no password reset flow and no credential-stuffing surface on the data path. When someone leaves your team you remove their workspace membership and, if they held one, rotate the key.
| Property | What it does |
|---|---|
| Prefix | Every issued secret starts vtx_, so a leaked key is
greppable in a repository scan and obvious in a log. |
| Storage | Compared as a SHA-256 digest. The secret is displayed once, at issue, and is not recoverable afterwards — including by us. |
| Scopes | Every key carries the read scopes it was issued with and nothing else; the registry of scopes is shown beside the issue form. |
| Rate limit | Per key, per minute, at the platform rate of 600. Enforced by the platform's rate limiter before the request handler runs. |
| Row cap | Per request. A larger limit is clamped to 5,000
rather than refused, and total always reports the
true match count, so a page never poses as the whole. A lower cap
recorded on a key is not yet enforced. |
| Expiry | Optional per key. A trial key is issued with one. |
| Revocation | Immediate, and the key stops working on the next request. There is no cache to wait out. |
| Last used | Recorded per key, and a key unused for 90 days is shown as dormant. |
Segregation, without seats
Issue one key per system rather than one per person — the ingestion job, the dashboard, the model, the notebook. Each gets its own rate limit, its own row cap and its own audit trail, so one team's backfill cannot exhaust another team's quota, and revoking one does not interrupt the others. That is the same isolation a role model gives you, expressed as keys, and it does not require us to hold a directory of your staff.
Audit
Every request made with an issued key is recorded: the key, the path, the query, the status, the row count, the bytes and the elapsed milliseconds. A per-key usage summary is shown in the workspace; the full request log for a key is produced on request. Denied anonymous requests are recorded too, deduplicated, against a salted digest of the requesting address rather than the address itself.
What we do not have, and what would change it
Stated plainly because a questionnaire will ask, and because a vendor who lists a control they do not operate is worth less than one who does not.
| Control | Status | What it depends on |
|---|---|---|
| SSO / SAML | Not built | Requires user accounts, which the product deliberately does not have. If your procurement requires SSO, say so on the first call — it changes the shape of the product, not the timeline of a feature. |
| SCIM provisioning | Not built | Same dependency. |
| Per-seat roles | Not built | Same dependency. Per-key segregation above is what exists. |
| IP allow-listing per key | Available | An exact-match list per key, checked on every request; a key with a list and an unidentifiable caller is refused. |
| Customer-visible audit export | Planned | The per-key log exists and is produced on request; the export screen in the workspace does not exist yet. |