01Product
Define it once. Query it from anywhere.
Each metric is one YAML definition. Every request for it becomes the same correct SQL, with the joins found and the totals right.
entities:
- name: Order
table: public.orders
primary_key: id
dimensions:
- name: region
type: string
measures:
- name: revenue
expr: amount
agg: sum
- name: order_count
expr: id
agg: count
metrics:
- name: avg_order_value
expr: revenue / order_count
format: currency{ "metrics": ["avg_order_value"],
"dimensions": ["region"] }SELECT o.region,
SUM(o.amount) / COUNT(o.id)
AS avg_order_value
FROM public.orders o
GROUP BY o.region;| region | avg_order_value |
|---|---|
| EMEA | 118.40 |
| NA | 131.52 |
| APAC | 104.77 |
How it works
Four steps from warehouse to answer
The same four steps whether you self-host or we host.
- 01
Run a Query Runner
Install it next to your warehouse with a read-only role. It keeps the password and the audit log.
$ query-runner -config runner.yaml ✓ listening :8787 ✓ warehouse reachable (read-only role)
- 03
Publish a version
Publishing freezes the model and its access rules together. Every change ships as the next version.
✓ published v7 model + access rules, frozen together # example
- 04
Hand out access
Apps get keys and agents sign in with OAuth. Each one sees only the metrics you grant.
app-finance key governed 4 metrics agent-sales OAuth governed 2 metrics
The compiler
You never write the join
Describe how your tables relate, once. datahashi picks the join path and sums before it joins, so a one-to-many join can't inflate a total.
Join path resolved from the model. Totals are summed before the join can repeat a row.
As you grow
What stays true at a hundred metrics
Access modes
Narrow for agents, wide for analysts
Governed keys see named metrics only. Explore keys add raw fields for hands-on work. The mode is fixed per key.
named metrics · curated views · the default for agents
raw measures × dimensions · hands-on analysis
- ✓Ask for metrics · avg_order_value, revenue
- ✓Query a curated view · finance_summary
- ✗Ask for raw measures · 403 forbidden
- ✗Read raw fields · not exposed to this key
Portability
Keep the warehouse you have
Requests never name a warehouse, so you can move later without touching a caller.
- Snowflake
- BigQuery
- Postgres
- MySQL
Rollups precompute your busiest questions so they skip the warehouse.
Ownership
Every metric has an owner
A definition names the team that owns it. Changes ship as new versions, and a caller can pin the one it trusts.
# model.yaml
- name: Order
owner: "Finance Analytics"
# a caller pins the version it trusts
{ "metrics": ["avg_order_value"],
"version": "7" }Next: how agents ask
An agent sends the same request a dashboard does.