A certification and cross-surface sync agent for DataHub.
Built for the DataHub Agent Hackathon β challenge category: Agents That Do Real Work.
π₯ Demo video: https://youtu.be/8lgGDGlq6lc
This submission is a repo + setup instructions, not a hosted demo β no live URL required to evaluate it. Two ways to see it work:
- Watch the demo video above β it walks the full detect β route β certify β write-back loop end to end, including a live Slack API pull.
- Run it yourself β follow Setup below. You'll need your own free OpenRouter key (no sponsor LLM credits on this hackathon); if you'd rather not spend it,
examples/has a full committed run's output (all 5 scenarios, detect/route/certify/before-after) so you can inspect real results without running anything.
Metadata disagreements happen everywhere and nobody notices:
- A Slack thread says
billing_amountmeans "net of adjustments." - The dbt model's own SQL comment says it means "gross charge, before adjustments."
- Every downstream dashboard and agent just... picks one. Silently.
Nobody decided. Nobody was asked. The next AI agent that answers a question about that term is confidently wrong, and nothing in DataHub reflects the disagreement.
Cert Sync watches real sources (Slack threads, docs) for a term, compares what they claim against the definition already registered in DataHub, and when it finds a genuine conflict:
- Detects the drift and extracts what each source is actually claiming (LLM-based extraction, not string matching).
- Resolves the real owner of the term from DataHub's own ownership metadata β routes to a human, never auto-decides.
- Once a human certifies the correct definition, writes it back into DataHub as the system of record, with a full audit trail of where it came from.
- Proves the fix reaches everyone downstream β a completely separate consumer agent, with no shared code or session, gives the wrong answer before certification and the right answer after, just by reading DataHub again.
It also runs as a standing service: a worker proactively scans for drift on a schedule, every detection/certification is persisted to a queryable audit log, and a dashboard shows the whole thing live.
flowchart LR
subgraph DH["DataHub (docker quickstart)"]
GMS["GMS :8081"]
UI["Frontend UI :9002"]
end
subgraph CS["Cert Sync (docker compose)"]
API["api β FastAPI\n1 long-lived cert MCP session (mutation-enabled)\n+ fresh consumer MCP session per /ask"]
Worker["worker\nscheduler.py"]
Dashboard["dashboard\nStreamlit + api_client.py"]
end
Slack["Slack Web API"]
SQLite[("SQLite\n(named volume)")]
Worker -->|"POST /scan\nevery N min"| API
Dashboard -->|"HTTP (httpx)"| API
API -->|"MCP (stdio)"| GMS
API --> Slack
API --> SQLite
GMS -.-> UI
Only the api service ever opens an MCP session or touches SQLite. The worker and dashboard only ever speak HTTP to api. eval/run_eval.py and the CLI pipeline (cert_agent/pipeline.py) are separate offline tools that never go through the live API β they're used for measuring detection precision/recall against the static scenario fixtures.
Built on the DataHub MCP Server (mcp-server-datahub, self-hosted, TOOLS_IS_MUTATION_ENABLED=true for the certifying agent, read-only for the consumer agent) β the two agents never share a session, which is what makes the "does the fix actually propagate" proof meaningful.
Across all 5 scenarios (including one where the term has no registered owner at all), detection runs at 100% precision and recall. See examples/sample_eval_run/summary.json for a committed run's raw output, or Page B of the dashboard for a live view after running eval/run_eval.py yourself.
- Docker + Docker Compose
- Python 3.11+ and
uv(providesuvx, used to launchmcp-server-datahub) - A free OpenRouter API key (used for drift detection and Q&A β see Notes on LLM usage below):
- Sign up at openrouter.ai (free, no card required).
- Go to openrouter.ai/keys β Create Key.
- Paste it into
.envasOPENROUTER_API_KEY(see Configure environment below).
- A Slack workspace where you can install a bot (free tier is fine) β only needed if you want to see the live Slack ingestion for
billing_amount; every other scenario works from static fixtures with no Slack setup at all
datahub docker quickstart
# generate setup/healthcare.db locally (gitignored - not committed) - see
# setup/README.md for the source CSVs and full generation steps
python setup/create_db.py /path/to/healthcare-dataset-csvs
datahub ingest -c setup/ingest.yaml
python setup/add_lineage.py
python setup/add_metadata.pySee setup/README.md for full details on the dataset (a forked healthcare pipeline built from the Kaggle Healthcare Dataset, CC0 licensed, synthetic data only).
cp .env.example .envFill in DATAHUB_GMS_URL (defaults to http://localhost:8081) and OPENROUTER_API_KEY. SLACK_BOT_TOKEN is optional β leave it blank to skip Slack setup; detection then falls back to the static source_a_*.md fixture for every scenario, including billing_amount.
Only scenario_01_billing_amount is wired to Slack. To see it live instead of from a fixture:
- Create a Slack workspace if you don't have one: slack.com/get-started.
- Go to api.slack.com/apps β Create New App β From scratch β name it (e.g.
certsync-demo-bot). - OAuth & Permissions β add Bot Token Scopes:
channels:history,channels:read,chat:write,users:read. - Install to Workspace, then copy the Bot User OAuth Token (
xoxb-...) into.envasSLACK_BOT_TOKEN. - Create a public channel (e.g.
#finance-reporting-demo) and/invite @certsync-demo-bot. - Get the channel ID from the channel details panel (starts with
C) and set it inslack_channels.yaml:billing_amount: channel_id: "C0XXXXXXX" channel_name: "finance-reporting-demo"
- Seed the channel with the demo thread once:
python setup/seed_slack_channel.py
docker compose up --buildThis brings up:
apiβ http://localhost:8010 (FastAPI, holds the MCP sessions, healthcheck at/health)workerβ proactively hits/scaneverySCAN_INTERVAL_SECONDS(default 1800s / 30 min β deliberately conservative, see Notes on LLM usage)dashboardβ http://localhost:8501 (Streamlit UI)
Open http://localhost:8501 and walk through "Live Walkthrough" for scenario_01_billing_amount.
The Source A panel shows a LIVE via Slack API badge and the exact fetch timestamp. To prove it's a real API pull and not a cached fixture:
- Post any message into the seeded Slack channel from another client (Slack app,
curlagainstchat.postMessage, orpython demo/video_pipeline/post_live_message.py <channel_id> "<text>"). - Click π Refresh from Slack in the dashboard.
- The new message appears and the fetch timestamp updates β with no dashboard restart, no redeploy, no code change.
examples/sample_eval_run/summary.json is a real, committed run across all 5 scenarios β full drift detection, resolved owner, certified before/after definitions, and consumer-agent answers before and after certification. See examples/README.md for how to read it. Useful if you want to see actual output without running the stack or spending LLM quota.
cert_agent/ Detection, owner resolution, certification (mutation-enabled MCP session)
consumer_agent/ Read-only Q&A agent, proves the fix propagates downstream
integrations/ Slack Web API client
persistence/ SQLite audit-log layer
services/ Shared business logic used by the API (detect/certify/ask/scan)
api/ FastAPI service β the only process that opens MCP sessions
worker/ Proactive drift-scan scheduler (HTTP-only, no MCP/business logic)
demo/ Streamlit dashboard + video-production tooling (demo/video_pipeline/)
eval/ Offline precision/recall evaluation against scenario fixtures
scenarios/ 5 scenario fixtures (sources + expected outcomes)
setup/ DataHub healthcare dataset setup (pipeline, lineage, metadata, Slack seeding)
Drift detection and Q&A call an LLM via OpenRouter β there's no sponsor LLM credit for this hackathon, so you'll need your own free OpenRouter key (see Prerequisites). Free-tier OpenRouter keys cap at 50 requests/day account-wide. Two things keep that from being a bottleneck:
- The
worker's proactive scan defaults to every 30 minutes (SCAN_INTERVAL_SECONDS=1800), not 5 β each scan is one LLM call per scenario (5 total), so a shorter interval left running unattended would exhaust a fresh free-tier key before you get to click anything in the dashboard. examples/has a full sample run committed, so you can see real output without spending any quota at all.
If you do run out mid-session, cert_agent/drift_detector.py also supports a GEMINI_API_KEY fallback, or point OPENROUTER_API_KEY at a paid tier.
The healthcare dataset (setup/) is derived from the Kaggle Healthcare Dataset (CC0 1.0, public domain, synthetic records) β column normalization, planted data-quality issues, pipeline staging, and DataHub metadata/lineage on top of it were built for this project. All agent, service, API, worker, and dashboard code was written for this hackathon submission.
Apache 2.0 β see LICENSE.