JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 44
  • Score
    100M100P100Q98099F
  • License MIT

Zero-dependency TypeScript security filter for LLM streams — redact secrets, enforce tool policy, byte and event modes.

Package Exports

  • llm-stream-guard

Readme

llm-stream-guard

core node runtime deps tests ci status

Security filter for LLM streams — redact secrets and PII, enforce tool-call policy, sanitize errors. Works on raw bytes (TransformStream) and parsed event streams. Declarative JSON/YAML policies and a CLI for offline scans.

A standalone, zero-dependency TypeScript security filter for LLM proxy and agent pipelines. Byte mode: chunk-safe secret redaction on raw SSE. Event mode: tool allow/deny, arg blocking, PII & error sanitization on parsed streams. Policy files + llm-stream-guard scan for CI prep.

Status: Stable 0.3.0 — declarative policy loader, built-in profiles, and CLI (validate, resolve, scan, diff). Review CHANGELOG.md before upgrades.


Contents


Why stream guard?

When proxying or running agents, unsafe content leaks downstream in predictable ways:

  1. Secrets in text deltas — API keys, bearer tokens, JWTs echoed in model output.
  2. Dangerous tool args — shell injection, exfil URLs, oversized JSON before execution.
  3. Unauthorized tool names — models invoke tools outside your allowlist.
  4. Raw provider errors — internal URLs and stack traces forwarded to browsers.

Many filters scan raw bytes only and miss precise policy on assembled tool_call.done JSON. This library targets both byte and event modes with zero runtime dependencies.

Chunk redaction: secrets split across TCP reads

  • Mid-chunk splits — secrets split across TCP reads use a rolling buffer + prefix holdback (LSG-C).
  • Tool policy timing — evaluate names early; validate args on done when JSON is complete (LSG-T).
  • Violation modesblock, warn, or audit with onViolation for SIEM-friendly logs.

Two modes

Mode API When
Byte createByteGuard() Proxy forwards provider-shaped SSE without parsing
Event guardEvents() Parsed stream — assemble, AI SDK, or custom mapper

Byte mode vs event mode


Architecture

Raw upstream content enters through byte guard or event guard; composable rules redact or block before your proxy, UI, or tool executor sees output.

End-to-end pipeline

Optional pairing with llm-stream-assemble (parse → guard) — cookbook only, no npm coupling:

Ecosystem: optional assemble + guard

Lifecycle and concurrency

Create one GuardContext per stream — never share across concurrent requests. Stateless helpers (pipeGuard, internal transform pipeline) compose into stateful entry points.

GuardContext lifecycle

Diagram sources: docs/img/ (Mermaid .mmd + committed SVG). Regenerate with pnpm diagrams:build.


GuardEvent model

Independent event union — not StreamEvent, not provider types:

GuardEvent mindmap

Type Shape
text { type, phase: delta | done, text }
tool_call { type, phase, id?, name?, args?, argsText? }
reasoning { type, phase, text }
error { type, message, code? }
finish { type, reason? }

Full spec: docs/proposal.MD.


Violation modes

block / warn / audit

Mode Secrets / PII Tool policy
block Always redact Safe substitute + policy_violation finish
warn Always redact Block tool + onViolation
audit Always redact + onViolation on match Pass tool through + onViolation

Install

pnpm add llm-stream-guard
# or npm install llm-stream-guard

Requirements: Node.js 18+ · Bun / Deno / Workers (Web Streams)

Maintainers: run pnpm release:prep before tagging and npm publish. GitHub Release notes from CHANGELOG.md.


First success in 30 seconds

git clone git@github.com:01laky/llm-stream-guard.git
cd llm-stream-guard
pnpm install
./scripts/setup-githooks.sh
pnpm verify

Then pipe bytes through the byte guard:

import { createByteGuard } from "llm-stream-guard";

const guarded = sourceStream.pipeThrough(createByteGuard({ redactSecrets: true, mode: "warn" }));

Quickstart

Proxy (byte mode)

import { createByteGuard } from "llm-stream-guard";

return new Response(
    upstream.body!.pipeThrough(
        createByteGuard({ redactSecrets: true, sanitizeErrors: true, mode: "warn" }),
    ),
    { headers: { "Content-Type": "text/event-stream" } },
);

redactSecrets and sanitizeErrors are active on createByteGuard() options.

Agent (event mode)

import {
    allowTools,
    blockToolArgs,
    guardEvents,
    redactSecrets,
    sanitizeErrors,
} from "llm-stream-guard";

for await (const event of guardEvents(
    parsedEvents,
    { mode: "block", onViolation: (v) => console.warn(v.rule, v.message) },
    redactSecrets(),
    allowTools(["search", "read_file"]),
    blockToolArgs(/rm\s+-rf/),
    sanitizeErrors(),
)) {
    if (event.type === "tool_call" && event.phase === "done") {
        await executeTool(event);
    }
}

Transform ordering

Recommended pipeline:

redactSecrets() → redactPII()? → allowTools/denyTools → blockToolArgs → maxToolArgsBytes → sanitizeErrors()

Reversing order is explicit — see docs/integration-cookbook.md.


Policy files & CLI

Policy compile pipeline

Declarative policies map to the same rule factories as manual stacks. Built-in profiles: proxy-strict, agent-gate, audit-only.

Policy file (policies/agent-gate.json)

{
    "version": "1",
    "policyVersion": "team-alpha-v3",
    "mode": "block",
    "rules": [
        { "allowTools": { "names": ["search", "read_file", "grep"] } },
        { "maxToolArgsBytes": { "max": 65536 } },
        { "sanitizeErrors": {} }
    ]
}

Programmatic (loadPolicy / createGuardFromPolicy)

import { createGuardFromPolicy, loadPolicy } from "llm-stream-guard";

const guard = createGuardFromPolicy(loadPolicy("./policies/agent-gate.json"));
for await (const event of guard.guard(parsedEvents)) {
    await handle(event);
}
const byteGuard = guard.createByteGuard();

CLI

npx llm-stream-guard validate policies/agent-gate.json
npx llm-stream-guard resolve policies/examples/extends-agent.json
npx llm-stream-guard scan --policy policies/agent-gate.json test/fixtures/events/
cat capture.log | npx llm-stream-guard scan --policy policies/proxy-strict.json -
npx llm-stream-guard diff policies/v1.json policies/v2.json --check
npx llm-stream-guard profiles list
Env variable Effect
GUARD_MODE Override policy mode (block / warn / audit)
GUARD_POLICY_PATH Default --policy path for CLI scan

Schema reference: schemas/policy-v1.json. Example policies: policies/.

Policy pitfalls: overlapping allow/deny lists (POLICY_E009); empty allowlist with mode: block (POLICY_E010 / POLICY_E008).


Mode decision guide

Pick byte vs event mode in ~30 seconds:

Use the modes diagram above, or:

  • Raw SSE to browser, no parsercreateByteGuard()
  • Tool gate before executeguardEvents() + rule factories
  • Parse with assemble / AI SDK first → map to GuardEvent, then guardEvents()

Documentation

Related: llm-stream-assemble — stream parsing and assembly (separate package).


How this compares

llm-stream-guard Enterprise middleware llm-stream-assemble
Scope Stream security filter Broad platform Stream parsing
Byte + event Both first-class Often bytes-only Events (after parse)
Tool policy First-class Varies Assembly only
Dependencies Zero runtime Varies Zero runtime

Full matrix: docs/comparison.md.


Non-goals

  • No HTTP client, auth, or agent loop
  • No tool execution or UI components
  • No LLM-as-judge classifier
  • No hard dependency on assemble, AI SDK, or LangChain
  • No provider adapters (use assemble or your parser)

See docs/proposal.MD.


Development

pnpm install
./scripts/setup-githooks.sh
pnpm verify
Command Description
pnpm verify format + typecheck + build + test + fixtures + smoke
pnpm verify:deps fail if runtime dependencies are added
pnpm release:prep pre-tag checks (version, CHANGELOG, dist, npm pack)
pnpm diagrams:build regenerate README SVGs from Mermaid sources
pnpm fixtures:check-policies validate example + profile policies
pnpm fixtures:audit-policy-registry policy fixture REGISTRY parity
pnpm test Vitest (LSG-S/B/E/C/R/T/P/POL, LSG-REL)
pnpm bench:smoke local byte/event timing (informational)
pnpm build tsup → ESM + CJS + declarations

Author

Ladislav Kostolny01laky@gmail.com · GitHub @01laky

License

MIT — see LICENSE. Copyright (c) 2026 Ladislav Kostolny.