A Cloudflare Workers-hosted dashboard for Setup Manager that shows macOS enrollment events in real time.
Setup Manager sends webhook events during provisioning. This project stores those events in Workers KV, streams them over WebSockets, and presents them in a dashboard with KPIs, charts, and event history.
Setup Manager already emits useful lifecycle events. Setup Manager HUD gives you a simple way to:
- watch enrollments live without refreshing
- confirm that devices are starting and finishing as expected
- spot failed enrollment actions quickly
- review recent device activity, timings, and trends
This project is designed to run on Cloudflare Workers. A deployment is not complete until all of the following are true:
- the Worker is deployed
- a
WEBHOOKSKV binding exists - a
WEBHOOK_TOKENsecret exists - Setup Manager is configured to send that same token in
Authorization - Cloudflare Access is configured if the dashboard should not be public
There are two practical ways to deploy this project:
Deploy to Cloudflare Workersbutton Best for Jamf admins who want the fastest path to a working Cloudflare-hosted HUD.- Manual Wrangler deploy Best if you want to manage the repo and deployment from the command line.
There is also a GitHub Actions workflow, but that is an advanced maintenance option. It deploys code only. It does not create Worker runtime secrets or bindings for you.
Use the deploy button if you want the quickest Cloudflare-first setup.
What it does:
- forks this repo to your GitHub account
- creates a Cloudflare Worker in your account
- wires up a GitHub-based deployment flow for that fork
What it does not do:
- create the
WEBHOOKSKV binding - create the
WEBHOOK_TOKENsecret - configure Cloudflare Access
- configure Setup Manager
After the deploy button flow finishes, continue with the required setup checklist below.
Use this path if you prefer to deploy directly from the command line.
Prerequisites:
- Node.js 20 or later
- a Cloudflare account
- Wrangler CLI
git clone https://github.com/motionbug/setupmanagerhud.git
cd setupmanagerhud
npm install
npx wrangler login
npm run build
npx wrangler deployThis creates the Worker code deployment, but it is still not ready to receive Setup Manager events until you complete the checklist below.
Complete these steps in order after any first deployment.
This app stores webhook events in Workers KV. Without the binding, the Worker cannot persist events.
Cloudflare dashboard path:
- go to Workers & Pages -> KV
- create a namespace
- name it
WEBHOOKS - open your Worker
- go to Settings -> Bindings
- add a KV Namespace binding named exactly
WEBHOOKS - save and deploy
If you want the binding managed in code for repeatable deploys, use Wrangler and then commit the KV namespace ID into wrangler.toml:
npx wrangler kv namespace create WEBHOOKSThen uncomment and fill in the [[kv_namespaces]] section in wrangler.toml.
Generate a shared token that Setup Manager and the Worker will both use.
openssl rand -hex 24That produces a random token you can paste into Cloudflare and into your Setup Manager configuration.
The webhook endpoint is secure-by-default. It rejects requests unless the Worker has a secret named WEBHOOK_TOKEN.
Cloudflare dashboard path:
- open Workers & Pages
- open your Worker
- go to Settings -> Variables and Secrets
- add a Secret
- name it
WEBHOOK_TOKEN - paste the token you generated
- click Deploy
Wrangler option:
npx wrangler secret put WEBHOOK_TOKENSetup Manager must send the same token in the Authorization header. Use the token-authenticated dict format for each webhook.
<key>webhooks</key>
<dict>
<key>finished</key>
<dict>
<key>token</key>
<string>your-shared-webhook-token</string>
<key>url</key>
<string>https://your-worker.your-subdomain.workers.dev/webhook</string>
</dict>
<key>started</key>
<dict>
<key>token</key>
<string>your-shared-webhook-token</string>
<key>url</key>
<string>https://your-worker.your-subdomain.workers.dev/webhook</string>
</dict>
</dict>Behavior:
- Setup Manager sends
Authorization: <your-shared-webhook-token> - the Worker compares that value to
WEBHOOK_TOKEN - if they match, the event is accepted and stored
Webhook token auth protects /webhook. It does not protect the dashboard itself.
If the dashboard should not be public, configure Cloudflare Access for the Worker and set the Worker vars that enable JWT verification.
Cloudflare Access setup:
- enable Zero Trust in Cloudflare
- create an Access Application for your Worker hostname
- create an allow policy for the people who should view the dashboard
- create a bypass policy for
/webhook - place the bypass policy above the allow policy
Then configure these Worker vars:
[vars]
CF_ACCESS_AUD = "paste-your-audience-tag-here"
CF_ACCESS_TEAM_DOMAIN = "your-team.cloudflareaccess.com"These values are documented in wrangler.toml. If they are not set, the Worker will not enforce Cloudflare Access JWT validation.
Webhook token auth is the baseline requirement. Rate limiting is still useful as defense in depth.
Suggested Cloudflare WAF rule:
- path equals
/webhook - start around
30 requests per minute per IP - raise the threshold if many devices enroll behind the same NAT or VPN
Use these checks after deployment and configuration.
- correct token and valid payload ->
200 - missing token or wrong token ->
401 - missing
WEBHOOK_TOKENon the Worker ->503
curl -i -X POST https://your-worker.your-subdomain.workers.dev/webhook \
-H "Authorization: your-shared-webhook-token" \
-H "Content-Type: application/json" \
-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"
}'If Cloudflare Access is enabled:
- visiting
/should require login /ws,/api/events,/api/stats, and/api/healthshould not be publicly readable without Access/webhookshould remain reachable for Setup Manager, but only with the correct token
This repo includes a script that sends dummy webhook events so you can verify the dashboard end to end.
WORKER_URL=https://your-worker.your-subdomain.workers.dev \
WEBHOOK_TOKEN=your-shared-webhook-token \
node scripts/send-dummy-events.jsThe script sends realistic started and finished events so you can confirm that:
- the Worker accepts authenticated webhook traffic
- events appear in the dashboard
- charts and KPIs populate
If you want to remove test data, delete the matching entries from the WEBHOOKS KV namespace in the Cloudflare dashboard.
This repo includes .github/workflows/deploy.yml, which can deploy the Worker from a fork.
Use this option if you want a repeatable fork-based deploy workflow. It is not the simplest first-time setup path.
What GitHub Actions does:
- checks out the repo
- installs dependencies
- builds the frontend
- deploys the Worker using Wrangler
What GitHub Actions does not do:
- create the
WEBHOOKSKV binding - create
WEBHOOK_TOKEN - configure Cloudflare Access
- configure Setup Manager
To use it:
- fork this repo
- create a Cloudflare API token with Workers deploy access
- find your Cloudflare Account ID
- add these GitHub repository secrets to your fork:
CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_ID
- if you want KV managed by code, uncomment the
[[kv_namespaces]]block inwrangler.tomland commit the real namespace ID - run the workflow from the Actions tab
- still complete the same Worker setup checklist above
Important:
- GitHub repository secrets are for the GitHub workflow only
- they do not become Worker runtime secrets automatically
- you must still add
WEBHOOK_TOKENinside the Cloudflare Worker settings
For local frontend-only work:
npm run devFor local Worker development:
npm run dev:workerIf you run the Worker locally, create a .dev.vars file with at least:
WEBHOOK_TOKEN=your-local-test-tokenCloudflare Access is normally not active during local development.
The runtime flow is:
- Setup Manager sends a signed-by-token webhook to
/webhook - the Worker validates the token and payload
- the Worker stores the event in Workers KV
- the Durable Object broadcasts new events to connected dashboards
- the dashboard reads history and live updates from the Worker
Main platform pieces:
- Cloudflare Workers for HTTP routing and static asset serving
- Workers KV for event persistence
- Durable Objects for WebSocket fan-out
- Cloudflare Access for dashboard protection
- React for the UI
Contributions are welcome. Please open an issue first if you want to propose a significant change.
