← Query Engine

Protection and privacy

query-engine · workzone

The security surface of the conversation layer — what Query Engine holds itself, and what it merely presents. Injection into the input was already covered by grounding (what was found is data, not commands), and injection through the tool arguments by retrieval (the identity for ACL comes from the session, not from the model's arguments, so spoofing access is impossible); rights over knowledge and authentication are held by Auth. Here are four things the conversation itself owns: access to the thread itself, the answer as an untrusted output channel, the egress of context to the model, and the traces of queries. The rest — authentication, rights, limits, model config, rendering — belongs to the neighbors; the map below separates one from another.

relies on
Authentication and rights who you are · what you see — Auth & Security Limits and cost per-user rate limit, expensive LLMs — Auth Model config and admission allow-list, sensitivity axis — Admin Answer rendering markup sanitization — the Web App shell
Conversation ownership

conversation_id addresses a thread but does not prove a right to it. Access is the owner's alone: on read, continuation (resume), and deletion, the endpoint checks the conversation owner's user_id against the current one — on top of RBAC, with the same “is it over your own object” pattern that Auth applies to personal agents and keys. The foundation is already in the model: conversations carry the owner's user_id.

The thread — for the owner only. Knowing the conversation_id grants no access: a matching user_id does. Without this check, continuing an exchange by someone else's identifier is a textbook IDOR: A would be reading B's messages.
The output is under suspicion too

Grounding closed the input — what was found is not a command. The mirror is the output: the model's answer is untrusted for the surface that renders it too. A string planted in a found fragment can make the model output a link or an image like http://attacker/?leak=…; the chat, auto-loading the resource, would drag context outside. So the answer rendering neutralizes the markup: it does not execute it, does not auto-load external resources (images, fetch), and escapes the output.

The leak channel is the render, not the prompt. The access breach is closed in SQL+ACL; injection's “teeth” are in the output. The answer is rendered by the Web App shell, which is also where sanitization and the ban on auto-loading external resources live.
Where the context goes

The user picks a chat model, and generation sends it the packed context — the found company fragments. If the model is in the cloud, those fragments leave the perimeter and go to the provider. The Admin model allow-list already narrows the choice — but by cost and availability, not by data sensitivity.

Context goes to the selected model. A cloud provider sees the found fragments in full. For now the choice is limited by the Admin allow-list. v2 — a sensitivity axis “data class → permitted model” (cloud forbidden for a closed class) and context redaction/minimization before sending — the control home is with Admin / ai-models (see open-questions).
Traces and privacy

A conversation leaves traces — the history (conversations /messages with the texts of queries and answers) and retrieval_trace (what was searched and what was found). This is company data: it lives under the same rights and cascades away when a user is deleted; we do not write the full context and secrets to logs.

Viewing traces is two different cases, not to be confused. Own history: a user browses their own conversations — what they asked and what was answered; visible to the owner only (by user_id, like thread ownership), and the screen itself is the Web App shell. Admin access to others' conversations and retrieval_trace is a rare, oversight right v2: not pursued in the first iteration.

Traces are data, not debug. Minimization in logs; own history is seen by the owner, others' — only by an admin, not by a fellow chat user. Retention and auto-cleanup are decided together with conversation management (home — the Web App shell, see open-questions); the data model is already ready for cascading deletion.

What this layer does not hold but merely respects: who the user is and what they can see — established by Auth (the surface presents a token, it does not dictate the identity), while application limits and cost (per-user rate limit, a separate bucket for expensive LLM queries) are designed there too and marked v2. Query Engine presents and honors them; it does not build the scheme.