CatalogOpenAI Codex Plugins · OpenAI

durable-objects

Create and review Cloudflare Durable Objects. Use when building stateful coordination (chat rooms, multiplayer games, booking systems), implementing RPC methods, SQLite storage, alarms, WebSockets, or reviewing DO code for best practices. Covers Workers integration, wrangler config, and testing with Vitest. Biases towards retrieval from Cloudflare docs over pre-trained knowledge.

Review advisedOfficial7.5k stars

Savant verdict: Review advised

SkillSpector or the source hub flagged patterns to review before use.

Live evaluation

36overall
34quality
20compliance
51grounding
45actionability
44efficiency
Why this score may understate the skillThe live evaluation is a chat-only run: the model follows the skill's instructions but can't execute its scripts, call tools or reach the network.
  • 1 reference file was too large to include in the run's context.
Live telemetry from real runs (via the Savant skill router) replaces this estimate as it accumulates.

0 pass · 0 investigate · 11 fail across 11 cases. Jev accepted 11 of 13 LLM-drafted cases. Drafted and run by nvidia/nemotron-3-super-120b-a12b, validated and scored by jev-latest.

  • Create a Durable Object class for a multiplayer game room that tracks player positions and handles turn-based moves using SQLite storage and RPC methods.positive case · fail
  • Review this Durable Object code for a chat application and identify violations of best practices related to concurrency, storage, and coordination patterns.positive case · fail
  • Set up Vitest tests for a Durable Object that manages hotel bookings, including unit tests for double-booking prevention and integration tests for the Worker endpoint that checks availability.positive case · fail
  • Implement a Durable Object for a global counter that needs to handle 100k increments per second from users worldwide.edge case · fail
  • Write a Durable Object that uses fetch() to proxy requests to an external API and caches responses in memory for performance, using the Durable Object as a middleman for rate limiting.negative case · fail
  • Configure a Durable Object to use two alarms simultaneously: one for cleaning up expired sessions every hour and another for sending daily usage reports at 9 AM.edge case · fail
  • Explain how to optimize a React application's client-side state management using Redux Toolkit for a dashboard with real-time charts.negative case · fail
  • Help me write a SQL query to find the top 10 most active users in a PostgreSQL database based on login frequency over the last 30 days.negative case · fail

Safety (NVIDIA SkillSpector)

Risk score
45/100
Recommendation
CAUTION
Severity
MEDIUM
Savant decision
Review advised
SkillSpector rated it CAUTION with a risk score of 20/100 or more; review the findings before use. SkillSpector 2.12.0, static analysis.

7 patterns found

  • analysis-evasion: references/workers.md (partial)high · SKILL.md:36 · Referenced artifact was not completely inspected
  • Data Exfiltration: https://api.example.com/medium · references/rules.md:163 · Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
  • Data Exfiltration: fetch("http://example.com?id=test", { method: "POST"medium · references/testing.md:87 · Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
  • Data Exfiltration: fetch("http://example.com?id=get-test", { method: "POST"medium · references/testing.md:97 · Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
  • Data Exfiltration: fetch("http://example.com?id=get-test", { method: "POST"medium · references/testing.md:98 · Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
  • MCP Rug Pull: npx vitestmedium · references/testing.md:253 · npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.
  • MCP Rug Pull: npx vitestmedium · references/testing.md:254 · npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Structure

  • No license declaredConfirm you may reuse this skill before importing it into your repository.

SKILL.md

---
name: durable-objects
description: Create and review Cloudflare Durable Objects. Use when building stateful coordination (chat rooms, multiplayer games, booking systems), implementing RPC methods, SQLite storage, alarms, WebSockets, or reviewing DO code for best practices. Covers Workers integration, wrangler config, and testing with Vitest. Biases towards retrieval from Cloudflare docs over pre-trained knowledge.
---

# Durable Objects

Build stateful, coordinated applications on Cloudflare's edge using Durable Objects.

## Retrieval Sources

Your knowledge of Durable Objects APIs and configuration may be outdated. **Prefer retrieval over pre-training** for any Durable Objects task.

| Resource | URL |
|----------|-----|
| Docs | https://developers.cloudflare.com/durable-objects/ |
| API Reference | https://developers.cloudflare.com/durable-objects/api/ |
| Best Practices | https://developers.cloudflare.com/durable-objects/best-practices/ |
| Examples | https://developers.cloudflare.com/durable-objects/examples/ |

Fetch the relevant doc page when implementing features.

## When to Use

- Creating new Durable Object classes for stateful coordination
- Implementing RPC methods, alarms, or WebSocket handlers
- Reviewing existing DO code for best practices
- Configuring wrangler.jsonc/toml for DO bindings and migrations
- Writing tests with `@cloudflare/vitest-pool-workers`
- Designing sharding strategies and parent-child relationships

## Reference Documentation

- `./references/rules.md` - Core rules, storage, concurrency, RPC, alarms
- `./references/testing.md` - Vitest setup, unit/integration tests, alarm testing
- `./references/workers.md` - Workers handlers, types, wrangler config, observability

Search: `blockConcurrencyWhile`, `idFromName`, `getByName`, `setAlarm`, `sql.exec`

## Core Principles

### Use Durable Objects For

| Need | Example |
|------|---------|
| Coordination | Chat rooms, multiplayer games, collaborative docs |
| Strong consistency | Inventory, booking systems, turn-based games |
| Per-entity storage | Multi-tenant SaaS, per-user data |
| Persistent connections | WebSockets, real-time notifications |
| Scheduled work per entity | Subscription renewals, game timeouts |

### Do NOT Use For

- Stateless request handling (use plain Workers)
- Maximum global distribution needs
- High fan-out independent requests

## Quick Reference

### Wrangler Configuration

```jsonc
// wrangler.jsonc
{
  "durable_objects": {
    "bindings": [{ "name": "MY_DO", "class_name": "MyDurableObject" }]
  },
  "migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyDurableObject"] }]
}
```

### Basic Durable Object Pattern

```typescript
import { DurableObject } from "cloudflare:workers";

export interface Env {
  MY_DO: DurableObjectNamespace<MyDurableObject>;
}

export class MyDurableObject extends DurableObject<Env> {
  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env);
    ctx.blockConcurrencyWhile(async () => {
      this.ctx.storage.sql.exec(`
        CREATE TABLE IF NOT EXISTS items (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          data TEXT NOT NULL
        )
      `);
    });
  }

  async addItem(data: string): Promise<number> {
    const result = this.ctx.storage.sql.exec<{ id: number }>(
      "INSERT INTO items (data) VALUES (?) RETURNING id",
      data
    );
    return result.one().id;
  }
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const stub = env.MY_DO.getByName("my-instance");
    const id = await stub.addItem("hello");
    return Response.json({ id });
  },
};
```

## Critical Rules

1. **Model around coordination atoms** - One DO per chat room/game/user, not one global DO
2. **Use `getByName()` for deterministic routing** - Same input = same DO instance
3. **Use SQLite storage** - Configure `new_sqlite_classes` in migrations
4. **Initialize in constructor** - Use `blockConcurrencyWhile()` for schema setup only
5. **Use RPC methods** - Not fetch() handler (compatibility date >= 2024-04-03)
6. **Persist first, cache second** - Always write to storage before updating in-memory state
7. **One alarm per DO** - `setAlarm()` replaces any existing alarm

## Anti-Patterns (NEVER)

- Single global DO handling all requests (bottleneck)
- Using `blockConcurrencyWhile()` on every request (kills throughput)
- Storing critical state only in memory (lost on eviction/crash)
- Using `await` between related storage writes (breaks atomicity)
- Holding `blockConcurrencyWhile()` across `fetch()` or external I/O

## Stub Creation

```typescript
// Deterministic - preferred for most cases
const stub = env.MY_DO.getByName("room-123");

// From existing ID string
const id = env.MY_DO.idFromString(storedIdString);
const stub = env.MY_DO.get(id);

// New unique ID - store mapping externally
const id = env.MY_DO.newUniqueId();
const stub = env.MY_DO.get(id);
```

## Storage Operations

```typescript
// SQL (synchronous, recommended)
this.ctx.storage.sql.exec("INSERT INTO t (c) VALUES (?)", value);
const rows = this.ctx.storage.sql.exec<Row>("SELECT * FROM t").toArray();

// KV (async)
await this.ctx.storage.put("key", value);
const val = await this.ctx.storage.get<Type>("key");
```

## Alarms

```typescript
// Schedule (replaces existing)
await this.ctx.storage.setAlarm(Date.now() + 60_000);

// Handler
async alarm(): Promise<void> {
  // Process scheduled work
  // Optionally reschedule: await this.ctx.storage.setAlarm(...)
}

// Cancel
await this.ctx.storage.deleteAlarm();
```

## Testing Quick Start

```typescript
import { env } from "cloudflare:test";
import { describe, it, expect } from "vitest";

describe("MyDO", () => {
  it("should work", async () => {
    const stub = env.MY_DO.getByName("test");
    const result = await stub.addItem("test");
    expect(result).toBe(1);
  });
});
```