Zorto now ships a public DuckDB database with the site.

/data/site.ddb is generated from local repo and build metadata. Search reads from it. The analytics dashboard reads from it. Static hosting serves it as a file; the browser attaches it read-only when a page needs data.

No visitor analytics. No tracking, cookies, tokens, or third-party event stream. The database contains site metadata: commits, packages, content files, links, build outputs, search rows, and pipeline receipts.

BuildRust CLI renders the site
Generateuv script writes site.ddb
Shipstatic hosting serves HTML and data
QueryDuckDB-Wasm runs in the browser

Local pipeline. Static artifact. Browser query.

Current slice #

📂website/
├── 📝bin/build-metauv script
├── 📝data/meta.tomlpipeline config
├── 📝data/analytics.tomldashboard config
├── 📝static/data/site.ddbpublic DuckDB file
├── 📝static/data/analytics-dashboard.jsonruntime manifest
├── 📝static/js/analytics-dashboard.jsbrowser app
├── 📝content/analytics/_index.mdpage

The zorto.dev data-app files today.

The dashboard page is a thin shell. On click it lazy-loads DuckDB-Wasm and Plotly.js, fetches /data/site.ddb, attaches it read-only, runs configured SQL, then renders charts and tables.

Search uses the same database. That matters more than the dashboard itself: Zorto can ship one public data artifact and let different browser surfaces query it.

One panel is just SQL over the attached database:

SELECT kind, count(*) AS files, sum(bytes) AS bytes
FROM site.main.build_outputs
GROUP BY kind
ORDER BY bytes DESC;

Layers #

1
Content
Markdown owns pages and explanations
website/content
2
Config
TOML owns sources, limits, panels, queries
website/data
3
Code
Rust, Python, SQL, HTML, CSS, and JS do the work
crates + pipelines + static/js

The editing contract stays small.

This follows the separation I want for Zorto:

  • Content: Markdown owns the page.
  • Config: TOML owns the data app shape.
  • Code: Rust, Python, SQL, HTML, CSS, and JavaScript handle execution.

Humans get stable files to edit. Agents get boundaries. The repo stays legible.

Dashboard /analytics/ Database /data/site.ddb Manifest analytics-dashboard.json Pipeline website/bin/build-meta Metadata config website/data/meta.toml Dashboard config website/data/analytics.toml

Implementation #

website/bin/build-meta is a self-contained uv script. It uses DuckDB, runs a timed current-code Zorto build through the Rust CLI, performs privacy checks, writes a temporary database, validates it, then atomically replaces website/static/data/site.ddb.

website/data/meta.toml owns the pipeline settings: output path, build output directory, collection limits, content include and exclude rules, privacy checks, and the build command.

website/data/analytics.toml owns the dashboard: views, panels, KPIs, SQL queries, table columns, and runtime assets.

The database includes pipeline_steps, so generation leaves receipts: step name, kind, status, duration, output count, command, and details.

The app surface #

A dashboard is data plus frontend.

Python is good for data work: pulls, transforms, validation, orchestration. Many Python dashboard tools eventually emit JavaScript anyway. Zorto keeps the split direct:

  • Build data locally with uv, Python, and DuckDB.
  • Ship .ddb files beside static HTML.
  • Render the interface with HTML, CSS, and JavaScript.
  • Query in the browser with DuckDB-Wasm when embedded data is enough.
  • Use remote DuckDB when live data is worth it.

That can stay static or grow into dynamic browser interfaces. The default remains standards first: files, SQL, HTML, CSS, JavaScript, and Rust where the generator needs to be solid.

Boundaries #

This is a zorto.dev implementation, not a public Zorto data API.

There is no [data] config in Zorto core yet. There is no automatic pipeline hook in zorto build. DuckDB-Wasm and Plotly are pinned CDN-loaded runtime assets for now.

Next #

  • Promote the stable pieces into Zorto after zorto.dev proves them.
  • Keep uv as the local Python orchestration layer.
  • Keep DuckDB as the local database layer.
  • Use DuckDB’s beta Quack protocol for remote DuckDB when live access is useful.
  • Use DuckLake when the data wants lakehouse-style partitioning instead of a single DuckDB database file.

Zorto remains an AI-native static site generator with executable code blocks. The & more is now visible: data apps built from small files, local pipelines, and browser-native interfaces.