feat(blockreason): X-Pipelock-Block-Reason header schema and emit package#467
feat(blockreason): X-Pipelock-Block-Reason header schema and emit package#467luckyPipewrench wants to merge 3 commits intomainfrom
Conversation
…kage Defines the v1 vocabulary for machine-readable block reasons so agents can react intelligently to a block instead of treating every 403 as opaque. Locks header names, reason codes (28 across egress, content, MCP, posture, generic), severity values matching pipelock's existing config severity vocabulary, and retry hints (none, transient, policy). Package emits four required headers (reason, version, severity, retry) and two optional (layer, receipt). CloseFramePayload helper fits within RFC 6455's 123-byte close-frame limit by dropping optional fields in a documented order; block_reason is always present. Privacy invariant: the Info struct exposes no fields that could carry matched content, pattern names, or agent identifiers. Coverage 97.1% (mustMarshal error sentinel is defensive code unreachable through the public API given the fixed-shape struct). Spec doc at docs/specs/block-reason-header.md is the canonical schema. Implementation across the six transports lands separately so the schema can be reviewed before any block site commits to it.
…, align layer labels
Review feedback on the initial schema. Three contract issues that would have shipped wrong if the transport PR followed:
1. Required headers were not required. SetHeaders skipped severity/retry on empty values and emitted only version on empty reason. New(reason, severity, retry) now enforces the triple at construction (panic on empty — programming error per the codebase's panic policy). SetHeaders unconditionally emits all four required headers. WithLayer and WithReceipt return copies for optional fields.
2. CloseFramePayload could return RFC 6455-overlong output. Now guaranteed to fit within 123 bytes. If even the bare {block_reason: <code>} would overflow, returns a fixed sentinel (parse_error / version 1) so the close frame is always RFC 6455-compliant. Test invariant pins len(out) <= closeFrameMaxBytes for every input shape.
3. Layer-label vocabulary diverged from existing scanner labels. Spec now reuses scanner.Scanner* constants verbatim (blocklist, ratelimit, length, databudget, dlp, ssrf, entropy) so operators can correlate the header with their existing audit / metrics streams. Reason codes stay user-facing snake_case (domain_blocklist, rate_limit, etc.); the mapping table makes both surfaces explicit.
Spec also reworded to flag PR-A status: vocabulary is the target; the transport PR will wire every block path.
Coverage: 97.3% (every public function 100%, mustMarshal defensive-error sentinel uncovered).
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughIntroduces a machine-readable block-reason metadata system with a canonical v1 HTTP/WebSocket specification and a Go implementation package that constructs, encodes, and serializes block reason triples (reason, severity, retry) to HTTP headers and WebSocket close frames with RFC 6455 byte-limit enforcement. ChangesBlock Reason Specification & Implementation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 0/1 reviews remaining, refill in 25 minutes and 36 seconds.Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
/review deep |
AI Review: security deep (
|
… ULID receipt Address GPT review on the schema PR. Four items, all medium-or-lower severity but all real once the package is wired into enforcement. 1. Make invalid states unrepresentable. New now validates reason, severity, and retry against fixed allowlists (validReasons, validSeverities, validRetries) and returns ErrInvalidReason / ErrInvalidSeverity / ErrInvalidRetry rather than accepting any non-empty string. WithLayer restricts to ASCII alphanumeric and underscore (the shape of internal/scanner/Scanner* constants), bounded by layerMaxLen. WithReceipt restricts to a 26-char Crockford-base32 ULID, locking the slot opaque so attacker-controlled metadata cannot reach agent-visible response headers. 2. New no longer panics on the enforcement hot path. Returns Info, error so callers can fail closed without crashing the process. MustNew kept as the panicking variant reserved for compile-time-known constants and tests. 3. Receipt format locked to ULID. Spec now documents the exact charset and length (26 chars, Crockford-base32: 0-9 plus A-Z minus I, L, O, U). Validator rejects any non-conforming string. 4. CloseFramePayload overflow no longer downgrades to parse_error. New BlockReasonOverflow code preserves the audit signal that block-emit metadata was itself malformed, rather than silently reclassifying the original block. Coverage 98.5 percent. mustMarshal defensive sentinel uncovered (unreachable through public API).
Summary
Defines the v1 vocabulary for
X-Pipelock-Block-Reason, the response header set agents read to decide whether to retry, switch tools, or surface a useful error to the user. Lands the schema doc and the emit package now so the vocabulary can be reviewed before any block site commits to it. The transport refactor that wires every block path to callInfo.SetHeadersfollows in a separate PR.The header set:
X-Pipelock-Block-Reason(required): machine-readable code. 28 codes across egress, content, MCP, posture, and generic.X-Pipelock-Block-Reason-Version(required): schema version.X-Pipelock-Block-Reason-Severity(required): matches the existinginternal/config/schema.govocabulary (info,warn,critical).X-Pipelock-Block-Reason-Retry(required): retry hint (none,transient,policy).X-Pipelock-Block-Reason-Layer(optional): reusesinternal/scanner/Scanner*constants verbatim so operators can correlate header-driven agent behavior with existing audit logs and Prometheus labels.X-Pipelock-Block-Reason-Receipt(optional): receipt UUID.Why design first
Once an agent in production reads
dlp_match, the vocabulary is locked. Renaming a code in v2 breaks every consumer. Splitting the schema and the transport refactor into separate PRs gives reviewers a chance to challenge the vocabulary before any of the roughly 50 block sites commits to it.Privacy
The
Infostruct deliberately exposes onlyReason,Severity,Retry,Layer, andReceipt. None of those fields can carry matched content, pattern names, or agent identifiers. Tests pin the surface so a future field addition trips review.RFC 6455 floor
CloseFramePayload()is bounded to RFC 6455's 123-byte close-frame limit. Optional fields drop in a documented order (receipt, layer, retry, severity, version). If even the bare{"block_reason":"<code>"}would overflow because the Reason value is unusually long, the helper returns a fixed sentinel rather than a malformed close frame. Test invariant pins the size ceiling across normal, truncated, overlong-reason, and floor cases.Anti-scope-creep
Summary by CodeRabbit
Release Notes
New Features
Documentation
X-Pipelock-Block-Reasonheader format and WebSocket close-frame behavior.Tests