DataGoat is one Worker, one D1 database, one KV namespace (login throttling only), three Rate
Limiting bindings and one cron trigger, all declared in wrangler.jsonc.
The button copies the repository into your GitHub account and builds it with Workers Builds:
- Resources. Cloudflare provisions the D1 database (
DB) and the KV namespace (LOGIN_THROTTLE) and writes their ids into your copy'swrangler.jsonc, replacing the placeholder ids. Rate Limiting bindings need no provisioning. - Secrets. You are prompted for the values in
.dev.vars.example:SETUP_TOKEN: any long random string (openssl rand -hex 24), used once.SESSION_SECRET: at least 32 random characters (openssl rand -hex 32).ENCRYPTION_KEY: at least 32 random characters. Never change it later: stored provider credentials would no longer decrypt. Setup refuses to run while any of them is missing, shorter than required, or still the example placeholder (GET /api/setuplists the problems).
- Build command:
npm run build(dashboard + tracker intopublic/). - Deploy command:
npm run deploy= build,wrangler d1 migrations apply DB --remote, thenwrangler deploy. Migrations reference the binding name, so a renamed database still works, and they run non-interactively in CI. The Worker also bundles every migration and applies any pending ones itself on its first request (samed1_migrationsbookkeeping as wrangler), so a deploy whose command skipped the migration step still works. If the schema cannot be brought up, every API call answers 503Database is not initialised: <reason>and the setup page shows it. - Open
https://<your-worker>/setup?token=<SETUP_TOKEN>to create the first owner and website, then copy the tracking snippet.
npm ci
npx wrangler login
npx wrangler d1 create datagoat # paste the database_id into wrangler.jsonc
npx wrangler kv namespace create LOGIN_THROTTLE # paste the id into wrangler.jsonc
npx wrangler secret put SETUP_TOKEN
npx wrangler secret put SESSION_SECRET
npx wrangler secret put ENCRYPTION_KEY
npm run deployThen open /setup?token=… as above. Later deploys are npm run deploy (migrations first, then the
Worker). Keeping a deployed copy up to date is described in updating.md.
Serve the Worker on a subdomain of your site (a.example.com) or a route on your own zone for
first-party cookies; *.workers.dev works but is blocked more often by ad blockers. Static assets
(/js/*, dashboard) never invoke the Worker, so the script keeps loading even if a Free account hits
its daily request limit.
- PBKDF2 uses 50,000 iterations by default (
PASSWORD_ITERATIONSinwrangler.jsonc). The Workers runtime allows up to 100,000, which measured about 16 ms per hash in local workerd, above the Free plan's 10 ms CPU limit per request; 50,000 is about 8 ms. On Workers Paid, set it to"100000". Existing hashes are re-hashed with the configured count at the next login. Trade-off: fewer iterations make offline guessing of a leaked hash cheaper, so use long passwords. - The cron runs every minute. Idle jobs write almost nothing (see architecture/jobs.md).
- Deleting a website removes its rows gradually (1000 rows per hour by default,
PURGE_ROWS_PER_RUN), because deletes count against the 100,000 daily row writes.
npm run reset-password -- you@example.com https://analytics.example.com # remote D1
npm run reset-password -- you@example.com http://localhost:8787 --local # local devIt inserts a one-hour, single-use token with wrangler d1 execute and prints the link.
npm run smoke -- --url https://analytics.example.comRead-only checks over HTTP with a pass/fail report: health and schema status, setup state and
secret validation, the dashboard HTML and assets, the three tracker scripts, JSON 401/404 responses,
and the 501 answers of features that are not built yet (or that they are implemented). Add
--email you@example.com --password … to also sign in, read your account and websites and sign out.
On a fresh install, --setup-token <SETUP_TOKEN> --email … --password … completes first-run setup
(this creates the owner account with those credentials). Exit code 1 when any check fails.