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

Bun 1.2 Native WebSocket Backpressure and ArrayBuffer Recycling Invariant

Enforces strict native queue-depth validation via ws.getBufferedAmount() and pre-allocated ArrayBuffer pool recycling across Bun 1.2 native WebSocket servers. Unbounded ws.send() calls bypass native libuv/C++ queues, inducing hidden C++ heap memory leaks and GC thrashing. Mandates backpressure-aware draining cycles and deterministic ring/slab buffer reuse for zero-copy high-throughput framing.

Prohibited Anti-Pattern: Unchecked ws.send() Fire-and-Forget with On-The-Fly Buffer Allocations Inducing Native Memory Exhaustion

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Bun 1.2 Native WebSocket Backpressure and ArrayBuffer Recycling Invariant - Enforces strict native queue-depth validation via ws.getBufferedAmount() and pre-allocated ArrayBuffer pool recycling across Bun 1.2 native WebSocket servers. Unbounded ws.send() calls bypass native libuv/C++ queues, inducing hidden C++ heap memory leaks and GC thrashing. Mandates backpressure-aware draining cycles and deterministic ring/slab buffer reuse for zero-copy high-throughput framing.
globs: src/**/*.{ts,js}, server/**/*.{ts,js}
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/bun-websocket-backpressure-buffer-recycling"
---

# Bun 1.2 Native WebSocket Backpressure and ArrayBuffer Recycling Invariant

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: Unchecked ws.send() Fire-and-Forget with On-The-Fly Buffer Allocations Inducing Native Memory Exhaustion

## System Constraints
You are a Principal Systems Engineer enforcing Bun 1.2 Native WebSocket Backpressure and ArrayBuffer Recycling invariants. You must reject any WebSocket message handling that fails to evaluate backpressure before serialization, ignores `drain` events, or creates transient Buffer/Uint8Array instances per connection frame.

## Strict Guidelines
- Engineers MUST ALWAYS verify ws.getBufferedAmount() before dispatching payloads, checking against a deterministic high-water mark (e.g., 64KB or 1MB depending on channel SLOs).
- Engineers MUST NOT call ws.send() when ws.getBufferedAmount() exceeds the configured high-water mark, and MUST queue outgoing messages or shed unacknowledged transient updates.
- Engineers MUST implement the native `drain(ws)` handler in Bun.serve() to resume transmission and flush throttled client queues when native buffer pressure drops.
- Engineers MUST allocate re-usable ArrayBuffer pools or Uint8Array slabs at server initialization rather than instantiating `Buffer.from()` or `new Uint8Array()` inside connection or broadcast loops.
- Engineers MUST ensure binary frames use subarray views into pre-allocated memory pools and return pooled regions deterministically upon completion to prevent V8/JavaScriptCore heap churn.
- Services MUST drop or terminate slow-draining connections when queued byte counters exceed terminal thresholds (low-water / hard limits) to guard against slowloris exhaustion attacks.

## Bad vs Good Implementations
### Naive ws.send() broadcast with per-message Buffer allocation vs. pooled zero-copy drain management

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Creates transient Buffers for every frame causing GC pauses under high traffic. Calling ws.send without evaluating ws.getBufferedAmount() results in silent native buffer accumulation, leading to C++ heap exhaustion and sudden process termination under slow client conditions.
```typescript
import { serve } from "bun";

// ANTI-PATTERN: Dynamic buffer creation in hot loop and unmonitored backpressure
serve({
  port: 8080,
  websocket: {
    message(ws, message) {
      // Allocating fresh Buffer per message pushes heavy GC pressure
      const response = Buffer.from(JSON.stringify({ status: "ack", data: message }));
      
      // Unchecked send ignores C++ native queue growth under saturated network
      ws.send(response);
    },
    open(ws) {
      console.log("Client connected");
    }
  },
  fetch(req, server) {
    if (server.upgrade(req)) return;
    return new Response("Upgrade failed", { status: 400 });
  }
});
```

#### Production Standard (Correct)
> **Standard**: Implements native backpressure verification with high and terminal watermarks via ws.getBufferedAmount(), uses the native drain() callback to flush throttled queues, and eliminates V8 allocations by encoding payloads directly into a pre-allocated slab buffer.
```typescript
import { serve, type ServerWebSocket } from "bun";

interface ClientData {
  id: string;
  pendingQueue: Uint8Array[];
}

const HIGH_WATER_MARK = 1024 * 64; // 64 KiB
const HARD_DROP_MARK = 1024 * 512; // 512 KiB

// Pre-allocated static pool slab for standard frame serialization
const FRAME_POOL_SIZE = 1024 * 1024; // 1 MiB
const slab = new ArrayBuffer(FRAME_POOL_SIZE);
const pooledView = new Uint8Array(slab);

function sendWithBackpressure(ws: ServerWebSocket<ClientData>, chunk: Uint8Array): void {
  const buffered = ws.getBufferedAmount();
  
  if (buffered > HARD_DROP_MARK) {
    ws.close(1008, "Backpressure overflow");
    return;
  }

  if (buffered > HIGH_WATER_MARK || ws.data.pendingQueue.length > 0) {
    // Preserve data order by queuing when saturated
    ws.data.pendingQueue.push(chunk.slice());
    return;
  }

  ws.send(chunk);
}

serve<ClientData>({
  port: 8080,
  websocket: {
    open(ws) {
      ws.data = { id: crypto.randomUUID(), pendingQueue: [] };
    },
    message(ws, message) {
      // Zero-copy encode into pre-allocated slab segment
      const encoder = new TextEncoder();
      const { written } = encoder.encodeInto(
        JSON.stringify({ status: "ack", ts: Date.now() }),
        pooledView
      );
      const payloadView = pooledView.subarray(0, written);
      sendWithBackpressure(ws, payloadView);
    },
    drain(ws) {
      // Native callback triggered when native buffer clears
      while (ws.data.pendingQueue.length > 0 && ws.getBufferedAmount() < HIGH_WATER_MARK) {
        const next = ws.data.pendingQueue.shift();
        if (next) {
          ws.send(next);
        }
      }
    },
    close(ws) {
      ws.data.pendingQueue.length = 0;
    }
  },
  fetch(req, server) {
    if (server.upgrade(req, { data: { id: "", pendingQueue: [] } })) return;
    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 and ArrayBuffer Recycling Invariant

Bun 1.2 Native WebSocket Backpressure and ArrayBuffer Recycling Invariant defines an industrial-grade behavioral standard for backend engineering. Enforces strict native queue-depth validation via ws.getBufferedAmount() and pre-allocated ArrayBuffer pool recycling across Bun 1.2 native WebSocket servers. Unbounded ws.send() calls bypass native libuv/C++ queues, inducing hidden C++ heap memory leaks and GC thrashing. Mandates backpressure-aware draining cycles and deterministic ring/slab buffer reuse for zero-copy high-throughput framing. 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: Unchecked ws.send() Fire-and-Forget with On-The-Fly Buffer Allocations Inducing Native Memory Exhaustion 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 DriftUnchecked ws.send() Fire-and-Forget with On-The-Fly Buffer Allocations Inducing Native Memory ExhaustionCritical
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 verify ws.getBufferedAmount() before dispatching payloads, checking against a deterministic high-water mark (e.g., 64KB or 1MB depending on channel SLOs).

Engineers MUST ALWAYS verify ws.getBufferedAmount() before dispatching payloads, checking against a deterministic high-water mark (e.g., 64KB or 1MB depending on channel SLOs).

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 NOT call ws.send() when ws.getBufferedAmount() exceeds the configured high-water mark, and MUST queue outgoing messages or shed unacknowledged transient updates.

Engineers MUST NOT call ws.send() when ws.getBufferedAmount() exceeds the configured high-water mark, and MUST queue outgoing messages or shed unacknowledged transient updates.

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 implement the native `drain(ws)` handler in Bun.serve() to resume transmission and flush throttled client queues when native buffer pressure drops.

Engineers MUST implement the native `drain(ws)` handler in Bun.serve() to resume transmission and flush throttled client queues when native buffer pressure drops.

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

Engineers MUST allocate re-usable ArrayBuffer pools or Uint8Array slabs at server initialization rather than instantiating `Buffer.from()` or `new Uint8Array()` inside connection or broadcast loops.

Engineers MUST allocate re-usable ArrayBuffer pools or Uint8Array slabs at server initialization rather than instantiating `Buffer.from()` or `new Uint8Array()` inside connection or broadcast loops.

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

Engineers MUST ensure binary frames use subarray views into pre-allocated memory pools and return pooled regions deterministically upon completion to prevent V8/JavaScriptCore heap churn.

Engineers MUST ensure binary frames use subarray views into pre-allocated memory pools and return pooled regions deterministically upon completion to prevent V8/JavaScriptCore heap churn.

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.
6

Services MUST drop or terminate slow-draining connections when queued byte counters exceed terminal thresholds (low-water / hard limits) to guard against slowloris exhaustion attacks.

Services MUST drop or terminate slow-draining connections when queued byte counters exceed terminal thresholds (low-water / hard limits) to guard against slowloris exhaustion attacks.

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 and ArrayBuffer Recycling Invariant critical for AI coding assistants?

Enforces strict native queue-depth validation via ws.getBufferedAmount() and pre-allocated ArrayBuffer pool recycling across Bun 1.2 native WebSocket servers. Unbounded ws.send() calls bypass native libuv/C++ queues, inducing hidden C++ heap memory leaks and GC thrashing. Mandates backpressure-aware draining cycles and deterministic ring/slab buffer reuse for zero-copy high-throughput framing. 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 (Unchecked ws.send() Fire-and-Forget with On-The-Fly Buffer Allocations Inducing Native Memory Exhaustion) 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