Skip to content

In-house product

Ledgerline

Multi-tenant SaaS

Built to answer the question every SaaS founder eventually gets asked in a security review: how do you prove one customer's data cannot reach another's?

Year
2026
Engagement
Ongoing

What it had to do.

A multi-tenant analytics product where each customer organisation has its own users, roles and data, and is billed on usage. We run it ourselves, which is why it is the case we can show in full.

The constraint

Isolation has to survive a mistake. Any design where a forgotten `where` clause leaks another tenant's rows is a design that will eventually leak another tenant's rows.

Architecture

How it is put together.

Application

  • DashboardNext.js, server components
  • Public APIscoped tokens

Access

  • Tenant contextset per connection, fails closed
  • Role hierarchyorg / team / user

Data

  • PostgreSQL + RLSpolicies on every table
  • ClickHousederived, rebuildable
  • Event logappend-only

Async

  • BullMQ workersingest, rollups
  • Meteringusage → billing
Isolation is enforced by PostgreSQL policies, one layer below the code that could forget it.

Stack

  • Next.js
  • PostgreSQL RLS
  • Drizzle
  • ClickHouse
  • BullMQ
  • Stripe Billing

Every choice cost something.

An architecture without trade-offs is an architecture nobody has stress-tested. Here is what we gave up for what we got.

Isolation lives in the database, not the ORM

What we did

PostgreSQL row-level security, with the tenant set as a session variable on every connection.

Why

An ORM helper can be bypassed by the next developer writing a raw query at 6pm. A database policy cannot. It is also the answer a security reviewer wants to hear.

What it cost

Every connection must set the tenant, and connection pooling makes that easy to get wrong. All queries route through a wrapper that refuses to run when the tenant is unset, failing closed, loudly, in development.

Analytics data is derived and disposable

What we did

Transactional data in PostgreSQL; aggregates in ClickHouse, rebuildable from an append-only event log.

Why

Analytical queries and transactional writes want opposite things from a database. Separating them keeps both fast and makes the analytical store safe to rebuild.

What it cost

Two stores to operate and a rebuild that takes real time on a large tenant. Accepted because the alternative, analytical scans against the primary, degrades the product for everyone.

Billing is computed, never accumulated

What we did

Usage is written to an append-only ledger; invoices are derived from it at billing time.

Why

A running total is one bad deploy away from being wrong with no way to check. A ledger can be recomputed and reconciled line by line.

What it cost

More storage and a slower invoice calculation. Worth it the first time a customer disputes a bill and the answer takes minutes instead of an apology.

How it was built.

  1. Weeks 1-3

    Tenancy foundation

    Schema, RLS policies, role model and the failing-closed connection wrapper, with isolation tests written first.

  2. Weeks 4-8

    Ingest and rollups

    Event ingestion, queue workers, ClickHouse schema and the rebuild path from the event log.

  3. Weeks 9-12

    Product surface

    Dashboards, saved views, sharing, invitations and the permission checks behind each of them.

  4. Ongoing

    Billing and operations

    Metering, Stripe integration, dunning, and the operational tooling that turns support questions into self-service.

What is true of it.

Properties of the build we can demonstrate, not business results we did not measure.

  • Cross-tenant reads are blocked by database policy; the isolation test suite asserts this on every table, on every CI run.

  • A query issued without a tenant context raises rather than returning rows.

  • The analytics store can be dropped and rebuilt from the event log without touching customer-visible state.

  • Any invoice can be reconstructed from the usage ledger, line by line.

Next step

Tell us what needs building.

A 30-minute call, under mutual NDA if you want one signed first. You leave with a view on scope, team shape and budget range, whether or not you work with us.

Or write to hello@warmbench.dev