dashctl
v0.1.0-beta.0
Published
Compile dashboard.yaml into a single self-contained static HTML dashboard.
Downloads
20
Maintainers
Readme
dashctl
Compile a dashboard.yaml into a single self-contained static HTML dashboard.
Tableau-style grid layout. Observable Plot charts. Live or snapshot data
(CSV / Parquet / DuckDB-WASM / HTTP / BigQuery).
dashctl build dashboard.yaml -o dashboard.htmlOne YAML in, one HTML out. Drop it on S3, attach it to an email, open it from Finder. There is no runtime server, no SaaS, no tracker. The format is small, declarative, and easy to write by hand or by LLM.
Status: v0 in active development. See SPEC.md for the language reference
and design decisions.
30-second pitch
# dashboard.yaml
title: Sales
sources:
orders: { type: csv, url: ./orders.csv }
panels:
- row:
- type: kpi
title: Total revenue
source: orders
query: SELECT SUM(amount) AS [value] FROM orders
format: currency
- type: line
title: Revenue over time
source: orders
query: SELECT date, SUM(amount) AS revenue FROM orders GROUP BY date
x: date
y: revenue$ dashctl build dashboard.yaml -o dashboard.html
$ open dashboard.htmlQuick start
npm install -g dashctl
dashctl init my-dashboard
dashctl build my-dashboard/dashboard.yaml -o dash.htmlDemo & landing
- Polished demo:
examples/demo/— three pages (Overview, Geography, Trends), 5 filter types, cross-filtering, custom indigo/coral theme, ~2500-row synthetic e-commerce dataset. Build it withdashctl build examples/demo/dashboard.yaml -o demo.html. - Landing page:
landing/index.html— plain static HTML, no build step. Open it in a browser.
Monorepo layout
packages/
schema/ Zod source-of-truth → JSON Schema + TS types + runtime validator
cli/ commander-based CLI: init, dev, build, validate, sources pull
runtime/ Svelte components for each panel type (kpi, line, bar, table, ...)
examples/
hello/ Minimal CSV-backed example to validate the spec end-to-end
sales/ Multi-page Phase 2 example with cross-filter on bar/table
duckdb/ DuckDB-WASM live source
bigquery/ BigQuery via `dashctl serve` proxy (ADC auth) — primary BQ mode
bigquery-oauth/ BigQuery via browser OAuth direct — niche internal-team mode
filters-showcase/ One panel per filter type
demo/ Polished hero example (custom theme, 3 pages, generated data)
landing/ Hand-written static HTML landing page (not built by dashctl)
docs/ (placeholder — built with dashctl itself, eventually)Development
npm install
npm run typecheck
npm run build
npm run cli -- build examples/hello/dashboard.yaml -o /tmp/hello.htmlDuckDB-WASM (~140 MB) is an optionalDependency — needed only for dashboards declaring a duckdb-wasm source. To skip it, run npm install dashctl then rm -rf node_modules/@duckdb. (Plain --omit=optional also drops vite/rolldown's platform-native bindings and breaks builds.)
BigQuery in 30 seconds
gcloud auth application-default login
dashctl serve dashboard.yaml --port 8080The bundled HTML's BQ queries route through the local server's /api/bq,
which authenticates via your gcloud login (no service-account JSON, no
browser OAuth setup, no @google-cloud/bigquery install). A SHA-256 query
allow-list is built from the YAML at compile time so the proxy can only
run queries the dashboard's author already approved. See
examples/bigquery/ for the full setup. For the older browser-OAuth-direct
mode (every viewer signs into Google), see examples/bigquery-oauth/.
To expose dashctl serve publicly, gate viewers with Google OAuth:
dashctl serve dashboard.yaml --auth google \
--google-client-id 1234.apps.googleusercontent.com \
--google-client-secret $DASHCTL_GOOGLE_CLIENT_SECRET \
--google-domain example.com \
--session-secret $DASHCTL_SESSION_SECRETSessions are stateless HMAC-signed cookies (no server-side store). At least
one of --google-domain or --google-allow [email protected],... is required.
Roadmap
- Phase 4 — DuckDB-WASM as the default in-browser engine (replaces alasql), removes the bracketed-keyword and quoting workarounds.
- Phase 5 —
dashctl publish(S3 / Pages / Vercel one-shot) + signed snapshots. - Phase 6 — write-mode panels (notes, annotations) and shared filter URLs.
SQL gotchas
Phase 2 still uses alasql (now in the browser). It treats value, key, order, name, etc. as reserved when used as AS aliases. Bracket them: SELECT SUM(amount) AS [value] FROM .... A future phase will swap to DuckDB-WASM and remove this constraint.
Filter values are bound into queries as safe-quoted SQL literals (single quotes doubled, dates as 'YYYY-MM-DD', arrays as ('a','b','c') for IN-lists). See packages/runtime/src/runtime.ts for the full substitution table.
Cross-filtering scope
Phase 2 ships cross-filtering for bar charts (click a bar to set the named filter) and table rows (declare on_click: { filter: <name>, from: <column> }). Line/area cross-filtering will follow in a later phase — Plot's pointer interactions for line marks are noisier and not worth blocking the milestone on.
License
MIT
