---
title: The security model
description: What Dashies can and cannot see, who can read a published dashboard, and the controls that keep one organization's identity separate from another's.
updated: 2026-09-21
---

These pages explain how Dashies handles your data and your credentials. They
contain no steps. Where an action is involved, they link to the page that walks
you through it.

Start with the property that shapes everything else.

## Dashies holds a copy of your numbers

Most BI tools re-run your query every time somebody opens a view. Dashies does
not. **Your query runs on a schedule and its results are stored**, every view is
answered out of that stored copy, and a scheduled refresh replaces the copy
without any AI in the loop.

That is what makes a Dashies dashboard fast, shareable by link, and able to keep
working when your warehouse is busy. It also means the honest statement is:
**the results of your queries are stored on our infrastructure**, not just
streamed through it.

What that copy contains follows from the shape your statement takes, and the two
shapes expose different things.

**Against a warehouse connection or the workspace's uploaded-file source**, the
statement returns one row per underlying record, carrying the columns each number
is worked out from, and Dashies works the numbers out when a reader opens the
page. So what is stored is those records. **There is no aggregating away here**:
what you control is which rows the statement returns, which columns it outputs,
and how coarse the values in them are.

**Against the built-in `self` connection**, the statement groups and aggregates
and those rows ship inside the published file, so what is stored is the summary
you wrote. A statement that aggregates to `region` and `month` stores regional
monthly totals and nothing else, while a `rows` dataset ships row-level records,
because that is what it is for.

Either way, **what the statement outputs is a data-exposure decision as well as a
correctness one**, which is the part people miss. See
[Designing the cube](/concepts/designing-the-cube) for which shape you write, and
[Datasets and the four modes](/concepts/dataset-modes) for what each in-file mode
carries.

Your warehouse credential is handled completely differently from your data, and
that difference is the subject of
[Warehouse credentials](/trust/warehouse-credentials).

## What the authoring AI sees

Your own AI tool writes the dashboard, so it is worth being precise about what it
receives. During authoring it can see:

- **Your schema.** Table and column names and types, for the tables it
  introspects, plus an approximate row count on a warehouse table.
- **A sample of real results.** When it validates cube SQL before publishing,
  Dashies runs the query and echoes back the first rows so the AI can check the
  shape. That echo is capped at **200 rows and roughly 8 KiB**, whichever binds
  first.
- **The dashboard's own contents**, because it wrote them.

What it **never** receives, at any point:

- **Your warehouse credential.** No publishing tool takes a password, key, or
  token as a parameter, and the service that serves those tools has no ability to
  read stored credentials at all. The tool that lists your data sources returns
  ten descriptive fields: `id`, `kind`, `label`, `engine`, `status`,
  `last_verified_at`, `last_error`, and a `health_state` / `health_error` /
  `health_source` trio. No credential, and no host.
- **Anything from another account.** Every tool acts as the account that
  authorized it.

And after publishing, the AI is out of the loop entirely: scheduled refreshes
re-run your SQL server-side with no model involved.

:::note{title="The practical consequence"}
If your organization's rule is "no production data through an AI tool", the
surface to think about is the **validate step's sample rows**, not the refresh.
Validating against a table of real customer records means a few of those records
reach your AI tool the same way any other output would.

**The remedy that works on either shape is to keep the sensitive column out of
the statement's output**, or to coarsen the values you do output: a month rather
than a timestamp, a region rather than a postcode. Against the built-in `self`
connection you can also aggregate it away, because there the statement groups and
aggregates anyway. **Do not reach for that against a warehouse connection or the
workspace's uploaded-file source**: a statement that has already aggregated is the
wrong shape there, Dashies aggregates it a second time, and a count comes back as
the number of groups you made. See
[Designing the cube](/concepts/designing-the-cube).
:::

## Who can read a published dashboard

**There is no public dashboard, and no anonymous viewing.** Every viewer holds a
session.

- A dashboard is readable by the current members of the workspace it lives in,
  and by nobody else.
- A **signed-out** visitor is redirected to sign in, carrying the dashboard URL as
  the return path. Nothing is served anonymously, including to a link-preview
  crawler, so a pasted link does not unfurl anywhere.

A dashboard requested by a signed-in person who may not see it returns a **404
that is byte for byte identical** to the 404 for a URL that never existed. The
check happens before any redirect, so guessing at URLs cannot reveal which of your
dashboards exist. The page says only "This dashboard is private or no longer
available", which covers all three cases on purpose. A signed-OUT request is
answered by the sign-in redirect before anything is looked up at all, so it is
identical for a real dashboard and a made-up one.

When a dashboard's data is too large to inline, it is served as a separate file
behind a **signed, short-lived link** valid for four hours. That
link is only ever minted while serving the dashboard to somebody already
authorized to see it, so there is no way to obtain one without the access it
represents.

Dashies never returns another user's account identifier for a dashboard you can
see: the serving path computes only whether **you** are the owner.

## The domain bind

The single most important control in the enterprise surface, and the reason
[domain verification](/admin/domain-verification) exists.

A workspace can create sign-in sessions and add members **only for email
addresses on the domain it has proven it controls**. Without that rule, an
administrator of any workspace could type in another company's domain and start
creating Dashies sessions for that company's employees.

It is enforced in four independent places, so that removing any one of them does
not open the door:

- On sign-in started from Dashies, the asserted address must end with the
  workspace's verified domain.
- On sign-in started from your identity provider, the same check runs again.
- On every directory-sync event, the same check runs a third time.
- In the database itself, changing a workspace's email domain **automatically
  clears its verification**, and the verification field cannot be written by the
  application's ordinary user-facing permissions at all. Only the server-side
  path that has actually completed the DNS check can set it.

That last layer is what makes the first three hard to bypass: even a bug in the
web application cannot mark a domain verified.

A domain can be verified by exactly one workspace at a time. Two workspaces may
both name it, but only one can prove it.

## The rest of this section

- **[Warehouse credentials](/trust/warehouse-credentials)** - where the
  credential you enter is stored, what can read it, and what "read-only" is
  actually enforced by on each engine.
- **[Dashboard isolation](/trust/dashboard-isolation)** - why a published
  dashboard cannot reach your Dashies session, and what that costs.
- **[Privacy and data handling](/trust/privacy)** - what we collect, analytics,
  and deletion.

## Reporting a vulnerability

Security reports are welcome. We do not pay bounties, and we would rather say
that up front than have you find out afterwards.

Report **privately, by email, with `SECURITY` in the subject line**, to the
contact address on [dashies.ai](https://dashies.ai). Please do not open a
public issue or a support ticket for a suspected vulnerability.

**Ask us for the rules of engagement in your first email, before you test.** They
set out what is in scope, the constraints on how you may test, and the safe
harbor that covers research conducted within them. We will send them straight
back. Do not assume a protection applies to testing you have already done: this
page is not that document, and we would rather answer the question than have you
rely on a summary of it.

Two things to get right in a first email: include reproduction steps from a
clean account, and **do not send live secrets or anyone else's data**. Send the
smallest redacted proof that shows the behaviour.

What we most want to hear about, roughly in order: cross-tenant access, bypassing
the workspace gate or using a dashboard/data capability outside the scope or lifetime
it was issued for, dashboard content escaping its isolation, warehouse credential
exposure, reaching an unintended host through a warehouse connection, and anything
that writes to a connected warehouse.

Denial of service is in scope but **do not test for it**. If you find one
through safe analysis, tell us how you found it without exercising it.
