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. Navigate to the lesson-07-do directory

    Run `cd ..\lesson-07-do` from your current lesson-06-d1 directory.

  2. Replace REPLACE_WITH_YOUR_DATABASE_ID and verify the DO binding in wrangler.jsonc

    Open `wrangler.jsonc`, replace `REPLACE_WITH_YOUR_DATABASE_ID` with your D1 database_id from lesson 06, and verify the `durable_objects` and `migrations` blocks are present.

  3. Redeploy the app

    Run `npm run 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.

OpenCode shortcut

Paste this prompt into the OpenCode TUI to let your AI agent walk you through this lesson:

 

Step 0: Navigate to the lesson directory

Open your terminal and navigate to the lesson-07-do directory:

cd ..\lesson-07-do

This directory contains a fresh copy of the app pre-configured for the Durable Objects lesson. The STATE variable is already set to state-3-do in wrangler.jsonc.

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: Verify the Durable Object binding

Open lesson-07-do\wrangler.jsonc and verify the Durable Object configuration. These two blocks should be present:

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: Redeploy

Deploy the updated configuration:

npm run deploy

When wrangler sees the 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 3: 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.

Key takeaways

Before moving on, make sure these ideas land. They are the reason this lesson matters in the larger triage system.

  1. 1

    Durable Objects provide one consistent instance per incident

    A Durable Object is a stateful coordination point with serialized access. That makes it a strong fit for per-incident session state that must not split across replicas.

  2. 2

    Deterministic IDs route related requests together

    Using idFromName with the incident ID means every request for that incident reaches the same Durable Object. Same name, same stateful instance.

  3. 3

    Stored state survives reloads and requests

    The incident status is persisted in the Durable Object's storage, not in the browser. That is what lets the console remember progress across page reloads.

Knowledge checkRequired to continue