尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Open-source, self-hosted curation for your warehouse: pin messy values like BCG / B.C.G. / Boston Consulting Group to one approved record, keep the reference tables everything depends on, and set up in one command. (The category is master data management — minus the enterprise.)
翻译准备中
正在加载完整 README。 以下为已保存的摘录。
One record for Brasil, Great Britain, and every other spelling your warehouse can't agree on.
Zug Zug is a shared, spreadsheet-fast UI that runs next to your warehouse. Your team
maps messy values to one approved record and maintains the reference tables every
dashboard depends on — with roles, drafts, and full history. Publish, and the result
is plain SQL tables back in your own warehouse. Self-hosted, one command to run.
Status: pre-1.0 — early release. Expect rough edges and breaking changes between minor versions until v1.0.查看完整 README ↗Brasil, Great Britain, and every other spelling your warehouse can't agree on.Zug Zug is a shared, spreadsheet-fast UI that runs next to your warehouse. Your team maps messy values to one approved record and maintains the reference tables every dashboard depends on — with roles, drafts, and full history. Publish, and the result is plain SQL tables back in your own warehouse. Self-hosted, one command to run.
Live demo → · Docs · Quickstart · How it works · Architecture
Status: pre-1.0 — early release. Expect rough edges and breaking changes between minor versions until v1.0.
Every warehouse fills with names nobody agrees on — BCG, B.C.G.,
Boston Consulting Group, all the same company. Multiply that by every country,
currency, vendor, and channel your dashboards group by, and "which spelling is real?"
becomes a standing tax on every number your team reports.
Zug Zug is where a human settles it. Point it at a warehouse column with a
read-only credential; it scans the distinct values still waiting to be mapped. Your
team maps each one to an approved record in a spreadsheet-fast grid, and maintains
the reference tables — Country, Currency, Partner — those records live in.
Publishing materializes dim_<x> / map_<x> tables your warehouse can read directly.
Everything you publish lands as plain tables in your own warehouse — no lock-in, no proprietary format. The mapping work your team does is yours to keep, with or without Zug Zug.
The enterprise calls this "master data management." The incumbents — Tamr, Stibo, Reltio — cost six figures, are closed, and are built for a central governance team. Zug Zug is the small, self-hosted version, for the people who actually know the data.
Not entity resolution. Not an app builder. No survivorship rules, no automatic merging, no app platform — a person decides which values mean the same thing, and the result is a table. Optional AI suggestions can propose a record for a value (bring your own OpenAI/Anthropic key), but nothing is saved until a human confirms it.
Live demo — demo.zugzughq.com · one-click login, seeded data, nothing to install. Poke the grid and the Review inbox in your browser.
Or run it yourself in 30 seconds. Requires Docker — no warehouse or Google account needed:
git clone https://github.com/Fredehagelund92/zugzug.git
cd zugzug
docker compose up
Open http://localhost:8080 and create your account — the first user becomes the
admin. It boots with sample reference tables like Country and Product Category,
plus a bundled local warehouse, so the grid and source catalog are live from the
first click. (First run builds the images — a few minutes; later runs start in
seconds.)
See Develop locally to hot-reload the code, or server/.env.example to connect your own warehouse and turn off the demo seed.
1. Read-only in. Connect with a read-only credential. Zug Zug scans DISTINCT
values from the columns you register as sources, and skips the ones already mapped —
it never writes to your warehouse unless you opt into a writable adapter.
2. A human maps them. The Review inbox lists every unmapped value across all your tables, ordered by how often it occurs, so you spend time where the rows are. You're not signing up to hand-map 40,000 spellings — the few hundred most frequent usually cover the vast majority of your rows, and the long tail stays unmapped until it's worth the effort. Map it, merge duplicates, or skip — with drafts, roles, and a full audit trail.
3. Publish. Drafts sit in Postgres until an editor publishes, folding them
into a new numbered table version and materializing the dim_<x> / map_<x> tables.
The audit log records who published what, and when. Then it's just a join:
-- Zug Zug publishes two tables per list:
-- map_country(raw, key) — every source spelling → a record's key
-- dim_country(key, label, …) — the approved records and their attributes
select o.*, d.label as country
from raw.orders o
left join map_country m on m.raw = o.shipping_country
left join dim_country d on d.key = m.key
For the full picture — the three stores, the request path, the server subsystems — see ARCHITECTURE.md.
Because those work right up until a second person touches them.
DISTINCT values still waiting to be mapped, so the work is always
the real backlog — not a stale copy someone exported last quarter.CASE statement is the same list, buried in SQL, editable only by
whoever owns the model. Zug Zug hands that list to the people who actually know
whether Brasil is Brazil.The honest tradeoff: this is a Postgres and a container that a CSV isn't. Worth it once more than one person edits the same list — probably not before.
The recommended path is pull-first: results live in Postgres, and your warehouse team ingests them through the pipeline they already trust — a dbt source, the Pull API, or an on-demand Parquet snapshot — so every publish flows through the same dev→prod and review path as everything else.
Prefer a direct push? Configure a writable warehouse adapter and each publish writes straight into your warehouse (e.g. a MotherDuck database you already query) — an opt-in convenience that trades pipeline control for simplicity. See ADR-0007.
Self-hosting? Read backup & restore before you have real data to lose. Running it for real? See Deploy to production — Caddy auto-HTTPS and bundled Postgres, with escape hatches for existing ingress and managed databases.
A warehouse adapter is a small TypeScript interface (WarehouseAdapter). DuckDB /
MotherDuck is production-ready and is what the demo runs on; Snowflake is experimental;
other warehouses are community-roadmapped.
| Adapter | Status | Notes |
|---|---|---|
| DuckDB (read-only) | shipped | Local files, in-memory, or MotherDuck with a read-only token |
| DuckDB / MotherDuck (writable) | shipped | Set MOTHERDUCK_WRITABLE=true; token needs write access |
| Snowflake | experimental | Key-pair (JWT) auth; the scan and publish paths work. Warehouse catalog auto-discovery isn't wired up yet, so you register sources by explicit path. |
| Postgres-as-warehouse | roadmapped | Community PR welcome — see Add an adapter |
| BigQuery | roadmapped | See above |
| Databricks | roadmapped | See above |
New adapters implement the interface in server/src/warehouse/adapter.ts; the
Snowflake adapter in server/src/warehouse/snowflake/ is the reference
implementation.
Two modes, switched by environment variable:
OIDC_ISSUER_URL to any compliant provider (Google Workspace, Okta,
Authentik, Keycloak). The sign-in page shows an SSO button automatically.zzsa_*). Pass as Authorization: Bearer <token>; scoped to one workspace,
intended for dbt CI and scripts.See server/.env.example for every auth option.
For hacking on Zug Zug with hot reload (Vite + bun --watch), run the two processes
directly. Prerequisites: Bun 1.x, Postgres 14+, and a warehouse you
have read access to.
git clone https://github.com/Fredehagelund92/zugzug.git
cd zugzug
# 1. Configure the server
cp server/.env.example server/.env
# DATABASE_URL=postgres://user:pass@localhost:5432/zugzug
# MOTHERDUCK_TOKEN=<your token> (read-only is enough for scanning)
# ATTACH_WAREHOUSE=true (false by default; flip to enable scans)
# 2. Bootstrap (first run only). --seed provisions a small demo warehouse;
# omit it if you have a real DATABASE_URL and MOTHERDUCK_TOKEN.
cd server && bun run bootstrap -- --seed
# 3. Start the backend (listens on :8787)
bun run start
# 4. In another shell, start the frontend
cd ../app && bun run dev
Open http://localhost:5173; the first user to sign up becomes the admin. For
non-default setups (Snowflake, OIDC, writable warehouse store), every option is
documented in server/.env.example.
See CONTRIBUTING.md. All commits require a Signed-off-by: line
(DCO) — use git commit -s. To propose a new warehouse adapter, open an
Add an adapter
issue first — it helps to agree on the auth shape before writing code.
MIT. See LICENSE.