Industrial and IoT: when the action is physical
Track: Industrial and IoT · In the room: pre-assigned teams · Seed use case: access-anomaly triage, or heat-pump fleet optimisation on a schedule.
Who this is for
Two industrial-IoT archetypes, both with little to no existing Cloudflare developer-platform footprint: access-control vendors (millions of installed endpoints, where the failure modes are a door opening for the wrong person or refusing the right one), and connected heat-pump manufacturers (fleets in Nordic homes, where "optimisation" means someone's house temperature and electricity bill). Both already have the hard part — a fleet reporting state — but nothing that watches it continuously and is trusted to act. The shared constraint that makes this track the most interesting in the room: in your industries, the agent's action is physical. Everyone else argues about a refund or a ticket; you're arguing about a lock and a radiator, which raises the bar on the approval gate and makes the demo unforgettable.
Invented sites, device ids, and occupants — access logs and in-home telemetry are both personal data. Nothing real goes into a shared account today.
Three agent use cases
1. Fleet-anomaly triage — the seed use case
Same architecture, two skins. Pick whichever fleet you know.
Doors: an access-control anomaly — a credential used at two sites 40km apart within ten minutes, a door held open eleven minutes at 03:00, a badge working hours it never worked before.
Heat pumps: a compressor running far longer than its neighbours for the same indoor temperature, a defrost cycle firing twice as often as its cohort, a house losing heat faster than its own history says it should.
- Trigger. A scheduled sweep over the fleet — nothing pushed, the agent goes looking.
- Decides. Whether a reading is abnormal for this site (a warehouse door legitimately stays open at shift change; open windows in April aren't a broken heat pump), how urgent, and which intervention fits.
- Tools.
assess_deviceproduces a verdict against the site's baseline;propose_interventiondrafts the specific change or action. - Human in the loop? Yes, on anything physical, no exceptions. Flagging, ranking and notifying are free. Revoking a credential can strand a night-shift worker outside in February; changing a heat curve changes someone's home temperature. Confident isn't the same as allowed to act.
2. Installation-quality reviewer
- Trigger. A device comes online for the first time, or reports commissioning values.
- Decides. Whether the install looks correct — plausible sensor ranges, configuration consistent with the building/property type, no swapped sensors, no default values left in place.
- Tool.
review_commissioningreturns a pass, a warning with the suspect values, or a fail, and drafts a note to the installer. - Human in the loop? Yes, the installer's decision. The agent can't see the site — it can say "these two sensor readings look transposed," not whether that's true. Catching a bad install in week one instead of at the first winter callout is most of this track's value.
3. Service-window planner
- Trigger. A weekly sweep of devices flagged as degrading but not failed.
- Decides. Which devices to visit together — geography, likely part, technician skill — turning individual faults into a route.
- Tool.
plan_routegroups flagged devices into a proposed visit schedule with reasoning per group. - Human in the loop? Yes, a plan the dispatcher edits. Nobody dispatches a van on an agent's word, but everyone would rather edit a plan than build one.
The 60-minute cut
Build use case 1: a scheduled sweep, a per-site baseline, and an intervention that won't fire without a human.
In scope:
- Eight synthetic devices in an array — site id, device type, short reading history. One clearly anomalous, one anomalous-looking but explainable by its own history, six normal.
- A scheduled sweep via the Agents SDK
this.schedule(), every 60 seconds so it fires on stage (say "production would be every 15 minutes, or hourly"). - A rolling per-site baseline in the agent's own storage — the sweep compares each reading against that site's own normal, not a global constant. This is what makes it an agent, not an alert threshold.
- A Workflow that pauses on the intervention, proceeding only on an explicit human event.
- A route that would perform the physical action and refuses loudly without approval.
Out of scope, and say so: device connectivity, MQTT, real protocols, authentication, timezones, machine learning. A mean and standard deviation over recent readings is an honest enough baseline for sixty minutes.
The demo that wins. Let the sweep find the anomaly on its own while you're still talking, then try to apply the intervention and let your system refuse you. Approve it and show the record of who approved what and when — for this audience, that record is the product.
Primitives — exactly three
- Schedules (
this.schedule()on the Agents SDKAgentclass). Fleet problems are found by sweeping, not by being notified — a heat pump working too hard never raises an error. Scheduling inside the agent keeps the sweep next to the baseline it needs, so detection and memory never drift apart. - Persistent Durable Object state. An anomaly is relative to normal, and normal is per
site. The agent's embedded SQLite (
this.sql) holds the rolling baseline — updated every sweep, strongly consistent, hibernating between sweeps so an idle fleet costs nothing. - Workflows. A physical intervention must be exactly-once and survive waiting for a
person.
step.waitForEvent()holds the run open for hours at no cost;step.do()checkpointing stops a retry double-revoking a credential or double-applying a setpoint change — and it's your audit trail.
Skip Browser Rendering (nothing to render) and AI Search — you're reasoning over numeric device state, not documents. AI Search becomes the right third primitive the moment you want the agent to cite an installation manual (use case 2).
Paste-ready starter prompt
Scaffold the starter first:
npm create cloudflare@latest -- team-industrial-fleet-triage --template cloudflare/agents-starter
cd team-industrial-fleet-triage
Then paste this into your coding agent:
I'm working in a fresh clone of Cloudflare's agents-starter template. The agent code is in
src/server.ts and the React client is in src/client.tsx. I have 60 minutes. Keep the
existing chat agent working — extend it, don't replace it.
Build a fleet-anomaly triage agent for connected industrial devices, where every physical
action requires human approval.
1. Add a hardcoded array of 8 synthetic devices to src/server.ts. Each has: deviceId, siteId,
deviceType, and a readings array of 10 entries with a timestamp and a numeric value. Use
heat-pump compressor runtime minutes per hour as the metric. Make device 3 clearly
anomalous against its own site history, make device 6 look high in absolute terms but
normal for its own site, and leave the rest normal. Invented data only.
2. On every sweep, maintain a rolling baseline per siteId in the Agent's SQLite storage using
this.sql: the mean and standard deviation of that site's recent readings. Anomaly detection
must compare a reading against ITS OWN SITE's baseline, never against a global constant.
3. Use this.schedule() to run the sweep every 60 seconds. For each device whose latest reading
is more than 2 standard deviations from its site baseline, call a tool called assess_device.
4. assess_device uses a Workers AI model and returns JSON: isAnomaly (boolean), severity (low,
medium, high), explanation (two sentences that state the site baseline and this reading),
intervention (one of none, notify_owner, adjust_setpoint, dispatch_technician), and
requiresApproval (boolean — true for anything that changes device behaviour). Store the
verdict keyed by deviceId, updating rather than duplicating on re-assessment.
5. Add a Cloudflare Workflow called Intervention in a new file, with steps as step.do():
a) prepare — record the proposed intervention and the reasoning
b) await_approval — step.waitForEvent() with a generous timeout, waiting for an event of
type "intervention-decision". This step must NOT be skippable for any intervention
where requiresApproval is true.
c) apply — record the applied change, who approved it, and when
d) notify — log a message to the site owner (do not actually send anything)
Import WorkflowEntrypoint, WorkflowStep and WorkflowEvent from "cloudflare:workers", and
add the workflow binding to wrangler.jsonc.
6. Add a POST /apply/:deviceId route that represents physically changing the device setpoint.
It must REFUSE with HTTP 403 and a clear JSON explanation unless the intervention for that
device has an approved decision recorded. Log every attempt, approved or refused. Put a
comment above it explaining this is a deliberate human approval gate because the action
changes the temperature in someone's home.
7. Add routes:
GET /fleet HTML page: every device, its site baseline, latest reading,
verdict, proposed intervention, and approval state. Show
PENDING APPROVAL prominently.
POST /decide/:deviceId sends the "intervention-decision" event with approved
true or false, and an approver name from the body
Requirements: TypeScript. Route every Workers AI call through AI Gateway by passing
{ gateway: { id: "agenthack" } } as the options argument. Run `npx wrangler types` after
changing wrangler.jsonc. Then deploy with `npx wrangler deploy` and give me curl commands
that prove /apply refuses before approval and succeeds after it.
The Worker name team-industrial-fleet-triage follows the shared-account rule
<team_prefix>-<slug>. Eight teams deploy into one account today, so if the Scoping agent
issued you a different prefix, use that one instead.
What "production" looks like
- Addressable identity per site, not per fleet. One agent instance per site or installation, hibernating between sweeps, keeps the baseline next to the device it describes and scales to a million units without a shared hot table — and makes deletion trivial when someone asks you to remove their home's data.
- Approval gate carries an authenticated identity and a reason code. Put your IdP in front of the decision endpoint so every physical change is attributable to a named person who saw the reasoning. Track override rate by intervention type — that ratio tells you which actions are ready for further automation.
- Widen the baseline from statistical to contextual. Weather, occupancy, tariff prices, shift patterns — every signal removes a class of false positive, and false positives are what get this system switched off inside your organisation, regardless of how good the architecture is.
Next: Level Up explains each primitive, the cheat sheet has the snippets.