Open-source subscription analytics

Subscription metrics,
open by inspection.

Tidemill computes MRR, churn, retention, LTV — and ships the SQL behind every number. Self-host alongside Stripe and your accounting platform. Audit any metric down to the row.

Metrics live
6
Connectors
2
License
MIT

MRR Waterfall · Nov 2025 → Apr 2026

live demo data
$150k $100k $50k $0 $147.6k Nov '25 Dec '25 Jan '26 Feb '26 Mar '26 Apr '26
01 — Capabilities

Three ideas, executed precisely.

A tight surface area, built so every part is legible. The product is the SQL, the schema, and the events — not a black box.

A · TRANSPARENT BY CONSTRUCTION

Every metric is a class. Read the formula, read the SQL.

Each metric (MRR, churn, retention, LTV…) lives in a self-contained Python module with its formula, SQL, edge cases, and API in one place. Compiled SQL travels through a typed query algebra — never string concatenation.

# tidemill/metrics/mrr/metric.py
@register
class MRR(Metric):
    """Active recurring revenue at time t."""
    def query(self, spec) -> Select:
        ...
B · PLUGGABLE CONNECTORS

Webhook or direct database.

Stripe (webhooks → Kafka → PostgreSQL) is the revenue pipeline. QuickBooks Online feeds expense data. Same-database mode supports billing engines that expose a PostgreSQL. Add a new connector in < 200 LOC.

stripe webhook quickbooks webhook + your own translate() + register()
C · YOURS TO RUN

One docker compose up.

PostgreSQL + Redpanda + FastAPI + a React dashboard, behind Caddy with auto-TLS. Deploys on a single €4/mo Hetzner box, or a k3s HA cluster. Your data stays on your infrastructure.

$ docker compose up -d ✓ postgres healthy ✓ redpanda healthy ✓ api listening :8000 ✓ caddy tls ready
02 — Metrics

Every metric, with its formula.

Six metric families ship today. Each one is a separate module with its own tables, event handlers, and API routes — adding a new metric requires zero changes to existing code.

01
MRR & ARR recurring revenue
Σ subscription.mrr_cents WHERE active AT t
totalwaterfalltime seriesby plan
02
Churn logo · revenue · net
churned_mrr / starting_mrr
logo raterevenue ratenetby cohort
03
Retention NRR · GRR
cohort_mrr(t) / cohort_mrr(start)
NRRGRRcohort matrix
04
LTV lifetime value
ARPU / monthly_churn_rate
totalARPUby cohort
05
Trials conversion · funnel
converted / started
funnelmonthlyby source
06
Expenses from accounting
Σ bill.total_base_cents per period
by vendorby account_typetimeline

CAC, warehouse export, and additional connectors are next. Roadmap on GitHub →

03 — Read the code

Class SQL API.
Nothing in between.

A Python class declares the metric. A typed query algebra compiles it to SQL. FastAPI exposes the result. Same chain for every metric — including the ones you write.

Python tidemill/metrics/mrr/metric.py
@register
class MRR(Metric):
    """Active recurring revenue at t."""

    async def query(
        self, spec: QuerySpec
    ) -> Select:
        c = self.cube
        return spec.fragment(
            select=[
              c.sub.mrr_base_cents
               .sum().label("mrr"),
            ],
            where=[c.sub.is_active_at(spec.at)],
        )
SQL compiled by query algebra
-- generated, never concatenated
SELECT SUM(s.mrr_base_cents) AS mrr
FROM   subscription s
WHERE  s.status IN ('active',
                       'past_due')
  AND  s.started_at <= '2026-04-30'
  AND  (s.ended_at IS NULL
       OR s.ended_at > '2026-04-30');
HTTP FastAPI route
$ curl https://tidemill.xyz/api/\
metrics/mrr?at=2026-04-30

// response
{
  "metric": "mrr",
  "at":     "2026-04-30",
  "value":  13720000,
  "currency": "USD"
}
04 — How it runs

Two modes. Same metrics.

Webhook-style billing systems get an event pipeline. Database-accessible ones get queried in place. The metric code doesn't care.

Mode A · Ingestion

Stripe & QuickBooks · webhook → events → tables

Webhooks are translated into typed internal events, fanned through Kafka, and materialized into analytics-owned PostgreSQL by per-metric handlers. Replayable, observable, idempotent.

SOURCE Stripe CONNECTOR translate() BUS Kafka METRICS handlers STORE PostgreSQL QUERY FastAPI / CLI
Mode B · Same-database

Direct query · read the billing PostgreSQL

For billing engines that already own a PostgreSQL, Tidemill connects directly. No Kafka, no ETL, no duplication. The same metric module — with a different cube definition — reads native tables.

BILLING ENGINE PostgreSQL native tables, untouched TIDEMILL FastAPI + metrics read-only, no copy SELECT … rows →
05 — Why open

Closed analytics is a black box. You shouldn't have to trust it.

01

Definitions aren't standardized.

MRR, churn, LTV — every closed tool computes them differently, and none show their work. When your numbers don't reconcile with the spreadsheet, you have no recourse. Tidemill ships the SQL.

02

Billing is getting weirder.

Hybrid pricing, usage-based, prepaid credits — 85% of vendors will use them by 2026. Generic formulas can't cope. Open code can. Fork a metric, change the SQL, run it.

03

Your stack, your servers.

Run Tidemill on infrastructure you own. Customer revenue data stays inside your network — no third-party SaaS analytics tool sitting between you and your numbers.

06 — Try it

See the engine, running on real data.

The live app at app.tidemill.xyz runs on seeded Stripe data over an 18-month window. Every chart is rendered by the same metrics engine you can clone and run on your own infra in minutes.

Local quick start
git clone & make seed
Production deploy
1× Hetzner box · €4/month · Docker Compose + Caddy
Stack
PostgreSQL · Kafka/Redpanda · FastAPI · React
License
MIT — fork, modify, redistribute