The CF_API_TOKEN GitHub Actions secret authorises Wrangler to deploy Workers.
To rotate:
- Cloudflare Dashboard → My Profile → API Tokens → Create Token
- Use the Edit Cloudflare Workers template
- Add D1 write permission if not included
- Copy the token
- GitHub →
Computer-Consultant/ShackDesk-Backend→ Settings → Secrets → Actions - Update
CF_API_TOKEN - Delete the old token from the Cloudflare dashboard
CF_ACCOUNT_ID is not a secret but is stored as a GitHub secret for convenience. Find it on the Cloudflare dashboard right sidebar of any zone page.
Database name: shackdesk-telemetry
Database ID: e77c8a3d-cba7-4326-846c-faeb2c585da0
There are two options for querying data without building a dashboard:
Run SQL queries directly from the terminal. Best for ad-hoc lookups and one-off investigations.
# Most recent 20 reports
wrangler d1 execute shackdesk-telemetry --remote \
--command="SELECT * FROM reports ORDER BY received_at DESC LIMIT 20;"
# All crash events
wrangler d1 execute shackdesk-telemetry --remote \
--command="SELECT id, app, version, os, props, received_at FROM reports WHERE event = 'crash' ORDER BY received_at DESC;"
# Crash count by app version
wrangler d1 execute shackdesk-telemetry --remote \
--command="SELECT app, version, COUNT(*) as crashes FROM reports WHERE event = 'crash' GROUP BY app, version ORDER BY crashes DESC;"
# Event breakdown by app
wrangler d1 execute shackdesk-telemetry --remote \
--command="SELECT app, event, COUNT(*) as total FROM reports GROUP BY app, event ORDER BY app, total DESC;"
# Reports from the last 7 days
wrangler d1 execute shackdesk-telemetry --remote \
--command="SELECT * FROM reports WHERE received_at > datetime('now', '-7 days') ORDER BY received_at DESC;"
# OS version distribution
wrangler d1 execute shackdesk-telemetry --remote \
--command="SELECT os, COUNT(*) as total FROM reports GROUP BY os ORDER BY total DESC;"A built-in SQL console in the Cloudflare dashboard — no setup required.
- Cloudflare Dashboard → Workers & Pages → D1
- Click shackdesk-telemetry
- Click the Console tab
- Type and run any SQL query directly in the browser
D1 Studio supports the same queries as the CLI above. Results are displayed as a table. Useful for quick browsing without opening a terminal.
The Reports Worker in workers/reports/ serves an authenticated web UI with aggregate
views, recent reports, filters, report detail, event totals, known installs, total runs,
and crash counts by app/version. It queries D1 directly and uses HTTP Basic Auth.
Total runs are counted from app_start events. Known installs are counted from distinct
anonymous install identifiers in props, using common keys such as install_id,
installation_id, installId, installationId, client_id, clientId,
anonymous_id, anonymousId, support_id, or supportId.
Reports Worker routes:
GET /- private HTML dashboardGET /api/summary- aggregate counts and breakdownsGET /api/options- distinct filter valuesGET /api/reports- filtered report rowsGET /api/reports/:id- single report detail
All Reports Worker API routes are read-only.
wrangler d1 execute shackdesk-telemetry --remote --command="SELECT * FROM reports ORDER BY received_at DESC LIMIT 20;"wrangler d1 execute shackdesk-telemetry --remote \
--command="DELETE FROM reports WHERE received_at < datetime('now', '-90 days');"This should be run periodically. Automating it via a Cloudflare Cron Trigger is a future improvement.
Schema changes require a migration file. Never alter the live schema directly without testing locally first.
- Write the migration SQL
- Test locally:
wrangler d1 execute shackdesk-telemetry --local --file=migration.sql - Apply to production:
wrangler d1 execute shackdesk-telemetry --remote --file=migration.sql - Commit the migration file to the repo under
db/migrations/
- Create
workers/<name>/src/index.js - Add a new
[[routes]]block and any bindings towrangler.toml - If the new Worker needs its own D1 database, create it:
wrangler d1 create <db-name> - Add a deploy step to
.github/workflows/deploy.ymlif it needs a separate pipeline
The Reports Worker has a separate Wrangler config:
wrangler deploy --config wrangler.reports.tomlSet the Basic Auth credentials as Cloudflare Worker secrets:
wrangler secret put REPORTS_USERNAME --config wrangler.reports.toml
wrangler secret put REPORTS_PASSWORD --config wrangler.reports.tomlDo not store these values in the repo.
The reports.shackdesk.com DNS record must be proxied in Cloudflare. Wrangler registers
the Worker route from wrangler.reports.toml during deployment.
wrangler devWrangler creates a local SQLite database at .wrangler/state/. This is gitignored.
To seed the local database with the schema:
wrangler d1 execute shackdesk-telemetry --local --file=db/schema.sqltelemetry.shackdesk.com — CNAME record in the Cloudflare zone, proxied (orange cloud).
Wrangler registers the route automatically on deploy via wrangler.toml. If the route ever disappears, redeploy: wrangler deploy.
Rate limiting is not in code — it is configured in the Cloudflare dashboard.
Location: Cloudflare Dashboard → shackdesk.com zone → Security → WAF → Rate Limiting Rules
Free tier limit: 1 rule per account (not per zone). The current rule is scoped to the
/report path. If additional Workers or routes are added in the future, consider updating
the rule expression to match the entire telemetry.shackdesk.com hostname instead:
- Broader expression:
http.host eq "telemetry.shackdesk.com"
This covers all current and future routes on that subdomain without needing additional rules.
If Workers on other subdomains also need rate limiting, combine them with or in the same
rule, or upgrade to Pro (5 rules).
Current rule:
- Expression:
http.request.uri.path eq "/report" - Threshold: 10 requests per 10 seconds per IP (free tier minimum window)
- Action: Block for 10 seconds