Skip to content

Warehouse credentials

Where the credential you enter is stored, what can read it, and exactly how much of read-only Dashies enforces on each warehouse engine.

Connecting a warehouse hands Dashies a credential that reaches your own data. It is the most sensitive thing you give us, and it is handled differently from everything else.

The path a credential takes

You type it into the connect form in the Dashies web app. That is the only way in.

From there it goes straight to storage:

  • It is posted over HTTPS from the form to a Dashies server route.
  • It is written into Supabase Vault, encrypted at rest.
  • The connection's own record stores a reference to that vault entry, never the credential itself. Everything visible on the record is non-secret metadata: a label, the engine, a host, a status.

Nothing later reads it back out to a person or a client. There is no screen that displays a stored credential, no API that returns one, and no support flow that recovers one. Changing it means replacing it.

Your AI tool never receives it

This is worth stating carefully because it is the question people actually have.

No publishing tool takes a credential as a parameter. Not a password, not a key, not a token. The tool that lists your data sources returns ten fields, and they are all descriptive: id, kind, label, engine, status, last_verified_at, last_error, health_state, health_error, and health_source. No password, no secret, no host, and no internal database detail.

And the publishing service could not read one if it tried. That is the part worth stating carefully, because "we do not do that" is a weaker promise than "the system is not built so that we could".

Two independent things stand in the way, and both would have to change together:

  • The service your AI tool connects to holds no administrative database key. It is not that the key is unused there; the configuration that would carry it does not exist on that service. It authenticates as you, with your own permissions.
  • Every routine that decrypts a stored credential is executable only by a server-side administrative role, and is explicitly revoked from the ordinary signed-in role. So even a service holding your session could not call one.

The first of those is enforced by the build rather than by review: code in the publishing service that tries to reach for an administrative key fails to compile, and that check runs on every change before it can ship. We verified this by making the change deliberately and confirming the build rejects it.

The credential path and the authoring path are therefore separate systems that meet only at a connection id. Your AI passes the id; the server does the rest.

What reads it, and what is recorded

Only server-side routines, and only in three situations: testing a connection when you click Test, introspecting or validating while you author, and running a scheduled refresh.

Where the credential has to be pulled out of storage into a server process to call the vendor's API, that read writes a row to the connection's own append-only event log. Append-only here means enforced by the database rather than by convention: updates, deletes, and truncation of that log are rejected at the storage layer.

PostgreSQL and SQL Server work differently, and better. On those two the credential is used inside the database, by the confined routine that runs your query, and is never materialized in a server process at all. So there is no extraction event to log on that path, because nothing was extracted. Do not read an empty log there as the absence of a check; read it as the absence of a handover. The Postgres extract path, which does pull a connection string out, does write its own entry.

Errors never carry your credential or your host

When a warehouse connection fails, the underlying driver's error typically contains the host it dialled, the resolved address, the port, and the user it tried. None of that is passed on.

The server reads the driver's detail only far enough to classify the failure into a fixed vocabulary, then discards it and raises only the class. That is why a failure in the app reads as something like "the credential was rejected" rather than quoting your database's message, and why the run history stores a category rather than a sentence. The one exception is deliberate: an error in your own cube SQL is returned verbatim, because that text is yours and you need it.

How much of read-only is actually enforced

This is the part where the honest answer differs per engine, and where a general reassurance would be misleading.

Grant a read-only credential on every engine. How much Dashies adds on top of it varies, and on two engines it adds very little, so the credential is the one control that is doing work no matter which engine you picked.

The cube must be a read-only query, but the mechanism is not the same everywhere

One property holds on all six engines: your SQL is rejected unless it begins with select or with. That is the whole of the shared floor, and the two checks people assume sit beside it each have an exception.

Bounding the query to a single statement holds on five. It does not hold on SQL Server, where the statement terminator is optional, so no check on the text can reliably tell how many statements a query contains. That is not an oversight, and it is the specific reason SQL Server is the one engine where a read-only login is required and verified rather than recommended.

Stripping comments and string literals before checking holds on five, so a keyword cannot hide inside one. What hides a keyword differs per dialect: dollar-quoting on Redshift, raw string prefixes and backtick identifiers on Databricks, bracketed identifiers on SQL Server. PostgreSQL does not strip; it refuses. Anything containing a ; or a /* is rejected outright and the leading keyword is matched against the raw text. That is more conservative than stripping, not less: there is no stripper to outwit.

Above that floor the engines diverge further, and the difference is worth knowing rather than smoothing over:

EngineWhat else stands between your cube and a write
PostgreSQLThe query runs inside a read-only transaction. The database refuses a write itself, whatever the text says.
Redshift, Databricks, SQL ServerWrite and DDL verbs are additionally rejected as whole words, including into, because select ... into creates a table on these engines and would otherwise pass a leading-select check.
BigQuery, SnowflakeThe shared floor only. There is no additional keyword reject, so the privileges on your credential are what stands between a cube and a write.

PostgreSQL's read-only transaction is the strongest of the three approaches, because it is the database enforcing it rather than a check on the query text. BigQuery and Snowflake have the weakest, which is the practical reason the advice below is not optional on those two.

PostgreSQL: structural confinement, plus the role you create

Each connection gets its own private schema and its own no-login, no-inherit database role with no memberships. The routine that runs your cube SQL is owned by that role and runs as it, natively, with no privilege switch to undo. It refuses to run at all if it is ever re-owned, and attempting to switch roles from inside cube SQL is rejected by the database.

References to system catalogues are also blocked, so one tenant's query cannot enumerate another's connection topology.

What Dashies does not do here: it does not check that the login you supplied is read-only. The grant select-only role you create is doing real work. The connect form gives you the exact SQL for it. See Connect PostgreSQL.

SQL Server: a read-only login is required and verified

The only engine where Dashies refuses your credential outright. On the credential axis this is the strongest of the six; PostgreSQL is the strongest on the query axis, above. They are different guarantees, not a ranking.

When you test the connection, Dashies probes the login's actual privileges and refuses it if it holds any write or administrative privilege, with readonly_login_required. A server administrator account is rejected, not warned about.

It is required rather than recommended because of the gap noted above: with an optional statement terminator, no check on the query text can reliably bound how many statements it contains. So on this engine the read-only login is the boundary, and the SQL check is only defense in depth. See Connect SQL Server.

Transport is also fixed rather than configurable: encryption is always on and server certificate validation is never disabled.

BigQuery, Snowflake, Redshift, and Databricks: your grant is the boundary

These four are reached over the vendor's own HTTPS API rather than a database connection, so there is no schema, role, or read-only transaction for Dashies to confine your query inside. Each still gets its row in the table above, and Redshift and Databricks get the extra keyword reject, but on all four the privileges attached to the credential are what actually limits it.

Concretely, that means a service account, key pair, IAM identity, or service principal with more than read access has more than read access, and Dashies will not stop it. Each engine's guide gives the minimum grant. Follow it.

Reaching your network

Dashies connects outward to the warehouse you name; nothing connects inward to you.

For engines where you supply a host, the address is validated before anything is dialled, and private and internal address ranges are refused, including addresses written in unusual numeric forms. Which ports are permitted is fixed per engine. IP allow-listing is not supported, so your warehouse has to be reachable from the public internet.

For BigQuery, Snowflake, and Redshift you never supply a host at all: it is derived server-side from the account or region identifier, so there is nothing there to point somewhere else. Databricks takes a workspace host, constrained to Databricks' own domains.

What this does not protect you from

Stated plainly, because a security page that only lists strengths is not useful.

  • An over-privileged credential. SQL Server is the only engine where Dashies checks whether the login you supplied is read-only. Everywhere else, including PostgreSQL, a credential with write access has write access, and the read-only SQL check is the only thing standing in front of it. That is your decision, not ours.
  • Data you choose to expose. A cube that selects raw personal data ships raw personal data into the dashboard. See dashboard isolation for who can then read it.
  • Your warehouse's own access controls. Dashies reads with the identity you gave it. If that identity can see a table, so can a dashboard built on it.