Privacy model
What crosses the boundary
AskDB sends the model the question and text from your schema artifact, plus your tenant policy and the caller’s scope context when you use one. It never reads your rows, your credentials or your query results, so it can’t send them.
What the model sees
Section titled “What the model sees”When ask() calls your model provider, the request contains:
- The user’s question. As supplied to AskDB.
- Schema context. From the schema artifact: table and column names, types, primary keys and nullability, plus the text you authored, such as descriptions, aliases,
enumvalues and common query language notes. Since 1.0.0-beta.53, also each table’s business context, each column’s note, the foreign keys between tables and your concepts. For large schemas, only what retrieval selected: the tables it points to, with their columns’ notes, and the business context, concepts, example questions and tenant policy text it retrieved. - Your tenant policy and the caller’s scope, when the schema has a tenant policy. The prompt lists the tenant roots, the tenant-scoped tables and the columns that filter them, for the tables the prompt shows: a table marked
tracked: false, or one retrieval left out, isn’t named. Two exceptions keep a shown table filterable. A root whose table isn’t shown appears by the label your policy gives it, with its placeholder. And a join path that runs through a tracked table retrieval left out is still printed, with that table’s name and the columns the path uses. It names a placeholder such as:tenant_organization_idswhere the caller’s IDs go, never the IDs themselves. It also carries aglobalscope’sreasonand everything intenantScope.context(role, department, region, label, description andattributes), marked as advisory. - Hints AskDB derives from the question and the schema. For example, when the question seems to name a table the schema doesn’t have, a note says so. The hints name no table from the schema, so a table the prompt leaves out, one marked
tracked: falseor one retrieval didn’t pick, stays out of them too. - Dialect cues and output rules. The SQL flavor AskDB expects back, and the format to answer in.
That’s everything ask() sends. The model returns SQL.
The experimental chart planner, planChart() in @askdb/client/charts, makes a separate call. It sends the question, the SQL your application ran with every literal replaced by ?, and each result column’s name and kind, the row count and whether the result was cut. It sends no row (Plan a chart for a query result).
What never crosses the boundary
Section titled “What never crosses the boundary”The model never receives:
- Your database rows. AskDB does not read your data to construct prompts. A value reaches the prompt only as text: something you wrote into the schema artifact, such as an
enumlist or a description, or something the user typed in the question. - Database credentials. Connection strings, passwords, and roles live in your application’s environment.
ask()doesn’t open a database connection. A pgvector retriever you pass in connects to its own vector store, and its credentials stay out of the prompt. - Query results. Your application runs the SQL after AskDB returns it. Results stay between your app and your database.
- Tenant IDs. The prompt names a placeholder for them, and AskDB binds your IDs into the SQL after the model answers. See Tenant isolation.
- User identifiers, session tokens, request headers. AskDB doesn’t send them unless you put one in the question or in
tenantScope.context. Keep personal data out ofcontextif it mustn’t reach your model provider: alabelsuch as"Jane Smith"goes into the prompt as written. SeeTenantScope.
No middlemen
Section titled “No middlemen”Requests go straight from your environment to the model provider you wired in. AskDB does not proxy, log, or cache the traffic:
- Your applicationcalls ask()
- @askdb/corein your dependency tree
- Your DB connectionyou run the SQL
- Prompt out: question + schema context
- SQL in: checked by
@askdb/core
The model provider sees what’s in the prompt and bills you for the tokens. AskDB is in your dependency tree, not on the wire.
Large schemas and the embedding provider
Section titled “Large schemas and the embedding provider”With @askdb/rag, a second provider receives text: your embedding model. Building the index sends it every schema chunk: table and column names and types, the text you authored, concepts, example questions and tenant policy text. Each ask() call that uses retrieval sends it the question.
By default, sensitive tables and columns, and any text that names a sensitive column, stay out of the index. chunkOptions: { includeSensitiveDescribable: true } puts them in. Untracked tables (tracked: false) are never indexed. See RAG for large schemas.
Sensitive columns
Section titled “Sensitive columns”The schema artifact lets you mark tables and columns as sensitive: true, either in schema.json or in a table’s markdown front-matter (which is what Studio’s Sensitivity tab writes). Sensitivity rules says how the effective flag is worked out. It changes how AskDB constructs the prompt:
- Tagged in prompt (default). The model sees the column name with a sensitivity tag. Useful when you want the model to acknowledge the column but generate carefully.
- Omitted entirely. With
--omit-sensitive-from-prompt(ormodes.omitSensitiveFromPrompt: true), sensitive columns are removed from the prompt, and so is any text in your schema artifact that names one: a description, alias, enum value, column note, common query language note, business context or concept, or a retrieved example question or tenant policy chunk. A foreign key with a sensitive column or table on either side is left out too. A sensitive table keeps a one-line placeholder with its name, and its columns are withheld. The model can’t propose a column it never sees. One exception: when you use a tenant policy, its rules (which tables are tenant-scoped and the columns that filter them) are always in the prompt, so a sensitive column named there still reaches the model.
The setting covers every prompt AskDB sends to a language model, including Studio’s Playground, its enrichment suggestions and its tenant policy draft.
Either way, AskDB never reads the values in those columns. The flag is about the schema metadata, not the data.
Both of those are prompt-level: they change what the model sees, not what can execute. SQL that reaches your database by another route — your own SQL cache, a stored or replayed statement, an artifact regenerated with the flags reset — is unaffected. That’s why AskDB also checks SQL for sensitive tables and columns, and why the database role should leave them out: Keep sensitive columns out of prompts covers both.
Where logging lives
Section titled “Where logging lives”Audit logging is your application’s job, not AskDB’s. AskDB doesn’t log questions, generated SQL, or model responses. If you want a record of every question that was asked and every SQL string that got generated, log them in the code that wraps ask(). That keeps the log inside your stack — same place your user identities and session data already live.
Studio is the one place AskDB keeps them, on your machine: its Playground saves each question, its SQL and explanation, and any tenant scope with its tenant IDs to playground-history.json in the schema directory, and git-ignores it. See the Studio reference.
In one sentence
Section titled “In one sentence”AskDB lets you put a natural-language interface in front of your database without putting your rows, your credentials, or your results in front of a model.
© 2026 Yahya Gilany

