Security
How Qevra is built
Specific enough to be checked. If you are evaluating Qevra for real customer data, the last section is the one to read first.
The boundary between workspaces
One rule sits under everything else: Qevra never takes the workspace identity from the caller. A request cannot name the workspace it wants to read, in a body, a query string or a header.
In the dashboard, the active workspace comes from the signed session cookie, and membership is checked against the database on every read — a stale session cannot outlive a revoked membership. Over the API, the workspace is derived from the key: a key belongs to exactly one workspace and that binding is fixed when the key is created.
This means the usual multi-tenant failure — an identifier swapped in a request until someone else’s data comes back — has nothing to attack. There is no parameter to change.
API keys
A key is generated from 24 bytes of cryptographic randomness and shown to you exactly once. What we store is a SHA-256 digest of it, plus the prefix and last four characters so you can tell your keys apart in a list. A leaked database yields no usable keys.
Revoking a key writes a timestamp rather than deleting the row, so the requests it made stay attributable afterwards. Keys carry their environment — qevra_live_ or qevra_test_ — in the key itself, and it cannot be changed later.
Accounts and sessions
Passwords must be at least twelve characters and are stored only as a hash, computed by Better Auth. Qevra never holds a password in the clear and never writes one to a log.
The session cookie is HTTP-only, so no script can read it, and marked Secure in production, so it travels only over TLS. Sessions last thirty days.
The database
Row level security is enabled on every table, with no policies attached. That combination denies everything by default. Qevra connects as the owning role and bypasses RLS, so the application is unaffected — the lockdown exists for a different reason.
Managed Postgres serves the public schema over HTTP. A table left without RLS is a table published to the internet to anyone holding the project’s publishable key. Enabling it on every table, and revoking the grants the HTTP layer relies on, closes that door rather than trusting that nobody finds it.
Development and production use entirely separate database projects. Nothing local can reach production data, and the end-to-end test suite — which deletes the records it creates — proves which database it is pointed at before it runs, and aborts if it is the wrong one.
Payments
No card number, expiry or security code ever reaches Qevra. Checkout is hosted by Stripe and the details are typed on Stripe’s page.
Credit is added only when a webhook arrives whose signature verifies against the raw request body, and the amount credited is read from the Stripe session rather than from anything the browser said. Each top-up is recorded against the checkout session identifier, which is unique in the ledger, so a webhook delivered twice credits once.
What reaches the logs
Logs carry metadata — a request identifier, the endpoint, the outcome, timings — and not the content of support requests. The question a customer asked does not end up in a log line, which is the one place data tends to escape the boundaries drawn for it.
Errors returned to a caller are deliberately unhelpful to a stranger: a generic message and a request identifier. Quote the identifier and we can find the detail on our side.
Untrusted text reaching the model
Indexed knowledge is content Qevra did not write, and a document can contain text designed to redirect the model reading it. Retrieved fragments are handed to the model wrapped and labelled as untrusted source material, separated from the instructions, and the model is constrained to a strict response schema.
It reduces the risk rather than removing it. Treat an answer as derived from your own documents, because that is what it is: index something hostile and you may get it back.
The application
Every input crossing a boundary is validated against a schema before anything touches it. Secrets are server-only and cannot be imported into browser code; nothing sensitive is exposed to the bundle. Responses carry nosniff, X-Frame-Options: DENY, a restrictive Permissions-Policy and a Referrer-Policy.
Not in place yet
Qevra is in private development, and the honest list of what it does not do is more useful than a longer list of what it does.
- Rate limitingNot in place
No per-key or per-workspace quota is enforced. Traffic that threatens the service is dealt with by hand today.
- Audit logNot in place
Key creation, revocation and membership changes are not recorded as reviewable events, so there is no history to hand you after an incident.
- Content Security PolicyNot in place
The response headers below are set; a CSP is not among them yet.
- Two-factor authenticationNot in place
Sign-in is email and password only. There is no TOTP, no passkey and no SSO.
- Self-service export and deletionNot in place
Both are honoured, but by a person answering an email rather than by a button in the dashboard.
- External auditNot in place
No third-party penetration test has been run, and there is no SOC 2 or ISO 27001 certification. Anyone who tells you otherwise about a product this young is guessing.
Reporting a vulnerability
Write to security@qevra.dev with enough detail to reproduce the problem. We aim to acknowledge within three working days and to tell you what we intend to do about it.
There is no paid bounty yet. Please do not run automated scanners against the production service, do not access data belonging to anyone else, and give us a reasonable chance to fix something before publishing it. Report in good faith and we will not come after you.
Last updated 8 August 2026. See also the privacy notice.