Security
What protects your data in Wallras today, what every release is checked for, and what hasn’t been done yet. Facts only, no badges.
Where we stand
Below is our security assessment: standard tools and attack tests, with every finding fixed, and the same checks on every release.
Security testing, 11 November 2026
An internal security assessment of the engine, the license server and this website, using standard open tools and attack tests run against working copies of both servers. Every finding was fixed and re-tested; the checks below now pass, and they run again on every release.
| Check | Tool | Result |
|---|---|---|
| Code scan for the OWASP Top 10, Python, JavaScript and secrets rules (332 rules) | Semgrep | 0 findings |
| Python security scan | bandit | 0 high severity; every medium finding reviewed (all were queries with bound parameters) |
| Known vulnerabilities in every dependency of both servers | pip-audit | 0 |
| Container image | Trivy | 0 critical with a fix available |
| Keys, tokens and passwords in the code and in the entire git history | detect-secrets, gitleaks, pattern scan of every commit | 0 secrets ever committed (the only key in the code is the public sign-in key browsers need, checked to be the public one) |
| Every engine endpoint without a token, with a wrong, truncated or read-only token, and with the app token | Attack test suite | All refused; read-only and app tokens can only do what their role allows |
| Token guessing | Attack test suite | Address locked out after 20 wrong tokens a minute, others unaffected |
| One customer reading, paying or changing another’s bills and licenses; every admin action without the admin token | Attack test suite | All refused |
| SQL injection, script injection and malformed input in every field a stranger can reach | Attack test suite | Stored and returned as plain data; no server errors |
| Forged, replayed and stale payment webhooks | Attack test suite | All refused |
| Requests from other websites using a visitor’s session | Attack test suite | Refused (only this site is allowed) |
| Oversized and parser-crashing requests | Attack test suite | Refused before parsing; servers stay up |
| Disaster recovery: restore a live backup on a new engine | Recovery test | Same history, tamper check intact, keeps recording |
| Website security headers | Mozilla HTTP Observatory | A+ (score 130), 12 of 12 tests passed |
Found and fixed in this assessment: a request nested thousands of levels deep could crash the license server or stop the engine’s feedback endpoint (now refused before parsing); the license server’s size limit could be bypassed by sending a body in chunks (now refused); the website allowed inline scripts in its security policy (all scripts moved to files, inline scripts now forbidden); payment redirects now only follow secure addresses on the payment provider’s domain; the SDK now refuses non-web addresses; one endpoint checked a request’s contents before its token (now the token first).
This is our own testing, and it’s repeatable: the attack tests are part of the automated suite every release must pass.
Your data
- Runs on your infrastructure. Prompts, answers, traces, test sets and every record are stored in a local database on your server. Wallras the company never receives them. See the data-flow diagram.
- What leaves: on a trial or paid plan, your license key, a random installation id and a monthly count of successful requests, about once an hour. Like any connection over the internet, our server also sees the time of each check and your server’s IP address. On Free, nothing.
- Encryption in transit: calls from Wallras to your AI provider use the provider’s HTTPS. Wallras serves HTTPS itself when given a certificate (
WALLRAS_TLS_CERT,WALLRAS_TLS_KEY; TLS 1.2 or newer, forward-secret ciphers), or plain HTTP behind your own TLS proxy or service mesh. - Encryption at rest: Wallras doesn’t encrypt its database file itself. Put it on encrypted storage (most cloud disks are by default; LUKS or BitLocker otherwise). How.
- Deleted means deleted: a deletion request removes the content and the random value mixed into its fingerprint, so the fingerprint that keeps the ledger checkable can’t be used to confirm a guess of what was said.
- Active-active: containers sharing a Postgres database write one hash chain, in order, under one database lock, so the tamper check covers the whole fleet. Use TLS to the database (
sslmode=require) and a database user that only Wallras uses: anyone who can write to the database directly can attempt what anyone with the SQLite file could, and is caught the same way, by your external receipts. - Hub and nodes: a node sends its hub statuses, counts and ledger receipts only, never prompts or answers, using a token that can do nothing else. The hub keeps every receipt, so each node’s history is checked against a copy held on another machine.
- Request limits: bodies over 32 MB are refused on every route before they’re read (
WALLRAS_MAX_REQUEST_MB), and keys passed in a URL, such as Gemini’s?key=, are removed from logs. - Keys stay with their provider: fallbacks and a judge on another provider must have their own keys, or Wallras won’t start. Text graded by a judge is fenced off as material, and the judge is told to ignore instructions inside it.
- Never in the way: recording and analysis happen after each answer is sent. If storage fails, answers still go through (tested).
Access control
- Named tokens with roles: admin (everything), viewer (read only) and app (can only send feedback, SDK reports, agent steps and OpenTelemetry traces, and can’t read anything). Tokens shorter than 24 characters are refused.
- Single sign-on with any OpenID Connect provider (Okta, Entra ID, Google, Auth0, Keycloak). Signatures are checked against the provider’s published keys; only public-key algorithms are accepted (no unsigned or shared-secret tokens); issuer, audience and expiry are checked; groups map to roles; every change is logged under the person’s name.
- Audit trail: every change made with an admin token or an SSO account is logged in the tamper-evident ledger with the token’s or person’s name, what was done and whether it worked.
- If you don’t set an admin token, Wallras generates a random one at start-up and prints it in the logs. There’s no default password.
- Tokens are compared in constant time, so they can’t be guessed by timing. After 20 wrong tokens in a minute, that address is refused for the rest of the minute.
- No trust based on IP address: behind a reverse proxy every caller can look local, so Wallras never relies on it.
- Proxied traffic uses your provider key exactly as your app sends it; Wallras doesn’t add its own authentication to the AI endpoint.
Keys and secrets
- Your provider key passes through to your provider and is never written to disk. The most recent one is kept in memory only, so hidden tests can run; set
WALLRAS_UPSTREAM_KEYto use a dedicated key instead. - Model swap diff sends your current key only to your own provider, never to a different address.
- A fallback provider (optional) gets its own key. Your provider key and every
X-Wallras-*header are removed first, and Wallras refuses to start with a fallback address but no key of its own. - The response cache (optional, off by default) is held in memory only, separately for each provider key, and emptied by every deletion request.
- Licenses are signed with a private key that exists only on the Wallras license server. The engine ships with the public key, which can check a license but can’t create one; edited or forged licenses are refused.
- The
X-Wallras-*headers and admin token are stripped before requests reach your provider.
Records and deletion
- Every record is tamper-evident: chained to the one before it, so editing, adding or deleting any record shows up in
GET /wallras/integrity. Keep receipts off the server to catch a full rewrite; the threat model explains what this does and doesn’t prove. - Deletion requests remove matching content from answers, consistency checks, decision records and regression test sets, empty the response cache, keep the chain intact, and log the deletion.
- Agent steps your app reports carry only a name, kind, timing, outcome and error type, never inputs or outputs. Regression runs store which items passed, not the answers.
Every release
An engine image is only published if all of these pass:
- The full automated test suite: about 240 engine tests, most of them end to end against a running engine, including attacks on the tamper evidence, licenses and sign-in tokens, plus about 65 for the license server and website.
- Dependency audit (
pip-audit) against known vulnerabilities. - Static security scans (
Semgrepwith OWASP Top 10, Python, JavaScript and secrets rules, andbandit): any Semgrep finding or high-severity bandit finding fails the build. - Secrets scan (
gitleaks) of the whole git history. - Attack tests against running copies of both servers (above), on SQLite and on Postgres.
- Container scan (
trivy): any critical vulnerability with a fix available fails the build; high ones are reported.
The container runs as an unprivileged user on a minimal Python base image. Dependencies are pinned to exact versions. Every image carries its version and source, and versions are kept so you can pin or roll back.
Website and account
- The website loads no third-party scripts, fonts or trackers. It sends a strict Content Security Policy that forbids inline scripts, HSTS, no-framing, no-sniffing and cross-origin isolation headers. There are no cookies; sign-in sessions stay in the browser.
- Sign-in is handled by Supabase. Payments are handled by the payment provider; Wallras never sees card details.
- The license server stores your account email, plan, license records and usage counts. See the privacy policy.
Deployment notes
- High availability: run several containers on Postgres (active-active), or one container with automatic restarts, a readiness check (
/wallras/ready), live backups and a fallback to your provider (see Running it reliably). - Encryption at rest: keep Wallras’s data on encrypted storage, as most platforms do by default (how).
- Deletion requests clear the live database immediately. Backups you made earlier follow your own rotation, and compacting the database (VACUUM) clears freed space.
Report a vulnerability
Email security@wallras.com with what you found and how to reproduce it. We aim to acknowledge within 3 working days and to keep you updated until it’s fixed. Please give us a reasonable time to fix it before telling others. We won’t take legal action against good-faith research that avoids privacy violations, data destruction and service disruption.