Open sourceApache 2.0

Python multi-tenancy

Multi-tenant ClickHouse analytics in Python, without a filter to forget

Declare the tenant column once on the dataset. Every query from your jobs, FastAPI endpoints, and agents runs inside one customer's data, and a query without a trusted tenant fails instead of returning everyone's rows.

Isolation

Fail-closed tenant key

Tenant source

Authenticated principal only

Runs in

Jobs, FastAPI, agents

One missing WHERE leaks a customer

When every query has to remember WHERE org_id = %s, the one that forgets returns another customer’s data. Code review is the only guard.

The tenant ends up in the request body

APIs that accept a tenant as a parameter let any caller ask for any tenant. The scope has to come from authentication, not input.

Each surface re-implements the rule

Background jobs, API routes, and AI agents all need the same scoping, and each one is a new place to get it wrong.

Declare it once

The dataset owns the tenant rule

Set tenant_key on the dataset and hypequery adds the tenant predicate to every query it plans. The tenant comes from a trusted execution context, never from the query itself.

  • A query without a tenant fails with tenant-required
  • Filtering on the tenant field from a query is rejected
  • analytics.for_tenant(...) binds a client to one tenant for a whole request
  • The same rule applies to relationships and joined datasets

datasets.py

Tenant key on the dataset

analytics/datasets.py

Ordinary Python you review and version with the rest of your app.

HTTP endpoints

Serve it with the tenant taken from auth

hypequery Serve resolves the tenant from the authenticated principal. No header, query parameter, or body field can set or change it.

Mark an endpoint tenant="required" to refuse callers without a tenant, or "forbidden" for internal, tenant-free routes.

A request body that tries to set a tenant is rejected with a validation error, so the scope can only narrow, never widen.

api.py

Tenant-scoped dataset endpoint

analytics/api.py

A resolver can return one tenant or none. It cannot grant several tenants or all of them.

Where teams usually get stuck

Questions teams ask

Row-level security for ClickHouse in Python

hypequery enforces tenant scope in the query it generates. You can still add ClickHouse row policies underneath as a second layer.

Multi-tenant FastAPI analytics

Dataset endpoints take the tenant from your authenticator, so every customer-facing chart is scoped the same way.

Tenant isolation for background jobs

Jobs pass a trusted tenant through ExecutionContext. A job that forgets fails loudly instead of aggregating every customer.

Same rules for AI agents

Agents connected over MCP run as one principal with one tenant, using the same datasets and the same scope.

Next step

Add a tenant key to one dataset

Define one dataset with tenant_key, run a query without a tenant, and watch it refuse.

Open source, and it runs in your own backend. Want it hosted?

Get early access to hypequery cloud.

Cloud hosts the same model you run in your backend, with a managed API and MCP server, versions, and rollback. We're letting teams in a few at a time. Join the waitlist and we'll reach out when your project can go live.

How do you want to build your analytics layer? (Optional)
What features do you most need? (Optional)

Pick as many as you like.

Only your email is required. All other questions are optional.