We added a SQL editor to Helicone called HQL. Customers get a text box, they write SELECT statements, and the queries run directly against our ClickHouse cluster. Every org's request data lives in the same table, request_response_rmt , separated only by an organization_id column.
Letting strangers run arbitrary SQL against a shared table sounds like a security incident with extra steps, so before shipping we needed the database itself to guarantee that a query from org A can never read a row belonging to org B. ClickHouse can do this with two features that don't get much attention: row policies and custom settings. Here's how we wired them together, and what the app-side code (including an AST parser) still has to do.
HQL in production. Every query on this screen runs against the same shared table.
The row policy
A row policy in ClickHouse is a filter the server appends to every read, per table, per user. The setup is two statements, straight from our migrations: create a unique user that will have the row policy, then attach the policy to that user.
CREATE USER IF NOT EXISTS hql_user; CREATE ROW POLICY hql_organization_filter ON request_response_rmt FOR SELECT USING organization_id = getSetting( 'SQL_helicone_organization_id' ) TO hql_user;
The unique user is the whole point of the first statement. hql_user exists purely to serve HQL traffic, and the policy binds to it through the TO hql_user clause. Our ingest and dashboard code connects as a different user and never sees the policy. For hql_user , every SELECT on request_response_rmt behaves as if WHERE organization_id = getSetting('SQL_helicone_organization_id') were part of the query, no matter what SQL the customer actually wrote.
getSetting is the interesting part. It reads a setting from the context of the current query. ClickHouse rejects setting names it doesn't recognize, and custom ones are only legal when they start with a declared prefix. If you self-host, you pick that prefix in the server config:
< clickhouse > < custom_settings_prefixes >SQL_</ custom_settings_prefixes > </ clickhouse >
On ClickHouse Cloud, where our cluster runs, there is no picking. You can't touch server config, and the ClickHouse docs state that "it is not possible to specify a custom prefix". Every custom setting begins with SQL_ , no exceptions. So ours is called SQL_helicone_organization_id instead of something cleaner, and that awkward prefix is load-bearing rather than a naming choice. You'll notice there's no custom_settings_prefixes entry anywhere in our repo; on Cloud there's nothing to configure.
... continue reading