The dashboard is the wrong artifact
Distill ships three SDKs and no UI. The query surface is MCP, which means the thing that accumulates over time is a catalog of definitions instead of a wall of charts.
Distill is an analytics backend I've been building. iOS, Android and web SDKs, one wire contract shared by all three, events batched to POST /v1/ingest. The part that makes people stop is that there is no dashboard. No charts, no screens, no analytics tab. The query surface is MCP: Claude reads the event store directly through tools.
That isn't a gap in the roadmap. It's the whole bet.
What a dashboard actually is
A dashboard is a set of questions somebody froze. Someone decided in advance which cuts matter, and those got screens: DAU, funnel, conversion, retention. Everything else got a custom-query button that nobody clicks, because it's slower than asking a colleague.
The failure isn't that the frozen questions are wrong. They're usually roughly right. The failure is that the question you actually need is almost never one of them, and the cost of asking a new one is high enough that most of the time you just don't.
Concretely: "how many exports failed today" is a chart. "Is export.fail one bug or three" is not a chart, and it's the question that decides what anyone works on next.
Tools, not screens
The query surface is four MCP tools:
| Tool | What it is |
|---|---|
distill_query_events | raw event rows, filterable |
distill_get_project_summary | rolling counters (24h / 7d / 21d) |
distill_get_stat_series | read a registered derived stat over daily buckets |
distill_create_stat_def | register a new SQL-backed derived stat |
The last one is the one that matters. A derived stat is a SELECT template with {{project_id}}, {{bucket_start}} and {{bucket_end}} substituted per bucket, returning a value plus optional JSONB dimensions.
So the agent answering your question can also durably register the question. That inverts the usual order of operations. Normally the dashboard is built once and then an analyst queries it. Here the question comes first, and the dashboard, to the extent one exists at all, is a by-product.
Two retention windows, because they do different jobs
Raw events expire at 21 days. Derived stats persist for as long as their retention_days says, up to 3650.
That split is load-bearing, and I'd defend it over the more obvious "keep everything". Raw events are for investigation. You need them fresh, complete, and greppable at the row level, and after a fortnight essentially nobody is drilling into one device's session from six weeks ago. Keeping them forever is expensive and mostly inert.
Derived stats are the opposite shape. They're small, aggregated, and the ones you want are precisely the ones you'll still want in a year.
The consequence is a discipline rather than a feature:
Anything you'd want twelve months from now has to be registered as a definition before the raw window closes. Miss it and the data is gone. No backfill recovers what expired.
That's a sharp edge and it's sharp deliberately. It forces the retention decision to be made early and explicitly, instead of being discovered later as an absence. The alternative, silently keeping everything forever, just moves the same decision to a point where nobody is thinking about it.
Privacy is architectural, not a policy page
The other structural choice, and the one that made the SDKs more work than they look.
All three SDKs strip flagged PII on the device, before anything leaves it. No ad IDs, no IP, no location, no raw prompt text. Android pulls in Room, OkHttp and WorkManager, and deliberately no Firebase, no Play Services, and no Google ad libraries. iOS has no dependencies at all beyond Apple's own frameworks. Web uses a first-party localStorage UUID: no cookies, no fingerprinting, no third-party requests.
That isn't a privacy posture bolted onto an analytics product. It's what lets an app like Pulse, which does its work entirely on the phone, say "nothing leaves your device" without its own analytics SDK being an embarrassing asterisk.
What I expect to be wrong about
The obvious objection is that dashboards exist for people who are never going to open a chat window. Someone wants a number on a screen on Monday morning, and "ask Claude" is a worse answer for that person than a chart is.
That's fair. My read is that it's a rendering problem rather than a storage problem: if the definitions exist and are queryable, a page of charts becomes a thing you generate rather than a thing you maintain. Whether that actually holds up is the part I don't know yet.
The second objection is sharper, and it's the one I'd bet on going wrong first. An agent handed both a raw-query tool and a define-a-stat tool will reach for the raw query every time. Registering a definition costs a schema negotiation and a round trip. Pulling rows and aggregating them in your head costs nothing in the moment, and answers the exact question asked. If that's how it plays out, the catalog stays empty, nothing compounds, and this is a database with extra steps.
The design only works if the ratchet works. Pulse gets instrumented next, which should settle it one way or the other.