Run the on-prem agent demo
The on-prem agent demo stands up a believable customer environment on your laptop: five tables in a local Postgres database, a drift loop that keeps writing fresh rows (and the occasional bad one), and the DQ Agent pointed at the hosted dev cloud. The first 60 seconds of UI look like a real production deployment instead of a 7-row toy.
This is a demo runbook only — none of the components are intended for production use.
What you get
| Component | What it does |
|---|---|
customer_demo Postgres database | 5 tables — customers (~200), products (~80), orders (~900), order_items (~2,300), events (~3,000) — seeded deterministically. |
| Drift loop daemon | Inserts a small batch of valid rows every 30-60 s. 5-10% of ticks insert one bad row that violates exactly one expectation. 3% of ticks corrupt an existing row and heal it 60 s later. |
| DQ Agent container | Picks up checkpoint runs from the hosted cloud, executes them locally against customer_demo, and posts metadata back. |
| Demo composition | Provisions 5 data assets + 5 rule suites (~25 expectations) + 5 checkpoints + 5 schedules with staggered crons (*/2, */3, */4, */5). |
End-state: a dashboard with a >95% pass rate, a 3-5% sliver of runs showing 1-2 failed expectations, and a visible "anomaly recovered" arc on the timeline whenever the corrupt+heal flow fires.
Prereqs
- Docker (for the local Postgres + the agent container).
uv(the workspace package manager — see the top-levelCLAUDE.mdfor setup).- A hosted DQ Cloud dev tenant you control, with:
- An agent token (
dqa_…) issued from Settings → Agents. - An API key (
dqk_…) for the demo provisioning script. - A workspace id and an org id (visible in the Settings URL).
- An agent token (
Environment variables
| Variable | Required for | Notes |
|---|---|---|
DQ_CLOUD_BASE_URL | agent + demo_setup | e.g. https://dev.placeholder.example.com. |
DQ_CLOUD_AGENT_TOKEN | agent | The dqa_… token. |
DQ_CLOUD_API_KEY | demo_setup | A dqk_… personal API key. |
DQ_CLOUD_ORG_ID | demo_setup | UUID of your hosted org. |
DQ_CLOUD_WORKSPACE_ID | demo_setup | UUID of the workspace the agent token is scoped to. |
DQ_CLOUD_DATASOURCE_ID | demo_setup | UUID returned when the agent registered customer_demo (see step 3). |
CUSTOMER_DEMO_DSN | seed + drift loop | Optional — defaults to postgresql://postgres:postgres@localhost:5432/customer_demo. |
DQ_AGENT_DATASOURCE_DSNS | agent | JSON object mapping datasource UUID → local DSN. See step 4. |
1. Stand up local Postgres
The same compose file the rest of the dev environment uses:
make dev
Postgres lands on :5432 with postgres / postgres as the default
credentials. The seed script creates customer_demo for you on first run.
2. Seed customer_demo and start the drift loop
make demo-up
The first time you run it make demo-up will:
- Create the
customer_demodatabase (if missing). - Drop and rebuild the five tables, inserting the seeded row counts.
- Start the drift loop as a background process. PID + log live in
.demo/drift_loop.{pid,log}. - If
DQ_CLOUD_BASE_URLandDQ_CLOUD_AGENT_TOKENare set, start the agent container too.
Tail the drift log to confirm it's alive:
tail -f .demo/drift_loop.log
You should see entries like:
INFO drift_loop valid_insert order_id=901 customer_id=27
INFO drift_loop valid_insert order_id=902 customer_id=143
WARNING drift_loop bad_insert kind=order expectation=orders.status in (...) customer_id=84
INFO drift_loop heal_applied order_id=412
3. Register customer_demo with the cloud
From inside the agent container (one-shot registration):
docker exec dq-demo-agent python -c "
import httpx, os
r = httpx.post(
os.environ['DQ_AGENT_CLOUD_URL'] + '/api/agent/v1/datasources',
headers={'Authorization': f'Bearer {os.environ[\"DQ_AGENT_TOKEN\"]}'},
json={'name': 'customer_demo', 'type': 'postgres'},
)
print(r.status_code, r.json())
"
Copy the returned datasource UUID. Save it as DQ_CLOUD_DATASOURCE_ID.
4. Wire the agent's local DSN
The agent needs to know how to reach customer_demo from inside its
container. Restart it with DQ_AGENT_DATASOURCE_DSNS populated:
docker rm -f dq-demo-agent
DQ_AGENT_DATASOURCE_DSNS='{
"<datasource-uuid>": "postgresql://postgres:postgres@host.docker.internal:5432/customer_demo"
}' \
make demo-up
host.docker.internal resolves to the host machine on macOS and Linux
(with the --add-host=host.docker.internal:host-gateway flag the
Makefile passes). If you're on a host without that resolver, use the
literal 172.17.0.1 (Docker bridge default) or your VPN-side IP.
5. Provision the hosted side
Run the demo composition to create the five assets, suites, checkpoints, and schedules:
DQ_CLOUD_API_KEY=dqk_... DQ_CLOUD_BASE_URL=https://dev.placeholder.example.com \
DQ_CLOUD_ORG_ID=... DQ_CLOUD_WORKSPACE_ID=... \
DQ_CLOUD_DATASOURCE_ID=... \
uv run python -m infra.terraform.compositions.services.demo_setup
The script is idempotent — re-running it updates existing rows by name rather than duplicating them, so it's safe to use as part of a "reset the demo" routine.
Within a couple of minutes the dashboard starts to fill in:
- Five checkpoint cards, each ticking on its own cron.
- A "Pass rate" gauge climbing past 95%.
- A timeline with the occasional red dot, plus the recover-after-60s arc whenever the drift loop's corrupt+heal flow fires.
Stopping
make demo-down # stop drift loop + agent container, keep data
make demo-reset # stop daemons AND re-seed customer_demo from scratch
make demo-down is non-destructive — the customer_demo data stays, so
restarting with make demo-up resumes where you left off.
Troubleshooting
could not translate host name "host.docker.internal". Your Docker
build doesn't add the host gateway automatically. The Makefile passes
--add-host=host.docker.internal:host-gateway, which works on Docker
20.10+; older Engines need a manual --network host swap or the literal
bridge IP.
relation "customers" does not exist. The drift loop ran before the
seed. Run make demo-reset to drop + reseed, then make demo-up again.
Pass rate stays at 100%. The drift loop hasn't injected a bad row
yet — bad inserts are 5-10% of ticks, so on average one every ~10 ticks
(5-10 minutes). Tail .demo/drift_loop.log and grep for bad_insert to
confirm; once one fires, the next checkpoint run for that table picks it
up. If bad_insert lines are present but the UI still shows green,
check that the agent container is up and not throwing
agent_poll_failed warnings (docker logs dq-demo-agent).
Missing required env vars from demo_setup.py. All five
DQ_CLOUD_* env vars are required. The script lists which one is
missing — set it and re-run.
Re-registering the datasource returns 409. The hosted API prevents
the same agent token from registering two datasources with the same
name. Either deregister the old one (DELETE /api/agent/v1/datasources/<id>
from inside the agent container) or use a different name.
See also
- Deploy the on-prem agent — production runbook.
- Agent mode — architecture and metadata cut.
scripts/demo_data/— seed + drift sources.infra/terraform/compositions/services/demo_setup.py— hosted-side composition.