SIRT Triage Agent Workshop
~15 min

Per-incident state with Durable Objects

Add a SIRTSession Durable Object so each incident has persistent state that survives page reloads.

Steps0 / 4
~15 min
  1. Uncomment the Durable Object binding in wrangler.jsonc

    Uncomment both the `durable_objects` and `migrations` blocks in `wrangler.jsonc`.

  2. Change STATE to state-3-do in wrangler.jsonc

    Change STATE from `state-2-d1` to `state-3-do`.

  3. Redeploy the app

    Run `npx wrangler deploy`. The new Durable Object class will be created automatically.

  4. Open an incident and verify state persists

    Click an incident in the queue, reload the page, and confirm the incident's state (open/investigating) is preserved.

What are Durable Objects?

Durable Objects are single-instance objects that live on Cloudflare’s network. Each Durable Object has:

  • Global uniqueness — There is exactly one instance of a Durable Object with a given ID, running in one location worldwide. All requests for that ID route to the same instance.
  • Persistent storage — Each Durable Object has its own private SQLite database that persists automatically. You never call “save” — writes are durable immediately.
  • In-memory state — Between requests, the Durable Object stays alive in memory, so you get fast reads without hitting storage.

Think of a Durable Object as a tiny, always-available server dedicated to one entity.

Why Durable Objects for incident state?

In lesson 06, your incident queue reads from D1 — but D1 stores the initial alert data. As an analyst investigates an incident, you need to track session state that evolves over time:

  • Current status (new → investigating → triaged → closed)
  • When the investigation started
  • The analyst’s scratchpad notes
  • Sub-agent results as they arrive
  • The triage card (once synthesized)
  • The action plan (once generated)

Each incident needs its own isolated state container that:

  • Survives page reloads
  • Is globally consistent (no stale reads)
  • Can hold structured data without a shared schema

A SIRTSession Durable Object is the perfect fit. Each incident gets its own DO instance identified by the incident_id. When you click an incident in the queue, the app routes to that incident’s SIRTSession DO — and all state for that investigation lives there.

SIRTSession Durable Object lifecycle

stateDiagram-v2
direction LR

state "Routing" as routing {
  [*] --> Worker
  Worker --> idFromName: idFromName(incidentId)
  idFromName --> DO: unique DO instance
}

state "Per-Incident DO" as lifecycle {
  [*] --> new
  new --> investigating: analyst opens
  investigating --> triaged: analysis complete
  triaged --> closed: action taken
  closed --> [*]
}

routing --> lifecycle

Step 1: Uncomment the Durable Object binding

Open sirt-workshop-app/wrangler.jsonc and find the commented-out Durable Object configuration. You need to uncomment two blocks:

The durable_objects bindings block:

"durable_objects": {
  "bindings": [
    {
      "name": "SIRT_SESSION",
      "class_name": "SIRTSession"
    }
  ]
}

The migrations block (required for Durable Object creation):

"migrations": [
  {
    "tag": "v1",
    "new_sqlite_classes": ["SIRTSession"]
  }
]

The new_sqlite_classes entry tells Cloudflare to provision a SQLite-backed Durable Object class called SIRTSession. This is what gives each DO instance its own database.

Step 2: Change STATE to state-3-do

In the same wrangler.jsonc file, update the STATE variable:

"vars": {
  "STATE": "state-3-do"
}

This activates the Durable Object integration in the app. When STATE is state-3-do, clicking an incident in the queue routes the request to a SIRTSession Durable Object instance. The DO stores and retrieves session state, making the investigation view stateful.

Step 3: Redeploy

Deploy the updated configuration:

npx wrangler deploy

When wrangler sees the new durable_objects and migrations blocks, it automatically creates the SIRTSession Durable Object class in your account. You don’t need to run any separate migration command — wrangler handles it during deployment.

Step 4: Verify persistent state

Now test that state persists across page reloads:

  1. Open your app in the browser at your *.workers.dev URL.
  2. Click on any incident in the queue — for example, “Malware + C2 Beacon.”
  3. You should see the incident detail view with the status showing as “new” or “open.”
  4. If there’s a button to change the status (like “Start Investigation”), click it to transition the status to “investigating.”
  5. Reload the page (Ctrl+R / Cmd+R).
  6. The incident should still show the “investigating” status — not reset back to “new.”

That’s the Durable Object at work. Without it, page state would be lost on every reload. With it, each incident’s investigation state lives in a dedicated DO instance that persists indefinitely.

What’s happening architecturally

Here’s the request flow when you click an incident:

Browser → Worker → SIRT_SESSION DO (incident_id)

                         ├── Reads/writes session state
                         ├── Stores: status, timestamps, scratchpad
                         └── Returns: current state to render
  1. Your browser makes a request to the Worker.
  2. The Worker looks up the SIRT_SESSION binding and calls env.SIRT_SESSION.get(incidentId) to get a stub for this incident’s Durable Object.
  3. The request is routed to the single global instance of that DO. If it doesn’t exist yet, Cloudflare creates it.
  4. The DO reads or writes state using its embedded SQLite storage.
  5. The response flows back through the Worker to the browser.

Because DOs are single-instance, there’s no risk of split-brain or stale reads. If two analysts open the same incident simultaneously, both requests route to the same DO instance.

What comes next

Right now, the SIRTSession DO stores basic state: status and timestamps. In lesson 08, you’ll add Workers AI so the “Investigate” button actually calls an LLM. In lessons 10–12, the DO becomes the backbone of the full agent harness — the Coordinator dispatches sub-agents, and their results are stored in the DO’s SQLite database as they arrive.

Knowledge check