architecture · access · isolation
Security
You are being asked to hand over credentials to the database your business runs on. This page is what we do with them, in enough detail to check.
The path a question takes
There is exactly one route from a question to your data, and it is narrow by construction.
The agent cannot run a command, read a file, or open a socket. Its entire tool surface is the set we expose to it, and shell access is not in that set — not disabled by policy, not guarded by a prompt, simply not present.
Read-only, proven when you attach
Most tools check permissions by asking politely and believing the answer. We treat grant
text as a claim to be tested: each driver reads the engine's own authority on effective
privilege — has_table_privilege on Postgres, SHOW GRANTS on MySQL
and Snowflake, testIamPermissions on BigQuery — and runs a probe that writes
nothing but makes the server reveal what it would have allowed. If either says the credentials
can write, the source does not activate.
The SQL guard stays in place regardless, as the second line: every statement is parsed in
your engine's dialect and rejected unless it is a single read-only SELECT, with
a per-engine table of functions that read the filesystem or reach the network blocked by
name. But a guard is code, and code has bugs. On a read-only account the worst case of a
guard bug is a failed query; on a writable account it is your data. That asymmetry is why
the credential check exists at all.
where this is honest
The gate is acknowledgeable, not absolute. An admin can accept write-capable credentials for a single source — recorded with who, when and why — because a hard refusal has its own failure mode: an organisation whose DBA has not yet created a reader cannot onboard at all, and the pressure that creates is to switch the check off everywhere. When it is accepted, the source's badge keeps saying read-only unverified, on the card, in the API, and in what the agent is told. It changes what we allow. It never changes what we claim.
Credentials
- Entered in an admin-only form and encrypted at rest. They are never typed into chat and never appear in a conversation.
- The model never sees a value. When a connector needs a credential the
agent declares the field name —
AWS_SECRET_ACCESS_KEY— and the value goes from your browser to the vault without passing through it. - Driver and connection errors are scrubbed of secret values before they can reach a log, a transcript, or your screen.
- For SSH tunnels we generate the keypair and give you only the public half, with a ready-to-install line that restricts it to forwarding to that one database and nothing else. A key you paste has already been through a clipboard, a browser and a form, and is usually a key that opens other things — so we would rather not be holding it.
Tenant isolation
Every row belonging to a workspace carries its workspace id, and that is enforced twice: in every query the application makes, and again by row-level security in Postgres, which defaults to denying. The second one exists because the first one is a discipline, and disciplines have off-days.
Retrieval is scoped the same way. Past conversations are searched within your workspace only — a leak there would put one company's history into another company's prompt, which is the single most dangerous path in a system like this, so it gets the same treatment as the data itself.
Generated connectors run sandboxed
When the agent writes a connector for an API, that code is untrusted by default. It is validated inside a container with a read-only root filesystem, dropped capabilities, no privilege escalation, memory and CPU and process limits, a hard timeout, and no network at all unless the connector declared it needs one. Credentials are injected as environment variables at run time and are not written to the image.
A connector is registered only if it passes the gate — test() reporting ok and
discover_schema() returning fields. One that fails is stored as broken, with the
error, rather than left half-installed.
where this is honest
A connector that has been granted network access can currently reach any host, not just the API it was written for — per-host egress allow-listing is built but not yet enforced. Until it is, granting network to generated code is a decision worth making deliberately, and the plugin factory stays limited to workspaces we have talked to rather than being open to every new sign-up.
What we store, and for how long
- Your rows stay in your database. We hold query results long enough to answer the question, plus the snapshots behind any dashboard you pin — that is the data that makes a saved chart render without re-querying.
- Conversations, saved charts, semantic models and audit records live in our control plane.
- Not used to train models. Inference runs through Anthropic's API under commercial terms that exclude training on inputs and outputs.
- Deleting your workspace deletes its sources, credentials, conversations and snapshots.
Subprocessors
The current list, with what each one can see, is on the subprocessors page and changes there first.
What we don't claim
We are pre-certification. There is no SOC 2 report, no ISO certificate, and no compliance badge on this site, because we do not have one and a logo is not a control. What we have is the list above, which you can verify against the product during a trial: attach a source with write permissions and watch it refuse to activate.
Found something wrong? security@evalyst.ai — we will answer, and we will not argue about whether a report was in scope.