This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
backendcritical PriorityID: bun-native-websocket-backpressure-drain-standard

Bun 1.2 Native WebSocket Backpressure Drain & Queue Shedding Standard

Enforces strict backpressure handling using Bun 1.2's native WebSocket `getBufferedAmount()`, send return byte tracking, and the `drain` lifecycle hook. Mandates deterministic socket buffer thresholds and proactive shedding via connection termination or message dropping to prevent unbounded V8/JSC heap growth during large-scale enterprise broadcast bursts.

Prohibited Anti-Pattern: Blind ws.send() Broadcasting and Missing Drain Handlers Causing Unchecked Buffer Saturation and Out-of-Memory Crashes

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Bun 1.2 Native WebSocket Backpressure Drain & Queue Shedding Standard - Enforces strict backpressure handling using Bun 1.2's native WebSocket `getBufferedAmount()`, send return byte tracking, and the `drain` lifecycle hook. Mandates deterministic socket buffer thresholds and proactive shedding via connection termination or message dropping to prevent unbounded V8/JSC heap growth during large-scale enterprise broadcast bursts.
globs: src/**/*.ts, server/**/*.ts, src/websockets/**/*.ts
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/bun-native-websocket-backpressure-drain-standard"
---

# Bun 1.2 Native WebSocket Backpressure Drain & Queue Shedding Standard

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: Blind ws.send() Broadcasting and Missing Drain Handlers Causing Unchecked Buffer Saturation and Out-of-Memory Crashes

## System Constraints
You are a Principal Backend Engineer enforcing strict kernel and application-level backpressure management across all Bun 1.2 Native WebSocket implementations. You must verify that `ws.send()` byte return values and `ws.getBufferedAmount()` are checked before dispatching high-throughput messages, that the `drain(ws)` lifecycle hook is registered on the server definition to resume paused producers, and that slow consumers exceeding defined byte saturation thresholds (e.g., 1MB) are proactively dropped or disconnected to guarantee zero-heap-exhaustion invariants.

## Strict Guidelines
- Engineers MUST ALWAYS inspect the numeric return value of `ws.send(payload)` or query `ws.getBufferedAmount()` prior to enqueuing further frames.
- Engineers MUST ALWAYS implement the native `drain(ws)` callback within the `Bun.serve({ websocket: { ... } })` configuration to process throttled queues or unpause reactive streams.
- Engineers MUST NOT synchronously iterate over a set of connected WebSockets and call `send()` unconditionally without evaluating consumer buffer levels.
- Services MUST establish a strict maximum buffered bytes ceiling (e.g., `MAX_BUFFERED_BYTES = 1024 * 1024`) per client socket; connections exceeding this threshold MUST be actively terminated with code 1008 (Policy Violation) to protect process memory.
- High-throughput broadcast mechanisms MUST shed non-critical state or enqueue messages into bounded per-client ring buffers when `ws.getBufferedAmount() > BACKPRESSURE_THRESHOLD` instead of growing dynamic arrays indefinitely.

## Bad vs Good Implementations
### Unbounded Broadcast Loop vs. Bounded Backpressure Drain with Queue Shedding

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: The broadcast loop ignores `ws.send()` return codes and client buffer states (`ws.getBufferedAmount()`). Under high-throughput bursts, slow consumers accumulate massive native/JSC-allocated buffers, precipitating an unrecoverable out-of-memory crash. Additionally, the WebSocket configuration lacks a `drain` handler.
```typescript
import type { ServerWebSocket } from 'bun';

interface ClientData {
  id: string;
}

const clients = new Set<ServerWebSocket<ClientData>>();

// ANTI-PATTERN: Blindly sending to all clients during broadcast events
// When clients on slow networks fall behind, Bun/uWebSockets buffers
// frames in memory indefinitely, causing process OOM during message spikes.
export function broadcastMetric(data: string) {
  for (const ws of clients) {
    ws.send(data); // Return value ignored; no buffer threshold check
  }
}

Bun.serve<ClientData>({
  port: 3000,
  websocket: {
    open(ws) {
      clients.add(ws);
    },
    message(ws, msg) {},
    close(ws) {
      clients.delete(ws);
    }
    // Missing drain handler to resume throttled transmission
  },
  fetch(req, server) {
    server.upgrade(req, { data: { id: crypto.randomUUID() } });
    return new Response('Upgrade failed', { status: 400 });
  }
});
```

#### Production Standard (Correct)
> **Standard**: Adheres to backpressure invariants by inspecting `ws.getBufferedAmount()`, applying an upper threshold (1MB) to disconnect unrecoverable slow consumers, maintaining a bounded ring-buffer for soft backpressure events, and implementing the native Bun `drain` callback to deterministically flush enqueued state as kernel buffers clear.
```typescript
import type { ServerWebSocket } from 'bun';

interface SocketState {
  id: string;
  isPaused: boolean;
  queue: string[];
}

const MAX_BUFFERED_BYTES = 1024 * 1024; // 1MB absolute cap
const BACKPRESSURE_HIGH_WATERMARK = 256 * 1024; // 256KB pause threshold
const MAX_OFFLINE_QUEUE_LEN = 100;

const clients = new Set<ServerWebSocket<SocketState>>();

export function broadcastMetric(data: string) {
  for (const ws of clients) {
    const buffered = ws.getBufferedAmount();

    if (buffered >= MAX_BUFFERED_BYTES) {
      // Hard breach: client is stalled or malicious. Terminate to protect cluster memory.
      ws.close(1008, 'Buffer overflow: backpressure ceiling exceeded');
      clients.delete(ws);
      continue;
    }

    if (buffered >= BACKPRESSURE_HIGH_WATERMARK || ws.data.isPaused) {
      // Soft breach: shed least-recent frames or enqueue to a bounded ring-buffer
      if (ws.data.queue.length >= MAX_OFFLINE_QUEUE_LEN) {
        ws.data.queue.shift(); // Evict oldest payload
      }
      ws.data.queue.push(data);
      ws.data.isPaused = true;
      continue;
    }

    // ws.send returns -1 if dropped, 0 if queued, or bytes sent > 0
    const sendResult = ws.send(data);
    if (sendResult <= 0 && ws.getBufferedAmount() >= BACKPRESSURE_HIGH_WATERMARK) {
      ws.data.isPaused = true;
    }
  }
}

Bun.serve<SocketState>({
  port: 3000,
  websocket: {
    open(ws) {
      clients.add(ws);
    },
    message(ws, msg) {
      // Handle client messages
    },
    drain(ws) {
      // Native lifecycle hook triggered when socket buffer drains
      while (ws.data.queue.length > 0) {
        if (ws.getBufferedAmount() >= BACKPRESSURE_HIGH_WATERMARK) {
          return; // Remain paused
        }
        const nextPayload = ws.data.queue.shift()!;
        ws.send(nextPayload);
      }
      ws.data.isPaused = false;
    },
    close(ws) {
      clients.delete(ws);
    }
  },
  fetch(req, server) {
    server.upgrade(req, {
      data: { id: crypto.randomUUID(), isPaused: false, queue: [] }
    });
    return new Response('Upgrade failed', { status: 400 });
  }
});
```

Recommended MCP Servers (mcp-serverlist.com)

Equip agent with system tools to verify code for this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Bun 1.2 Native WebSocket Backpressure Drain & Queue Shedding Standard

Bun 1.2 Native WebSocket Backpressure Drain & Queue Shedding Standard defines an industrial-grade behavioral standard for backend engineering. Enforces strict backpressure handling using Bun 1.2's native WebSocket `getBufferedAmount()`, send return byte tracking, and the `drain` lifecycle hook. Mandates deterministic socket buffer thresholds and proactive shedding via connection termination or message dropping to prevent unbounded V8/JSC heap growth during large-scale enterprise broadcast bursts. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: Blind ws.send() Broadcasting and Missing Drain Handlers Causing Unchecked Buffer Saturation and Out-of-Memory Crashes Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftBlind ws.send() Broadcasting and Missing Drain Handlers Causing Unchecked Buffer Saturation and Out-of-Memory CrashesCritical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Engineers MUST ALWAYS inspect the numeric return value of `ws.send(payload)` or query `ws.getBufferedAmount()` prior to enqueuing further frames.

Engineers MUST ALWAYS inspect the numeric return value of `ws.send(payload)` or query `ws.getBufferedAmount()` prior to enqueuing further frames.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Engineers MUST ALWAYS implement the native `drain(ws)` callback within the `Bun.serve({ websocket

Engineers MUST ALWAYS implement the native `drain(ws)` callback within the `Bun.serve({ websocket: { ... } })` configuration to process throttled queues or unpause reactive streams.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

Engineers MUST NOT synchronously iterate over a set of connected WebSockets and call `send()` unconditionally without evaluating consumer buffer levels.

Engineers MUST NOT synchronously iterate over a set of connected WebSockets and call `send()` unconditionally without evaluating consumer buffer levels.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

Services MUST establish a strict maximum buffered bytes ceiling (e.g., `MAX_BUFFERED_BYTES = 1024 * 1024`) per client socket

Services MUST establish a strict maximum buffered bytes ceiling (e.g., `MAX_BUFFERED_BYTES = 1024 * 1024`) per client socket; connections exceeding this threshold MUST be actively terminated with code 1008 (Policy Violation) to protect process memory.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
5

High-throughput broadcast mechanisms MUST shed non-critical state or enqueue messages into bounded per-client ring buffers when `ws.getBufferedAmount() > BACKPRESSURE_THRESHOLD` instead of growing dynamic arrays indefinitely.

High-throughput broadcast mechanisms MUST shed non-critical state or enqueue messages into bounded per-client ring buffers when `ws.getBufferedAmount() > BACKPRESSURE_THRESHOLD` instead of growing dynamic arrays indefinitely.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is Bun 1.2 Native WebSocket Backpressure Drain & Queue Shedding Standard critical for AI coding assistants?

Enforces strict backpressure handling using Bun 1.2's native WebSocket `getBufferedAmount()`, send return byte tracking, and the `drain` lifecycle hook. Mandates deterministic socket buffer thresholds and proactive shedding via connection termination or message dropping to prevent unbounded V8/JSC heap growth during large-scale enterprise broadcast bursts. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (Blind ws.send() Broadcasting and Missing Drain Handlers Causing Unchecked Buffer Saturation and Out-of-Memory Crashes) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

1 Rule in Stack

Bun