Security
Where your data goes, and where it doesn't
Support conversations are dense with personal data, which makes “where does this run” the first question a privacy review asks. Deskbell runs on your infrastructure: one outbound call to the model provider you chose, or none at all with a local model. This page also lists the security work that isn't finished.
Last updated: 2026-09-22
Data flow
One boundary, and you control which side things sit on
Stays in your infrastructure
- Your documentation, chunked and embedded
- Every question a user asks
- Every answer, citation and refusal
- Conversation transcripts and session identifiers
- Analytics, including unanswered-question reports
- Nothing about visitor IP addresses: raw addresses are never stored
All of it lives in the Postgres instance you operate. We never receive it, which means there is nothing for us to leak, subpoena or retain.
Crosses the boundary
The model call
The retrieved context and the user's question go to the model provider you configured, over their API. This is the only outbound call the answering path makes.
Nothing, with a local model
Point the model interface at Ollama and the outbound call disappears. The entire loop then runs inside your network.
Provider SDKs are confined to the model-configuration module, so switching providers — or moving to a local model — changes one value, not your architecture.
Compliance
What self-hosting actually changes for a privacy review
Not legal advice — get your own. But the structural facts are worth stating plainly, because they're the reason regulated teams end up here.
GDPR
There is no processor agreement to negotiate with us and no international transfer to justify, because your data never reaches us. You stay the controller; your model provider is the only processor in the answering path, and a local model removes even that.
Data residency
Deskbell runs wherever you run Postgres. If that's Frankfurt, your conversations are in Frankfurt. Choosing an EU-hosted model provider — or a local model — keeps the entire path inside the region.
Retention
Transcript retention is configurable per project in the open core, and deletion is a database operation you control. Audit-grade logging built for EU AI Act Article 12 evidence is an enterprise feature.
Sources: EU AI Act
Threat model
A public widget is an untrusted input surface. We treat it as one.
Why answering comes before acting
Anyone on the internet can type into a widget embedded on public documentation. While the agent only reads and answers, the worst outcome of a manipulated prompt is a bad answer. The moment it can write — cancel an order, issue a refund — the worst outcome changes category entirely.
So write actions ship behind that model, never ahead of it: every tool is off until an admin chooses, a lookup can be allowed outright, a write runs only after the visitor confirms a card, the visitor’s identity reaches the server as a signed token, and every call lands in an audit log. The model was specified before the first tool was wired in. One incident would end this project’s credibility permanently.
Where the widget sits
- Rendered in a shadow DOM, so it can't read or restyle your page and your CSS can't break it.
- Authenticated with a publishable project key — the same class of credential as a publishable payments key. It grants asking, nothing else.
- No third-party bot-scoring service is loaded onto your documentation page.
- Origin binding, rate limits and a daily token budget stand between the widget and your model account. See below.
Running in public
What protects a public widget
The widget endpoints are unauthenticated by design and the project key ships in your page source. These controls are what protect you, not key secrecy. The security guide has the request path and every limit.
Origin binding
Each project carries an allowlist of origins. In production, a project with no allowed origins refuses to answer, so a forgotten setting fails closed.
Rate limits
Per IP and per project, with Retry-After. Buckets are keyed by an HMAC of the address, and the raw address is never stored.
Daily token budget
A spend ceiling per project. When it is reached the widget stops answering until the next day instead of spending your model account.
Context is data, never instructions
Retrieved chunks reach the model in a user turn; the system prompt is byte-identical whatever was retrieved. Instruction-shaped text is flagged at ingest and counted per source in the dashboard.
Not finished
Security work that isn't done
A security page that only lists strengths is not a security page. These are the known gaps as of 2026-09-22. If you’re evaluating Deskbell for anything public-facing, read this list first.
Row-level security
Tenant isolation is enforced in the query layer through scoped helpers, not yet by Postgres row-level security. The schema carries an organisation id on every row so RLS can be switched on, but that work isn't finished.
Injection resistance is measured, not guaranteed
Flagged chunks still answer: flagging is visibility, not filtering, because silently dropping a chunk deletes part of your documentation on a heuristic's say-so. Whether a payload gets through is a property of your model; the eval harness ships an injection dataset so you can measure yours.
Third-party security audit
None has been performed. There is no SOC 2 report, no penetration test and no certification, and there won't be before a stable release.
Nothing on this list is hidden — each one is tracked in the repository. If a gap here is a blocker for you, saying so is the fastest way to move it up the queue.
Disclosure
Reporting a vulnerability
Email security@deskbell.dev with the details and a reproduction if you have one. Please don't open a public issue for anything exploitable. We'll acknowledge it, work a fix in private, and credit you in the release notes unless you'd rather we didn't.
For anything non-exploitable, please open an issue instead.
Questions
Security questions
What data leaves my infrastructure?
One thing: the model call. The user's question and the retrieved documentation context go to the provider you configured, and the answer comes back. Documents, embeddings, transcripts and analytics all stay in your Postgres. With a local model through Ollama, nothing leaves at all.
Does Deskbell help with GDPR compliance?
Self-hosting removes an entire class of problem: there is no processor to contract with, no transfer mechanism to justify and no sub-processor list to track, because we never receive your data. You remain the controller, and your model provider becomes your only processor in the answering path.
What about the EU AI Act?
A support agent answering customer questions is generally limited-risk, and the practical obligations are transparency and record-keeping. Self-hosting gives you the records; audit-grade logging built for Article 12 evidence is an enterprise feature. This is not legal advice — get your own.
Can the widget be abused to spend my model budget?
Not without passing three controls. The widget endpoints are public by design and the project key is not a secret, so origin binding, per-IP and per-project rate limits, and a daily token budget are what protect you. Read the security guide before putting an instance on the internet; in production a project with no allowed origins refuses to answer.
How do you handle prompt injection?
Two layers, neither a guarantee. Retrieved context reaches the model as data in a user turn with a fixed system prompt, and instruction-shaped chunks are flagged at ingest. The answering path is read-only, so the blast radius of a successful injection is a wrong answer. Write actions stay unshipped until the permission model has real miles on it.
How do I report a vulnerability?
Email security@deskbell.dev with the details and a reproduction if you have one. Please don't open a public issue for anything exploitable. We'll acknowledge, work a fix, and credit you unless you'd rather we didn't.