Multi-IdP SSO resolved by hostname. Five collection permission levels, two data access levels and most-permissive-group-wins resolution. A read-only connection pool, a SQL sanitizer and an audit log that records the exact SQL executed. And a plainly worded list of the things Qrly does not do, because you are going to find out anyway and it may as well be from us.
Identity, authorisation and governance are separate systems in Qrly. Conflating them is how BI platforms end up with an admin flag that quietly means everything.
The per-organisation column is the one that matters in a multi-tenant deployment: two organisations on one installation can authenticate against two entirely different directories.
qrly_live_<32 hex>, SHA-256 at rest, shown once, per-token rate limiting, per-IP lockoutA multi-tenant installation cannot show every sign-in button to everybody. Qrly resolves the domain first and shows only the methods that domain is entitled to.
Custom domains are scoped to a tenant or to an organisation, with a primary flag, and they drive two things at once: brand resolution and which sign-in methods the login page offers. The resolved domain decides whether the page shows a password form, LDAP, Active Directory, a per-organisation Azure button, a per-organisation Google button, a global Azure button or a global Google button — narrowing to a single organisation slug when the custom domain resolves to exactly one organisation.
The practical effect: your customer visits their own hostname and sees their own logo and their own identity provider. They never see, and never have to be told to ignore, a button belonging to somebody else on the same installation.
Unauthenticated, by design: /api/auth/**, /embed/**, shared report links, /setup while the platform is uninitialised, /live, /ready, public branding, the current-domain lookup and static assets. Superuser-only: the API explorer and its spec, the platform console, system APIs, the session-token mint, tenant branding writes and superadmin.
That list is short and worth reading twice, because in most BI platforms the equivalent list is neither published nor short.
Edited as a matrix — collections down one side, groups across the top — plus per-resource views for the cases where a matrix is the wrong shape.
Collections are a materialized-path hierarchy: a child inherits its parent's permissions unless it overrides them. Every user gets a personal collection, and collections can sit outside any project, which is how organisation-wide content works.
Collection access says what content you can reach. Data access says what you can do to a connection, and it is a separate decision with three values.
Projects are a workspace above collections, grouping questions, dashboards, reports and collections, and they carry the same VIEW / EXPLORE / CURATE / MANAGE semantics. On creation, Administrators receives MANAGE and every other system group receives EXPLORE — a default that is permissive enough to be usable on day one and explicit enough to be visible in the matrix.
Permission models decide who may ask. These decide what asking is allowed to do to your database, and what evidence is left behind afterwards.
Every connection Qrly opens for a query is handed out with setReadOnly(true). All native SQL passes a sanitizer before it reaches a driver, and every parameter is bound rather than interpolated into a string. That is three independent mechanisms guarding the same boundary, which is the right number when the boundary is somebody else's production database.
The only schema write path in the product is Constructions, and it is gated by a three-tier cascade: the tenant AND the organisation AND the connection must each have ddl_enabled, all three defaulting to off. When one of them blocks, the policy service names the outermost blocking tier, so an administrator is told where to go rather than left guessing. On top of the cascade, the caller still needs FULL data access on the target. Constructions also never executes AI-authored SQL: the assistant proposes a design document, which is rebuilt field by field through the DDL generator as a trust boundary.
The query audit log stores the full SQL that was executed, together with the row count, the duration, the status — SUCCESS, ERROR or TIMEOUT — and the error message where there is one. It is browsable and filterable in the product, and retention is enforced by a daily scheduler rather than by somebody remembering to prune a table.
Recording the actual statement is the difference between an audit trail and a log. When someone asks in six months why a figure moved, "user X ran question Y" does not answer it; the verbatim SQL does. Constructions applies the same principle to schema changes: a run records the verbatim SQL, statements total and run, status, error, duration and who triggered it.
Every user has a daily query budget: a per-user override, falling back to an organisation default, falling back to 1000. It is checked and reserved atomically before every query, race-safe via an upsert keyed on user and date, with bytes returned and AI tokens recorded asynchronously afterwards. Administrators get a budget page, a per-user editor, a reset and a usage report; users see their own budget on their profile, which stops the whole thing feeling like a trap.
The query governor works on the other axis. Each connection has a semaphore sized to its maxConcurrentQueries and a FIFO wait queue capped at maxQueueSize, returning a queue-full error on overflow and timing out after queueWaitTimeoutSec. The admin page shows live per-connection statistics — active, queued, maximum concurrent, maximum queue, last wait — with a recent-events table. Nobody's ad-hoc query becomes everyone's incident.
Connection credentials are AES-encrypted at rest, including LDAP bind credentials and the extra options JSON that carries SSL settings, SSH tunnel private keys and passphrases, and BigQuery service-account keys. Debezium receives credentials injected after decryption rather than holding its own copy in configuration.
Cached query results are encrypted too, which is the part most platforms skip. Qrly's in-process JVM cache holds encrypted bytes, and the persistent tier — whether it lives in the shared application database or in a per-connection SQLite WAL file laid out by tenant and organisation — is AES-256-GCM encrypted at rest. The cache never stores plaintext rows. Cache failures always degrade to a miss rather than to an error, so the encryption boundary cannot become an availability problem.
The whole point of a security page is the list vendors leave out. Here is ours, and it is the reason the rest of this page should be believed.
Multi-factor authentication is described in the project notes, but there is no TOTP code, no filter and no endpoint in the product. Anyone telling you otherwise has read a planning document rather than the software.
What to do about it today: enforce the second factor at your identity provider. Azure AD and Google OIDC registrations are supported both platform-wide and per organisation, so the factor is applied where your other applications already apply it, by the team that already owns it. That is a legitimate architecture — it is just not the same as Qrly having MFA, and we are not going to write it on a comparison grid as though it were.
There is no policy language that filters every query for every user across the whole product. The shipped equivalents are narrower and specific: embed locked params, signed into an embed token so a browser cannot override them, and the embed-agent allow-lists covering agent type, connections, questions, tools, locked params and session caps. Between them they solve the embedded multi-tenant case properly.
Inside the application, access is governed by the collection, data and project levels described above, and OLAP models carry always-applied model filters that act as a security boundary for anything queried through the model. If your requirement is per-user row filtering across arbitrary ad-hoc SQL, Qrly does not do that, and a proof of concept will confirm what this paragraph already told you.
No SOC 2, no ISO 27001, no attestation of any kind for the software or the company. We are not going to imply one with a badge or a phrase like "built to SOC 2 principles", which means nothing and is designed to be misread.
What Qrly offers instead is self-hosting, and it is a genuinely different answer rather than a consolation prize. The software runs on your infrastructure, against your databases, inside the certification scope you already maintain and the controls your auditors already assess. There is no vendor cloud holding your rows, no sub-processor to add to a register, no data-residency question to negotiate, and no phone-home. The compliance argument is not that we passed an audit — it is that your data never left the environment your audit already covers.
A separate point, so it is not mistaken for a claim about Qrly itself: the product ships a BCBS 239 compliance module. That is a reporting and risk-aggregation capability for institutions subject to BCBS 239. It is not a certification of Qrly.
No. MFA is described in the project notes but is not implemented — there is no TOTP code, no filter and no endpoint. If you need a second factor today, put it in front of Qrly at your identity provider: Azure AD and Google OIDC registrations are supported both platform-wide and per organisation, so the factor is enforced where your other applications already enforce it. We would rather state this than let a checkbox on a comparison grid imply otherwise.
No. Row-level security and data sandboxing are not implemented as a general platform feature. The nearest shipped equivalents are embed locked params — signed into an embed token so a browser cannot override them — and the embed-agent allow-lists. Inside the application, access is governed by collection, data and project permission levels resolved most-permissive-group-wins, and OLAP models carry always-applied model filters that act as a security boundary for model-driven queries.
No. Qrly holds no compliance certification. What Qrly offers instead is self-hosting: the software runs on your infrastructure, against your databases, inside your own control and your own existing certification scope. Your auditors assess an environment you already own rather than a vendor's cloud you cannot inspect. A BCBS 239 compliance module ships in the product, but that is a reporting capability, not a certification of Qrly itself.
RESTRICTED lets a user run saved questions against a connection but not author SQL against it. FULL lets a user author and run any SQL, and it is additionally the level required for RPC calls, table writes and applying a Construction. The distinction is deliberate: giving somebody the ability to read a curated answer is a different decision from giving them the ability to write an arbitrary query.
Most permissive wins. A user in a group with EXPLORE and a group with VIEW on the same collection gets EXPLORE. A user in no groups at all resolves to NO_ACCESS rather than to a default. Unknown level strings are rejected at the boundary rather than silently ranking below no-access, so a typo in an integration fails loudly instead of quietly granting something. Resolution is memoised per request, and Administrators and superusers bypass to full access.
Not through the query path. The connection pool hands out read-only connections, all native SQL passes a sanitizer, and every parameter is bound rather than interpolated. The only write path into schema is Constructions, which requires ddl_enabled on the tenant AND the organisation AND the connection — all three default to off — plus FULL data access on the target. Table writes and RPC are separate API surfaces that also require FULL data access on the connection.
Self-hosted, so your data stays inside the environment your auditors already cover. Made in Belgium.