Skip to content

v0.41.35.0 feat(guardrails): vendor-neutral content guardrail seams (supersedes #1652)#1660

Merged
garrytan merged 5 commits into
masterfrom
garrytan/guardrails-seam
May 30, 2026
Merged

v0.41.35.0 feat(guardrails): vendor-neutral content guardrail seams (supersedes #1652)#1660
garrytan merged 5 commits into
masterfrom
garrytan/guardrails-seam

Conversation

@garrytan

Copy link
Copy Markdown
Owner

Summary

Adds vendor-neutral content guardrail seams at the five boundaries where external content enters GBrain's retrieval layer and LLM gateway, so a content firewall (prompt-injection / RAG-poison detector, PII scrubber, etc.) can be hooked in without binding GBrain to any specific vendor.

Supersedes #1652 — rebased from garrytan-agents into a base-repo branch so CI gets secret access (per CLAUDE.md's "Checking out PRs from garrytan-agents" workflow). Original authorship preserved via cherry-pick; the feature commit is unchanged.

The seam, not the vendor. GBrain gets a generic runGuardrails({ hook, content, metadata }) interface with five hook points. Zero guardrails registered by default — the OSS ships completely inert. No vendor code, no API URL, no phone-home in the public tree. A provider (Silmaril or anyone) implements classify() in their own package and registers it at init.

Five hooks, all observe-only / fail-open:

  • file_storage.markdown + file_storage.code — the import-into-retrieval boundary (the RAG-poison chokepoint)
  • ai_gateway.chat / .expand / .tool_input — GBrain's own LLM calls

Hard invariants, test-enforced (14 tests): runGuardrails returns void so nobody can branch on a verdict; a throwing provider can't break an ingest; slow providers are awaited inline; empty content short-circuits. The existing import-file tests still pass — the hot path is undisturbed.

Closes the CVE gap. The injection-via-filesystem attack bypassed the old LLM-only hooks because content entered through the file boundary. file_storage.markdown is a real hook at that boundary now — the seam and the vuln fix are the same move.

Commits:

  • feat(guardrails): vendor-neutral content guardrail seams (garrytan-agents) — the module, five hooks, doc, tests
  • chore: bump version and changelog (v0.41.34.0)

Files

  • src/core/guardrails.ts (new) — runGuardrails / registerGuardrailProvider / unregisterGuardrailProvider / hasGuardrails. Snapshot-before-iterate; per-provider try/catch.
  • src/core/import-file.tsfile_storage.markdown (after parseMarkdown + size guard, before sanity/hash/chunk/embed/write) + file_storage.code (after code size guard, before hash/chunk/embed/write).
  • src/core/ai/gateway.tsai_gateway.chat (latest user message only), ai_gateway.expand (query), ai_gateway.tool_input (tool name + input, before pending-persist). Cycle-safe stringifier.
  • docs/guardrails.md (new) — contract, seam table, provider-authoring guide.
  • test/guardrails.test.ts (new) — 14 tests pinning the contract.

Test Coverage

The seam is a new isolated module — test/guardrails.test.ts (14 tests) covers every invariant: observe-only (void return), fail-open (throw + reject isolation), inline await (slow async provider awaited), inert-by-default (zero providers = no-op), empty/blank short-circuit, register/unregister idempotency by id, content+metadata passthrough unmutated.

The five hook call sites are guarded by hasGuardrails() and wrapped fail-open, so with no provider registered they are pure no-ops — verified by the existing import-file suite (25 pass) and full typecheck.

Pre-Landing Review

bun run verify — 29/29 checks green. bun run typecheck clean. bun test test/guardrails.test.ts 14/14 pass. bun test test/import-file.test.ts 25/25 pass (ingest hot path undisturbed).

Documentation

docs/guardrails.md is the deliverable handed to integration partners: the contract (five hard invariants), the seam table, the provider-authoring guide, and a shadow-mode provider sketch. A partner's shadow-mode firewall becomes ~80 lines of provider code that never touches this public repo.

Test plan

  • bun run verify — 29/29 green
  • bun run typecheck — clean
  • bun test test/guardrails.test.ts — 14/14 pass
  • bun test test/import-file.test.ts — 25/25 pass (hot path undisturbed)

🤖 Generated with Claude Code

Expose observe-only guardrail seams at the five boundaries where external
content enters the retrieval layer and the LLM gateway, so a content firewall
(prompt-injection / RAG-poison detector, PII scrubber, etc.) can be hooked in
without binding GBrain to any specific vendor.

New module src/core/guardrails.ts:
  - runGuardrails({ hook, content, metadata }) -> void
  - registerGuardrailProvider / unregisterGuardrailProvider
  - hasGuardrails() fast-path guard for hot paths

Seams (all observe-only, fail-open, inline-await, inert by default):
  - file_storage.markdown  (import-file.ts importFromContent)
  - file_storage.code      (import-file.ts importCodeFile)
  - ai_gateway.chat        (gateway.ts chat, last user message only)
  - ai_gateway.expand      (gateway.ts expand)
  - ai_gateway.tool_input  (gateway.ts toolLoop, before pending-persist)

Invariants enforced by test/guardrails.test.ts (14 tests):
  - returns void; callers never branch on a verdict
  - provider throw/reject is swallowed (fail-open isolation)
  - slow async provider is awaited before resolving (inline)
  - zero providers => no-op; empty/blank content short-circuits
  - content + metadata passed through unmutated; idempotent by id

Hooks pass only the ingest/user-facing payload (md/code body, last user
message, expansion query, tool input). Never system prompts, full history,
tool output, LLM output, embeddings, or multimodal payloads.

Docs: docs/guardrails.md (contract, seam table, provider authoring guide).
OSS ships inert; vendors register a provider in their own package.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@garrytan garrytan force-pushed the garrytan/guardrails-seam branch from d795be4 to 27b1116 Compare May 30, 2026 16:44
@garrytan garrytan changed the title v0.41.34.0 feat(guardrails): vendor-neutral content guardrail seams (supersedes #1652) v0.41.35.0 feat(guardrails): vendor-neutral content guardrail seams (supersedes #1652) May 30, 2026
garrytan added 3 commits May 30, 2026 10:33
…-seam

# Conflicts:
#	CHANGELOG.md
#	VERSION
#	package.json
…-seam

# Conflicts:
#	CHANGELOG.md
#	VERSION
#	package.json
…-seam

# Conflicts:
#	CHANGELOG.md
#	VERSION
#	package.json
@garrytan garrytan merged commit 0b2a26a into master May 30, 2026
21 checks passed
mgunnin added a commit to mgunnin/gbrain that referenced this pull request Jun 3, 2026
* upstream/master:
  v0.41.36.0 feat(mcp): publish agent skills (list_skills / get_skill) for thin clients (garrytan#1661)
  v0.41.35.0 feat(guardrails): vendor-neutral content guardrail seams (supersedes garrytan#1652) (garrytan#1660)
  v0.41.34.0 feat(search): retrieval cathedral — max-pool + title + alias + evidence (garrytan#1657)
  v0.41.33.0 feat(search): intent-aware adaptive return-sizing + agent-facing query param (garrytan#1640)
  v0.41.32.0 fix(staleness): commit-relative sync staleness (supersedes garrytan#1623) (garrytan#1656)
  v0.41.31.0 feat(embed): delta-aware sync --all cost gate + real stale-embedding semantics (garrytan#1632)
  v0.41.30.0 fix(brainstorm/lsd): --save writes the advertised .md file via canonical ingestion path (garrytan#1655)

# Conflicts:
#	src/core/operations.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant