Two dashboards can look identical and still run on different row-level security models. One filters what's displayed. The other filters what's ever returned. Only one of those is actually safe to share.
Row-level security: "hidden" vs. "never returned"
App-layer filtering fetches the full result set from the warehouse. Then it hides the rows a viewer shouldn't see, inside the interface. The data still left the database. It still reached the application. An exported CSV can expose all of it. So can a browser's network tab, or a direct API call.
Query-time enforcement compiles the restriction into the query itself. The excluded rows are never executed against. They're never returned, either. The difference isn't visible in the dashboard. It only shows up the moment someone tries to get the data out a different way.
Filtered in the app
Full result set is fetched, then rows are hidden in the UI.
Exports and API calls often bypass the UI's filtering logic.
Scheduled reports can carry the unfiltered query.
Enforced at query time
The restriction is compiled into the query before it runs.
Excluded rows never leave the warehouse, on any code path.
Exports, APIs and scheduled reports inherit the same rule.
Where this actually breaks in practice
The dashboard is rarely where app-layer filtering fails. The UI code path is usually the one that got tested. It's the four exits around it that break. A CSV export. A scheduled email report. A direct API request. An embedded dashboard shared with an external client. Each is often built on a different code path than the one the filtering logic was written for. Each is a common blind spot, where "we filter in the dashboard" quietly stops being true.
A common pattern: the dashboard team ships a filter clause in the front-end query builder. They test it against the UI. They ship. The export button calls a separate endpoint. That endpoint pulls from the same underlying query — without the filter clause. Nobody wrote a second filter. Nobody thought of the export as a second code path. It is. The gap usually surfaces months later, in an audit — not in a demo.
What to test, not just ask
- Can a user with restricted access export a CSV that contains rows they can't see on screen?
- Can the same user request the underlying report directly by ID or API, bypassing the dashboard entirely?
- Does a scheduled report sent to a distribution list respect the same restrictions as a logged-in session?
- Does a permission change take effect immediately on every one of those paths, or only in the dashboard?
"We filter in the dashboard" is not a complete answer to any of these. Each is a five-minute test, not a policy review. If the answer to any of them is "we're not sure," that's the finding.
Column-level matters too
Row-level security answers "which records." It doesn't answer "which fields." A viewer can be entitled to every row in a table. They can still need one column masked — a salary figure, a margin, a client's identity behind an otherwise-shareable dataset. The dataset itself can be entirely fine to share. It's the one number inside it that isn't.
A sales dashboard can be scoped correctly by row and still leak margin, if the margin column isn't masked for reps who shouldn't see it. The row-level rule was right. The column was never checked. Real permission-aware reporting checks both: which rows a query is allowed to return, and which columns within those rows.
How Veritly enforces this
Row-level security and column-level masking are compiled into the query before it reaches the warehouse. A shared view, an export, or a scheduled report can't expose data the viewer was never cleared for. The same rule applies everywhere in the workspace, not just in the dashboard.
That's possible because every query resolves through one semantic model. It's defined once, and read everywhere — rather than re-implemented per export path. None of it depends on a dashboard remembering to ask. Every scheduled run leaves a trail. It's clear what was returned to whom, and when.
Join the private beta waitlist — early access includes a setup session with the team that built it.
Related reading: how Veritly's security model works, and the professional services reporting stack.
See also: UK GDPR data residency and the semantic layer.



