A standalone web frontend for browsing VGI (Vector Gateway Interface) database catalogs. Cupola connects to any VGI HTTP server and presents its schemas, tables, views, and functions in a navigable catalog browser — with an embedded SQL shell, pivot tables, and an AI data analysis agent built in.
Designed to be shared across all VGI server implementations (Python, TypeScript, Go). VGI servers redirect browsers to the hosted frontend with ?service={url}.
- Catalog browser — sidebar tree of schemas, tables, views, functions, and macros with searchable navigation, detail panels, column statistics, and on-demand column profiling
- Embedded SQL shell — DuckDB-WASM running in the browser with the VGI extension, an xterm.js terminal, tab completion, session persistence, and dot commands
- AI data analysis — Claude-powered agent that can run SQL, inspect schemas, and answer questions about your data (bring your own Anthropic API key)
- Pivot tables — Perspective-based data grids backed directly by DuckDB-WASM
- Data preview — paginated table browsing, geometry (WKB) visualization, example queries, and Markdown descriptions via VGI tags
- Deep linking — selection state encoded in the URL hash so views can be shared
- OAuth / PKCE — works with VGI servers that require authentication, including per-catalog identity
- Theming — custom color themes loadable via
?theme=<url>, with a live editor at/theme-builder
- Astro + React — static site with React islands
- ShadCN/UI + Tailwind CSS — components and styling
- DuckDB-WASM — in-browser SQL engine
- Perspective — pivot tables and data grids
- xterm.js — terminal emulator for the SQL shell
- Bun — package manager and runtime
- Hosted on Cloudflare Pages with versioned assets served from R2
# Install dependencies
bun install
# Start the dev server at http://localhost:4321
bun run devCupola needs a running VGI server to talk to. Point it at one with the service query parameter:
http://localhost:4321/?service=http://localhost:9003
Without ?service=, a welcome / connect page is shown.
| Command | Action |
|---|---|
bun install |
Install dependencies |
bun run dev |
Start local dev server at localhost:4321 |
bun run build |
Build the production site to ./dist/ |
bun run preview |
Preview the production build locally |
bun run test |
Run unit tests |
bun run test:e2e:smoke |
Run the Chromium CI smoke suite against VGI_SERVICE_URL |
bun run check |
Type-check Astro, React, and test sources |
bun run check:bundle |
Enforce the production JS size budget after a build |
bun run audit |
Report production dependency advisories |
bun run image:build |
Build the self-hosted Caddy image as cupola:flat |
bun run image:test |
Smoke-test the locally built container image |
./publish.sh |
Publish a new version (build, upload to R2, deploy) |
| Parameter | Purpose |
|---|---|
?service=<url> |
VGI server base URL to connect to |
?attach_options=<sql> |
Extra options spliced into the DuckDB ATTACH statement |
?theme=<url> |
URL of a theme JSON file (colors, logo, terminal theme) |
?fresh |
Clear any saved DuckDB session snapshot for this service |
#ai_key=<key> |
Anthropic API key for the AI agent (stripped from the URL after use) |
#/schema/<s>/table/<t> |
Deep link to a catalog selection |
Assets are served from Cloudflare R2 via a Worker with a versioned URL scheme (/v{version}/..., with /latest/ redirecting to the current version).
Publish locally:
- Bump
versioninpackage.json - Run
./publish.sh
Pull requests and pushes to main run the CI workflow (.github/workflows/ci.yml): frozen installs, type checking, unit tests, a production build, bundle-size enforcement, and Chromium smoke tests against the hosted Open-Meteo VGI service. Dependency advisories are reported there without failing the build until the existing audit backlog is resolved.
Releases are tag-driven. Update package.json to the next unused version,
commit it, then push the matching tag:
git tag v0.4.116
git push origin v0.4.116The Release workflow requires the tag to exactly match package.json. It
then runs the complete validation suite, publishes a multi-platform image to
GHCR, deploys the versioned Cloudflare/R2 site in a separate job, and creates a
GitHub Release with generated notes after both publishing jobs succeed.
Required repository secrets:
| Secret | Purpose |
|---|---|
SENTRY_AUTH_TOKEN |
Sentry source-map upload |
CLOUDFLARE_API_TOKEN |
Worker deploy |
R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY |
R2 S3-API asset upload |
The GitHub-provided token publishes the container; no registry password is required. The GHCR package must be made public once in the repository/package settings so unauthenticated users can pull it.
Release images support both linux/amd64 and linux/arm64:
docker run --rm -p 8080:80 ghcr.io/query-farm/cupola:latestOpen http://localhost:8080/?service=https://your-vgi-server.example. Exact
release and minor-line tags are also published, for example 0.4.116 and
0.4. The image is a static Caddy server and does not embed a VGI endpoint;
the service URL remains runtime-configurable.
To build locally, run bun run image:build, followed by
bun run image:test. All VGI client dependencies are installed from npm; no
sibling repository checkout is required.
Licensed under the Apache License 2.0.
Copyright © 2026 Query Farm LLC — https://query.farm