A real-time webhook dashboard for Setup Manager - monitor macOS device enrollments as they happen.
Built with React, shadcn/ui, and Cloudflare Workers. Deploys in minutes. Webhooks are protected with a required secret token; the dashboard can optionally be protected with Cloudflare Access.
| Dark Mode | Light Mode |
|---|---|
![]() |
![]() |
Setup Manager sends webhook events during macOS device provisioning. This dashboard:
- Shows enrollments in real-time via WebSocket - no refresh needed
- Tracks KPIs - total enrollments, completion rate, average duration, failed actions
- Displays event details - device info, macOS version, enrollment actions, timing
- Charts trends - events over time, actions breakdown
- Filters and searches - by event type, model, macOS version, text search
- Works in light and dark mode
- Can be secured by Cloudflare Access - only authorized users can view the dashboard; the webhook endpoint stays open for devices
You do not have to fork this repo! You can deploy it directly to your Cloudflare account.
Click the deploy button above. It will:
- Fork this repo to your GitHub account
- Set up a GitHub Actions workflow
- Deploy to your Cloudflare account
Tip: During setup, you'll be asked for a project name. This becomes your Worker URL (
<project-name>.<your-subdomain>.workers.dev). You can name it anything you like —setupmanagerhud,enrollment-dashboard, or even something obscure likex7k9-internal. A less obvious name makes the URL harder to guess, which is fine as long as it's a valid URL (lowercase letters, numbers, and hyphens).
After clicking Deploy, you'll need to:
- Create a KV namespace and bind it to your Worker (see KV Namespace) — no CLI needed, this is done entirely in the Cloudflare dashboard
- Set a
WEBHOOK_TOKENsecret on your Worker (see Webhook Token) - Optionally secure the dashboard with Cloudflare Access
Prerequisites:
- Node.js 20 or later
- A Cloudflare account (free tier works)
- Wrangler CLI (
npm install -g wrangler)
# 1. Clone the repo
git clone https://github.com/motionbug/setupmanagerhud.git
cd setupmanagerhud
# 2. Install dependencies
npm install
# 3. Log in to Cloudflare
npx wrangler login
# 4. Create the KV namespace for event storage
npx wrangler kv namespace create WEBHOOKS
# -> Copy the ID from the output
# 5. Paste the KV namespace ID into wrangler.toml:
# Uncomment the [[kv_namespaces]] section and set:
# id = "your-namespace-id-here"
# 6. Set the webhook token secret
npx wrangler secret put WEBHOOK_TOKEN
# 7. Deploy
npm run deployYour dashboard is now live at https://setupmanagerhud.<your-subdomain>.workers.dev
Next step: Configure Setup Manager to send your WEBHOOK_TOKEN. You can also secure the dashboard with Cloudflare Access so only authorized users can view it.
Note: If you used the Deploy Button (Option 1), this is already set up for you. This section is for manual forks or if you need to reconfigure the workflow.
This repo includes a GitHub Actions workflow that builds and deploys to Cloudflare Workers. It runs manually from the Actions tab — useful if you prefer deploying from GitHub instead of the command line.
GitHub Actions needs permission to deploy to your Cloudflare account. This is done through two repository secrets:
- Fork this repo
- Create a Cloudflare API token — this is what allows GitHub to deploy on your behalf:
- Go to Cloudflare API Tokens
- Click Create Token
- Use the Edit Cloudflare Workers template
- Save the generated token
- Find your Cloudflare Account ID:
- Go to the Cloudflare dashboard
- Your Account ID is shown in the right sidebar on the overview page
- Add both as repository secrets in your fork:
- Go to Settings → Secrets and variables → Actions
- Add
CLOUDFLARE_API_TOKENwith the token from step 2 - Add
CLOUDFLARE_ACCOUNT_IDwith the ID from step 3
- Create your KV namespace and bind it to your Worker (see KV Namespace — use Option B for CLI)
- Set
WEBHOOK_TOKENas a Worker secret with Wrangler or in the Cloudflare dashboard - Go to the Actions tab in your fork, select Deploy to Cloudflare Workers, and click Run workflow
Setup Manager HUD has two separate security layers: a required webhook token for device POSTs, and optional Cloudflare Access protection for the dashboard.
Tip
Full security setup guide: Security covers webhook token configuration, Cloudflare Access setup, and rate limiting.
- Webhook token is required for
/webhook— configuration guide - Cloudflare Access is optional dashboard authentication — setup guide
- Rate limiting prevents abuse — WAF rules guide
Cloudflare Access is optional dashboard authentication. If you enable it, you can also have the Worker verify Cloudflare Access JWTs before serving dashboard, API, and WebSocket requests. This hardening step is separate from the required WEBHOOK_TOKEN; /webhook must bypass Access so Setup Manager devices can post events. When JWT validation is enabled, rejected dashboard/API requests return 403 from the Worker and still include the standard security headers.
To enable Worker-side JWT validation, create your Access application, find its audience tag and team domain, then add these values to wrangler.toml:
[vars]
CF_ACCESS_AUD = "paste-your-audience-tag-here"
CF_ACCESS_TEAM_DOMAIN = "your-team.cloudflareaccess.com"If you used the Deploy Button, Cloudflare created a copy of this repository in your GitHub account and deployed from that copy. Update wrangler.toml in that copied repo, then redeploy in one of two ways:
- Clone your copied repo locally, run
npm install, editwrangler.toml, then runnpx wrangler loginandnpm run deploy. - Edit
wrangler.tomlin GitHub, make sure Actions are enabled andCLOUDFLARE_API_TOKEN/CLOUDFLARE_ACCOUNT_IDsecrets exist, then run the deploy workflow.
See the wiki’s JWT validation section for the full walkthrough.
Setup Manager HUD requires a shared token for POST /webhook. Generate a long random value, set it as the Worker secret WEBHOOK_TOKEN, and configure Setup Manager to send the same value.
With Wrangler:
npx wrangler secret put WEBHOOK_TOKENOr in the Cloudflare dashboard, open your Worker, go to Settings → Variables and Secrets, add a secret named WEBHOOK_TOKEN, and redeploy if prompted.
Setup Manager sends this value in the Authorization header without the Bearer prefix. The Worker accepts both raw tokens and standard Bearer tokens so curl and other tools are easy to use.
Setup Manager HUD stores webhook events in Cloudflare Workers KV. You need to create a namespace and connect it to your Worker. Without this, the Worker will return a 500 error when receiving webhooks.
For Deploy Button users, the easiest path is the Cloudflare dashboard: create a KV namespace, then bind it to your Worker with the variable name WEBHOOKS.
For CLI or GitHub Actions deployments, create a namespace and add its ID to wrangler.toml:
npx wrangler kv namespace create WEBHOOKS[[kv_namespaces]]
binding = "WEBHOOKS"
id = "paste-your-id-here"See the wiki’s KV configuration guide for dashboard steps, CLI steps, and how redeploys affect bindings.
In your Setup Manager configuration, set the webhook URL to:
<key>webhooks</key>
<dict>
<key>started</key>
<string>https://setupmanagerhud.<your-subdomain>.workers.dev/webhook</string>
<key>finished</key>
<string>https://setupmanagerhud.<your-subdomain>.workers.dev/webhook</string>
</dict>
If either the started or finished key is missing, no webhook will be sent for that event.
Setup Manager will POST enrollment events to this endpoint. They'll appear on the dashboard in real-time.
Note
Webhook requests require WEBHOOK_TOKEN. See Webhook Token Setup in the wiki.
You can test without Setup Manager by sending a sample webhook:
curl -X POST https://setupmanagerhud.<your-subdomain>.workers.dev/webhook \
-H "Content-Type: application/json" \
-H "Authorization: your-webhook-token" \
-d '{
"name": "Started",
"event": "com.jamf.setupmanager.started",
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",
"started": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",
"modelName": "MacBook Pro",
"modelIdentifier": "Mac15,3",
"macOSBuild": "24A335",
"macOSVersion": "15.0",
"serialNumber": "TESTSERIAL01",
"setupManagerVersion": "2.0.0"
}'Tip
Advanced configuration: See the Configuration wiki page for environment variables, wrangler.toml reference, and health check endpoints.
# Start the Vite dev server (frontend only, hot reload)
npm run dev
# Start the full Worker locally (with KV, Durable Objects, WebSocket)
npm run dev:workerFor local Worker development, create a .dev.vars file (see .dev.vars.example).
Note: Cloudflare Access is not active during local development. The dashboard is unprotected when running locally, but
/webhookstill requiresWEBHOOK_TOKEN.
After deploying, you can populate the dashboard with dummy data to verify everything is working.
The included test script generates 140 realistic webhook events (70 started + 70 finished) across 10 simulated devices, spread over the past 3 days. This gives the dashboard enough data to display KPIs, charts, and event details.
# Replace with your actual Worker URL
WORKER_URL=https://setupmanagerhud.<your-subdomain>.workers.dev \
WEBHOOK_TOKEN=your-token-here \
node scripts/send-dummy-events.jsThe script sends the token as Authorization: Bearer <token>. Setup Manager sends the same token as a raw Authorization header; both formats are accepted.
Once the script finishes, open the dashboard in your browser. You should see events appearing with device details, enrollment actions, and charts populated with data.
Dummy events use serial numbers starting with DUMMY and expire automatically after 90 days. You can remove them sooner from the Cloudflare KV dashboard by filtering for DUMMY in your WEBHOOKS namespace.
┌─── Cloudflare Access ───┐
│ (optional dashboard auth) │
└──────────┬───────────────┘
│
Authenticated dashboard requests
│
▼
┌─────────────────────────────────────────────────┐
│ Cloudflare Worker │
│ │
│ POST /webhook ──→ Validate ──→ Store in KV │
│ (bypasses Access) └──→ Broadcast via DO │
│ │
│ GET /ws ──→ Durable Object (WebSocket hub) │
│ ├── Send history on connect │
│ └── Broadcast new events live │
│ │
│ GET /api/events ──→ Read from KV │
│ GET /api/stats ──→ Aggregate from KV │
│ GET /api/health ──→ Check KV + DO status │
│ │
│ GET /* ──→ Serve React dashboard (static) │
└─────────────────────────────────────────────────┘
- Cloudflare Access - Optional authentication gate at the edge. Protects the dashboard when enabled;
/webhookstays reachable and is protected byWEBHOOK_TOKEN. - Cloudflare Workers - Serverless edge runtime, handles all HTTP and WebSocket traffic
- Durable Objects - WebSocket hub with hibernation for real-time event broadcasting
- Workers KV - Event storage with 90-day TTL
- React + shadcn/ui - Dashboard UI, built with Vite, served as static assets
| Problem | Likely Cause | Solution |
|---|---|---|
| Worker returns 500 error | KV namespace not bound | See KV setup |
| Dashboard shows no events | WebSocket not connecting | Check browser console for errors |
| Webhook returns 401 | Token mismatch | Verify WEBHOOK_TOKEN matches your Setup Manager config |
| Can't access dashboard after enabling Access | Cloudflare Access misconfigured | Check CF_ACCESS_AUD and CF_ACCESS_TEAM_DOMAIN |
For detailed fixes, see the Troubleshooting wiki. For token behavior, see Webhook Authentication Format.
Contributions welcome! Please open an issue first to discuss what you'd like to change.

