Skip to content

fix(stream): apply policy row-filter per subscriber on SSE#381

Open
taitelee wants to merge 9 commits into
mainfrom
sse-row-filter
Open

fix(stream): apply policy row-filter per subscriber on SSE#381
taitelee wants to merge 9 commits into
mainfrom
sse-row-filter

Conversation

@taitelee

@taitelee taitelee commented Jul 8, 2026

Copy link
Copy Markdown
Member

Summary

The Server-Sent Events stream applied a role's column allowlist but never its row-level filter, so a subscriber could receive rows the structured-query path would have hidden for that same role — a row-level-security bypass on the streaming surface. This applies the same row-filter on the stream:

  • The filter resolves once into predicates shared by both read paths — rendered to SQL on the query path, evaluated in memory (ResolvedPermissions.RowVisible) on the stream — so the two can't drift.
  • Fan-out is now claims-aware: a role without a filter keeps the once-per-role projection fast path unchanged (zero change for the public stream); a filtered role keeps the shared column projection but delivers each row only to the subscribers whose JWT claims admit it (evaluated against the full event). Replay does the same per connection.
  • Ordering predicates (_gt/_lt) are schema-informed — numeric columns compare numerically via the schema registry now wired into the hub — while equality/set (_eq/_neq/_in) stay exact. Ambiguous values fail closed; the best-effort ordering boundary is documented in access-control docs.

Related Issues

Closes #319

@taitelee
taitelee requested review from a team and EricAndrechek July 8, 2026 01:21
@taitelee taitelee moved this from Backlog to In progress in WaveHouse Task Board Jul 8, 2026
@github-actions github-actions Bot added documentation Improvements or additions to documentation go Pull requests that update go code area/api HTTP handlers, routing, middleware area/query Structured query AST, SQL builder area/policy Access control policies (Hasura-style) area/docs Documentation, site/, README area/infra CI, build, deploy, Docker, release labels Jul 8, 2026
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: fb8d93e5-3e4d-4381-bd41-12894779e93d

📥 Commits

Reviewing files that changed from the base of the PR and between dd38086 and 2379bc4.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • docs/src/content/docs/architecture.md
  • internal/policy/policy.go
  • internal/policy/rowfilter.go
  • internal/policy/rowfilter_test.go
  • internal/stream/hub.go
  • internal/stream/hub_test.go
  • tests/e2e/sdk/streaming.test.ts
💤 Files with no reviewable changes (1)
  • internal/policy/rowfilter_test.go
📜 Recent review details
⏰ Context from checks skipped due to timeout. (9)
  • GitHub Check: Docs build
  • GitHub Check: Unit tests
  • GitHub Check: Coverage
  • GitHub Check: Integration tests
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (actions)
  • GitHub Check: Lint
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
🧰 Additional context used
📓 Path-based instructions (2)
docs/src/content/docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Documentation prose under the Starlight docs site must stay accurate against code, include runnable examples where relevant, and reflect code↔docs sync for changed behavior.

Files:

  • docs/src/content/docs/architecture.md
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Write tests in table-driven form with t.Run(tt.name, ...) for multiple cases.
Use shared mocks from internal/testutil/ instead of ad-hoc mocks in tests.
Use the repo’s JWT, schema, policy, pipes, and JSON response test helpers (testutil.MakeJWT, testutil.MakeExpiredJWT, NewTestSchemaRegistry, policy.NewMemoryStore, pipes.NewMemoryStore, AssertJSONResponse, AssertJSONContains) where applicable.
Every new function should have corresponding test cases, and new code should aim for 80%+ coverage.

Files:

  • internal/stream/hub_test.go
🧠 Learnings (3)
📚 Learning: 2026-06-10T15:01:09.027Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 312
File: docs/src/content/docs/development.md:0-0
Timestamp: 2026-06-10T15:01:09.027Z
Learning: In this repo’s Markdown review (all .md files), do not flag capitalization/style issues for literal paths starting with ".github/" (or any substring that is a path beginning with ".github/"). Treat ".github" as the correct lowercase dotfile directory name, even when it appears inside prose or code spans; automated checks such as LanguageTool’s "(GITHUB)" rule commonly produce false positives for this literal filesystem path.

Applied to files:

  • docs/src/content/docs/architecture.md
  • CHANGELOG.md
📚 Learning: 2026-07-07T12:38:12.052Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 378
File: internal/auth/auth.go:119-132
Timestamp: 2026-07-07T12:38:12.052Z
Learning: In this repo, do not add or recommend logging/tracing client IP addresses using naive or untrusted sources (e.g., `r.RemoteAddr` or directly trusting/deriving `X-Forwarded-For`) anywhere in the Go codebase. `middleware.RealIP` was removed due to IP-spoofing risks, and proper trusted-proxy-aware client-IP handling is intentionally deferred to issue `#333`. During code review, if proposed changes would record client IPs (including in audit paths such as `internal/auth/auth.go`), reject/redirect until `#333` lands with correct trusted-proxy configuration and safeguards.

Applied to files:

  • internal/policy/rowfilter.go
  • internal/policy/policy.go
  • internal/stream/hub.go
  • internal/stream/hub_test.go
📚 Learning: 2026-06-26T12:23:22.696Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 346
File: internal/stream/subscriber_test.go:9-28
Timestamp: 2026-06-26T12:23:22.696Z
Learning: In this Go repository, prefer table-driven tests (e.g., `[]struct{...}` with `t.Run(...)`) only for tests that cover multiple scenarios/inputs and can be cleanly enumerated. Do not artificially rewrite a clear single-scenario sequential behavioral-flow test into a table-driven form just to fit the pattern; if there’s only one meaningful scenario, keep the test as a straightforward linear flow (as in `TestSubscriber_SendDeliversThenDropsWhenFull`).

Applied to files:

  • internal/stream/hub_test.go
🔇 Additional comments (10)
internal/stream/hub_test.go (1)

21-27: LGTM!

Also applies to: 30-39, 41-51, 53-64, 68-68, 100-100, 136-136, 163-181, 182-212, 214-228, 230-253, 254-284, 286-288, 331-331, 352-352, 373-373, 390-390, 411-411, 425-425, 438-462, 464-466

CHANGELOG.md (1)

34-34: LGTM!

docs/src/content/docs/architecture.md (1)

88-88: LGTM!

tests/e2e/sdk/streaming.test.ts (1)

3-10: LGTM!

Also applies to: 28-39, 31-31, 133-182

internal/policy/policy.go (3)

111-226: LGTM!


301-312: LGTM!


242-262: 🚀 Performance & Scalability

Consider deterministic column ordering. resolvePredicates ranges over a map, so multi-column filters can emit predicates in different orders across runs; sorting the keys before resolving would make the SQL text and WhereParams order stable.

internal/policy/rowfilter.go (1)

36-51: LGTM!

internal/stream/hub.go (2)

218-233: LGTM!


168-176: 🩺 Stability & Availability

Check sub.claims synchronization
Broadcast reads sub.claims while Subscriber.SetClaims may write it. If claims can change after the subscriber is added, guard the field with the same lock or make it immutable; otherwise this path is fine.


📝 Walkthrough

Summary by CodeRabbit

  • Security
    • Live SSE streams now apply role row-level filters per subscriber using each subscriber’s JWT claims, improving protection against row leaks.
    • Replay and gap-fill now use the same per-subscriber row visibility checks as live updates.
  • Documentation
    • Updated streaming access-control docs to clarify how row filters and ordering behave, and that resource limits are not enforced on the live event stream.
  • Bug Fixes / Tests
    • Improved permissions consistency between streaming and normal queries.
    • Added end-to-end coverage for subscriber-specific row scoping.

Walkthrough

This PR makes SSE live-stream delivery apply row-level policy filters per subscriber, using claims-aware policy resolution, schema-aware numeric comparisons, and replay-path filtering. It also updates wiring, tests, and documentation to match the new streaming behavior.

Changes

Streaming row-level security enforcement

Layer / File(s) Summary
Policy row-filter predicate resolution
internal/policy/policy.go
ResolvedPermissions caches resolved row-filter predicates; filter resolution is split into resolvePredicates and predicatesToSQL, replacing the monolithic resolveFilters.
Row visibility evaluation engine
internal/policy/rowfilter.go, internal/policy/rowfilter_test.go
Adds HasRowFilter/RowVisible with fail-closed matches, compareScalar, and scalarString helpers, covering equality, in-set, ordering, and multi-predicate cases.
Numeric type detection for ordering
internal/discovery/validation.go, internal/discovery/validation_test.go
Adds unwrapType and exported IsNumericType to distinguish numeric vs lexicographic comparisons.
Hub broadcast/replay row-filter integration
internal/stream/hub.go, internal/stream/subscriber.go, internal/api/stream.go
Hub gains a schema registry; Broadcast and ReplayFrame use projectColumns and per-subscriber RowVisible checks; Subscriber.SetClaims stores per-connection claims; Handle passes claims into the stream path.
NewHub constructor wiring and test updates
cmd/wavehouse/main.go, tests/integration/setup_test.go, internal/api/*_test.go, internal/stream/hub_test.go, go.mod
Updates NewHub/ReplayFrame call sites for the new signatures, refreshes stream/router tests, adds row-filter isolation and replay coverage, and bumps the Go toolchain version.
Documentation and changelog updates
CHANGELOG.md, docs/src/content/docs/access-control.mdx, docs/src/content/docs/architecture.md, tests/e2e/sdk/streaming.test.ts
Documents enforced row-level filtering, claims-aware column projection, and ordering predicate semantics on the live stream, and adds an end-to-end scoped-stream test.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant StreamHandler
  participant Subscriber
  participant Hub
  participant Policy

  StreamHandler->>Subscriber: SetClaims(claims)
  StreamHandler->>Hub: register subscriber
  Hub->>Policy: Evaluate(role, claims)
  Policy-->>Hub: resolved rowFilter
  Hub->>Hub: projectColumns(role, event)
  Hub->>Hub: RowVisible(row, numericCols)
  Hub-->>Subscriber: deliver frame if visible
Loading

Possibly related issues

Possibly related PRs

  • Wave-RF/WaveHouse#172: Both PRs touch the authorization/policy layer that streaming relies on.
  • Wave-RF/WaveHouse#353: Both PRs modify the live SSE hub fan-out and projection path in internal/stream/hub.go.
  • Wave-RF/WaveHouse#358: Both PRs work on row-filter predicate handling and operator semantics in the policy layer.

Suggested reviewers: EricAndrechek

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The go.mod toolchain bump is unrelated to the SSE policy fix and appears out of scope. Remove the Go toolchain version bump or split it into a separate maintenance PR unless it is required for this fix.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main SSE row-filter fix.
Description check ✅ Passed The description clearly matches the streaming row-filter security fix and its scope.
Linked Issues check ✅ Passed The changes apply row-filter evaluation per SSE subscriber and replay, matching issue #319's RLS drift fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sse-row-filter
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch sse-row-filter

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

📚 Docs preview is livehttps://c89f5022-wavehouse-docs.wave-rf.workers.dev

  • Commit2379bc4: refactor(policy): drop compiled row filter; evaluate per subscriber
  • Author@taitelee
  • Committed — 2026-07-09 08:39 (UTC-04:00)
  • Deployed — 2026-07-09 08:58 EDT

@github-code-quality

github-code-quality Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: Go

Go

The overall coverage in the branch remains at 90%, unchanged from the branch.

Show a code coverage summary of the most impacted files.
File 774faec 2379bc4 +/-
internal/stream/hub.go 97% 95% -2%
internal/policy/policy.go 98% 98% 0%
internal/discov...y/validation.go 94% 94% 0%
internal/stream/subscriber.go 100% 100% 0%
internal/api/stream.go 55% 56% +1%
internal/policy/rowfilter.go 0% 85% +85%

Updated July 09, 2026 12:56 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/src/content/docs/access-control.mdx (1)

373-373: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the Live stream row in the enforcement table to include row-level filtering.

The "Where each rule is enforced" table at line 373 still reads "denied columns are masked from each event" with no mention of row-level filter, but this PR adds exactly that enforcement. The Structured read row (line 371) explicitly lists "row filter", so the table is now inconsistent with both the caution block below (lines 377–378) and the actual code (hub.go Broadcast calls RowVisible per subscriber). Per the docs code↔docs sync guideline, the table should reflect the changed behavior.

📝 Proposed fix to update the table row
-| Live stream | `GET /v1/stream` | table+role `select` required (a table the role can't read is skipped), then denied columns are masked from each event |
+| Live stream | `GET /v1/stream` | table+role `select` required (a table the role can't read is skipped), then denied columns are masked from each event, and row-level `filter` predicates are evaluated per subscriber against their JWT claims (see caution below) |

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2efc09f7-0227-4f93-844b-1b18b486200f

📥 Commits

Reviewing files that changed from the base of the PR and between 774faec and 7756e11.

📒 Files selected for processing (17)
  • CHANGELOG.md
  • cmd/wavehouse/main.go
  • docs/src/content/docs/access-control.mdx
  • docs/src/content/docs/architecture.md
  • internal/api/errors_test.go
  • internal/api/router_test.go
  • internal/api/stream.go
  • internal/api/stream_test.go
  • internal/discovery/validation.go
  • internal/discovery/validation_test.go
  • internal/policy/policy.go
  • internal/policy/rowfilter.go
  • internal/policy/rowfilter_test.go
  • internal/stream/hub.go
  • internal/stream/hub_test.go
  • internal/stream/subscriber.go
  • tests/integration/setup_test.go
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: E2E tests
  • GitHub Check: Docs build
  • GitHub Check: Coverage
  • GitHub Check: Lint
  • GitHub Check: Analyze (go)
⚠️ CI failures not shown inline (2)

GitHub Actions: PR housekeeping / 0_PR housekeeping.txt: fix(stream): apply policy row-filter per subscriber on SSE

Conclusion: failure

View job details

##[group]Run # Single source of truth for the rule: scripts/lint-pr-title.sh — the
 �[36;1m# Single source of truth for the rule: scripts/lint-pr-title.sh — the�[0m
 �[36;1m# SAME validator the local agent gate runs (.claude/hooks/agent-bash-gate.sh),�[0m
 �[36;1m# so CI and local can't drift. The checkout above is ref: main, so this is�[0m
 �[36;1m# always the default-branch script. Dependabot's grouped-update titles�[0m
 �[36;1m# routinely exceed the 72-char subject cap and the format isn't�[0m
 �[36;1m# configurable, so Dependabot PRs are exempt from the length check�[0m
 �[36;1m# (the format check still applies).�[0m
 �[36;1mif [[ "$PR_AUTHOR" == "dependabot[bot]" || "$PR_AUTHOR" == "app/dependabot" ]]; then�[0m
 �[36;1m  export PR_TITLE_SKIP_LENGTH=1�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif reason=$(bash scripts/lint-pr-title.sh "$PR_TITLE" 2>&1); then�[0m
 �[36;1m  echo "passed=true" >> "$GITHUB_OUTPUT"�[0m
 �[36;1m  echo "PR title OK: $PR_TITLE"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "passed=false" >> "$GITHUB_OUTPUT"�[0m
 �[36;1m  printf '%s\n' "$reason"�[0m
 �[36;1m  echo "::error::$(printf '%s' "$reason" | head -1)"�[0m

GitHub Actions: PR housekeeping / PR housekeeping: fix(stream): apply policy row-filter per subscriber on SSE

Conclusion: failure

View job details

##[group]Run # Single source of truth for the rule: scripts/lint-pr-title.sh — the
 �[36;1m# Single source of truth for the rule: scripts/lint-pr-title.sh — the�[0m
 �[36;1m# SAME validator the local agent gate runs (.claude/hooks/agent-bash-gate.sh),�[0m
 �[36;1m# so CI and local can't drift. The checkout above is ref: main, so this is�[0m
 �[36;1m# always the default-branch script. Dependabot's grouped-update titles�[0m
 �[36;1m# routinely exceed the 72-char subject cap and the format isn't�[0m
 �[36;1m# configurable, so Dependabot PRs are exempt from the length check�[0m
 �[36;1m# (the format check still applies).�[0m
 �[36;1mif [[ "$PR_AUTHOR" == "dependabot[bot]" || "$PR_AUTHOR" == "app/dependabot" ]]; then�[0m
 �[36;1m  export PR_TITLE_SKIP_LENGTH=1�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif reason=$(bash scripts/lint-pr-title.sh "$PR_TITLE" 2>&1); then�[0m
 �[36;1m  echo "passed=true" >> "$GITHUB_OUTPUT"�[0m
 �[36;1m  echo "PR title OK: $PR_TITLE"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "passed=false" >> "$GITHUB_OUTPUT"�[0m
 �[36;1m  printf '%s\n' "$reason"�[0m
 �[36;1m  echo "::error::$(printf '%s' "$reason" | head -1)"�[0m
🧰 Additional context used
📓 Path-based instructions (2)
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Write tests in table-driven form with t.Run(tt.name, ...) for multiple cases.
Use shared mocks from internal/testutil/ instead of ad-hoc mocks in tests.
Use the repo’s JWT, schema, policy, pipes, and JSON response test helpers (testutil.MakeJWT, testutil.MakeExpiredJWT, NewTestSchemaRegistry, policy.NewMemoryStore, pipes.NewMemoryStore, AssertJSONResponse, AssertJSONContains) where applicable.
Every new function should have corresponding test cases, and new code should aim for 80%+ coverage.

Files:

  • internal/api/errors_test.go
  • tests/integration/setup_test.go
  • internal/api/stream_test.go
  • internal/api/router_test.go
  • internal/discovery/validation_test.go
  • internal/policy/rowfilter_test.go
  • internal/stream/hub_test.go
docs/src/content/docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Documentation prose under the Starlight docs site must stay accurate against code, include runnable examples where relevant, and reflect code↔docs sync for changed behavior.

Files:

  • docs/src/content/docs/access-control.mdx
  • docs/src/content/docs/architecture.md
🧠 Learnings (5)
📚 Learning: 2026-05-20T01:02:00.784Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 164
File: internal/api/router_test.go:289-350
Timestamp: 2026-05-20T01:02:00.784Z
Learning: In WaveHouse’s internal API tests (files matching internal/api/**/*_test.go), follow the existing separation-of-concerns convention for testing the RequireRole middleware: inject `ContextKeyRole` directly into the request `context.Context` instead of using `testutil.MakeJWT`/JWT-driven flows. Do not refactor role-gate tests to use JWT tokens—JWT parsing and token handling are covered separately in `middleware_test.go` (the dedicated JWT parsing tests), and mixing those concerns would expand the failure surface and reduce isolation.

Applied to files:

  • internal/api/errors_test.go
  • internal/api/stream_test.go
  • internal/api/router_test.go
📚 Learning: 2026-05-23T01:23:59.268Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 174
File: internal/api/ingest_test.go:111-111
Timestamp: 2026-05-23T01:23:59.268Z
Learning: In WaveHouse Go tests in internal/api/**/*_test.go, use internal/testutil.AssertJSONErrorResponse(t, w) for HTTP error-path JSON assertions. Do not use (or reintroduce) package-local assertJSONErrorResponse helpers. AssertJSONErrorResponse verifies the response Content-Type is application/json, includes the X-Content-Type-Options: nosniff header, and that the JSON body contains an "error" field.

Applied to files:

  • internal/api/errors_test.go
  • internal/api/stream_test.go
  • internal/api/router_test.go
📚 Learning: 2026-06-26T12:23:22.696Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 346
File: internal/stream/subscriber_test.go:9-28
Timestamp: 2026-06-26T12:23:22.696Z
Learning: In this Go repository, prefer table-driven tests (e.g., `[]struct{...}` with `t.Run(...)`) only for tests that cover multiple scenarios/inputs and can be cleanly enumerated. Do not artificially rewrite a clear single-scenario sequential behavioral-flow test into a table-driven form just to fit the pattern; if there’s only one meaningful scenario, keep the test as a straightforward linear flow (as in `TestSubscriber_SendDeliversThenDropsWhenFull`).

Applied to files:

  • internal/api/errors_test.go
  • tests/integration/setup_test.go
  • internal/api/stream_test.go
  • internal/api/router_test.go
  • internal/discovery/validation_test.go
  • internal/policy/rowfilter_test.go
  • internal/stream/hub_test.go
📚 Learning: 2026-07-07T12:38:12.052Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 378
File: internal/auth/auth.go:119-132
Timestamp: 2026-07-07T12:38:12.052Z
Learning: In this repo, do not add or recommend logging/tracing client IP addresses using naive or untrusted sources (e.g., `r.RemoteAddr` or directly trusting/deriving `X-Forwarded-For`) anywhere in the Go codebase. `middleware.RealIP` was removed due to IP-spoofing risks, and proper trusted-proxy-aware client-IP handling is intentionally deferred to issue `#333`. During code review, if proposed changes would record client IPs (including in audit paths such as `internal/auth/auth.go`), reject/redirect until `#333` lands with correct trusted-proxy configuration and safeguards.

Applied to files:

  • internal/api/errors_test.go
  • cmd/wavehouse/main.go
  • internal/discovery/validation.go
  • tests/integration/setup_test.go
  • internal/api/stream.go
  • internal/api/stream_test.go
  • internal/api/router_test.go
  • internal/discovery/validation_test.go
  • internal/policy/policy.go
  • internal/policy/rowfilter.go
  • internal/stream/subscriber.go
  • internal/policy/rowfilter_test.go
  • internal/stream/hub.go
  • internal/stream/hub_test.go
📚 Learning: 2026-06-10T15:01:09.027Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 312
File: docs/src/content/docs/development.md:0-0
Timestamp: 2026-06-10T15:01:09.027Z
Learning: In this repo’s Markdown review (all .md files), do not flag capitalization/style issues for literal paths starting with ".github/" (or any substring that is a path beginning with ".github/"). Treat ".github" as the correct lowercase dotfile directory name, even when it appears inside prose or code spans; automated checks such as LanguageTool’s "(GITHUB)" rule commonly produce false positives for this literal filesystem path.

Applied to files:

  • docs/src/content/docs/architecture.md
  • CHANGELOG.md
🔇 Additional comments (20)
CHANGELOG.md (1)

34-34: LGTM!

docs/src/content/docs/access-control.mdx (1)

377-378: LGTM!

docs/src/content/docs/architecture.md (1)

88-88: LGTM!

cmd/wavehouse/main.go (1)

295-295: LGTM!

tests/integration/setup_test.go (1)

315-315: LGTM!

internal/api/errors_test.go (1)

197-197: LGTM!

internal/api/router_test.go (1)

286-286: LGTM!

Also applies to: 342-342, 414-414, 475-475, 526-526, 551-551, 648-648

internal/api/stream_test.go (1)

25-25: LGTM!

Also applies to: 53-53, 78-78, 117-117

internal/stream/hub_test.go (2)

41-51: LGTM!


68-68: LGTM!

Also applies to: 100-100, 136-136, 288-288, 331-331, 352-352, 373-373, 390-390, 411-411, 440-440

internal/policy/policy.go (1)

64-82: LGTM!

Also applies to: 197-208, 228-313

internal/policy/rowfilter.go (2)

13-15: LGTM!


17-103: 🔒 Security & Privacy

Lexicographic fallback is intentional NewHub is already wired with a schema registry in cmd/wavehouse/main.go, and numericCols deliberately falls back to string comparison when schema information is unavailable. That makes >/< best-effort by design, so this isn’t a security bug; the doc comment is the part that should be softened.

			> Likely an incorrect or invalid review comment.
internal/policy/rowfilter_test.go (1)

1-108: LGTM!

internal/discovery/validation.go (1)

51-69: LGTM!

Also applies to: 152-158

internal/discovery/validation_test.go (1)

227-255: LGTM!

internal/api/stream.go (1)

45-51: LGTM!

Also applies to: 85-85, 103-103

internal/stream/hub.go (2)

25-31: LGTM!

Also applies to: 43-45


185-198: LGTM!

Also applies to: 218-233, 245-277

internal/stream/subscriber.go (1)

26-31: LGTM!

Also applies to: 51-55

Comment thread internal/stream/hub_test.go
Comment thread internal/stream/hub_test.go
Comment thread internal/stream/hub.go
@github-project-automation github-project-automation Bot moved this from In progress to In review in WaveHouse Task Board Jul 8, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 8, 2026
@github-actions github-actions Bot added the dependencies Pull requests that update a dependency file label Jul 8, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c2faf18f-1e6f-44b3-92f4-aa3152c98c0c

📥 Commits

Reviewing files that changed from the base of the PR and between d534a25 and 8987480.

📒 Files selected for processing (6)
  • go.mod
  • internal/policy/policy.go
  • internal/policy/rowfilter.go
  • internal/policy/rowfilter_test.go
  • internal/stream/hub.go
  • internal/stream/hub_test.go
📜 Review details
⏰ Context from checks skipped due to timeout. (7)
  • GitHub Check: Integration tests
  • GitHub Check: E2E tests
  • GitHub Check: Docs build
  • GitHub Check: Unit tests
  • GitHub Check: Coverage
  • GitHub Check: Lint
  • GitHub Check: Analyze (go)
⚠️ CI failures not shown inline (2)

GitHub Actions: PR housekeeping / PR housekeeping: fix(stream): apply policy row-filter per subscriber on SSE

Conclusion: failure

View job details

##[group]Run # Single source of truth for the rule: scripts/lint-pr-title.sh — the
 �[36;1m# Single source of truth for the rule: scripts/lint-pr-title.sh — the�[0m
 �[36;1m# SAME validator the local agent gate runs (.claude/hooks/agent-bash-gate.sh),�[0m
 �[36;1m# so CI and local can't drift. The checkout above is ref: main, so this is�[0m
 �[36;1m# always the default-branch script. Dependabot's grouped-update titles�[0m
 �[36;1m# routinely exceed the 72-char subject cap and the format isn't�[0m
 �[36;1m# configurable, so Dependabot PRs are exempt from the length check�[0m
 �[36;1m# (the format check still applies).�[0m
 �[36;1mif [[ "$PR_AUTHOR" == "dependabot[bot]" || "$PR_AUTHOR" == "app/dependabot" ]]; then�[0m
 �[36;1m  export PR_TITLE_SKIP_LENGTH=1�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif reason=$(bash scripts/lint-pr-title.sh "$PR_TITLE" 2>&1); then�[0m
 �[36;1m  echo "passed=true" >> "$GITHUB_OUTPUT"�[0m
 �[36;1m  echo "PR title OK: $PR_TITLE"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "passed=false" >> "$GITHUB_OUTPUT"�[0m
 �[36;1m  printf '%s\n' "$reason"�[0m
 �[36;1m  echo "::error::$(printf '%s' "$reason" | head -1)"�[0m

GitHub Actions: PR housekeeping / 0_PR housekeeping.txt: fix(stream): apply policy row-filter per subscriber on SSE

Conclusion: failure

View job details

##[group]Run # Single source of truth for the rule: scripts/lint-pr-title.sh — the
 �[36;1m# Single source of truth for the rule: scripts/lint-pr-title.sh — the�[0m
 �[36;1m# SAME validator the local agent gate runs (.claude/hooks/agent-bash-gate.sh),�[0m
 �[36;1m# so CI and local can't drift. The checkout above is ref: main, so this is�[0m
 �[36;1m# always the default-branch script. Dependabot's grouped-update titles�[0m
 �[36;1m# routinely exceed the 72-char subject cap and the format isn't�[0m
 �[36;1m# configurable, so Dependabot PRs are exempt from the length check�[0m
 �[36;1m# (the format check still applies).�[0m
 �[36;1mif [[ "$PR_AUTHOR" == "dependabot[bot]" || "$PR_AUTHOR" == "app/dependabot" ]]; then�[0m
 �[36;1m  export PR_TITLE_SKIP_LENGTH=1�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif reason=$(bash scripts/lint-pr-title.sh "$PR_TITLE" 2>&1); then�[0m
 �[36;1m  echo "passed=true" >> "$GITHUB_OUTPUT"�[0m
 �[36;1m  echo "PR title OK: $PR_TITLE"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "passed=false" >> "$GITHUB_OUTPUT"�[0m
 �[36;1m  printf '%s\n' "$reason"�[0m
 �[36;1m  echo "::error::$(printf '%s' "$reason" | head -1)"�[0m
🧰 Additional context used
📓 Path-based instructions (1)
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Write tests in table-driven form with t.Run(tt.name, ...) for multiple cases.
Use shared mocks from internal/testutil/ instead of ad-hoc mocks in tests.
Use the repo’s JWT, schema, policy, pipes, and JSON response test helpers (testutil.MakeJWT, testutil.MakeExpiredJWT, NewTestSchemaRegistry, policy.NewMemoryStore, pipes.NewMemoryStore, AssertJSONResponse, AssertJSONContains) where applicable.
Every new function should have corresponding test cases, and new code should aim for 80%+ coverage.

Files:

  • internal/policy/rowfilter_test.go
  • internal/stream/hub_test.go
🧠 Learnings (2)
📚 Learning: 2026-07-07T12:38:12.052Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 378
File: internal/auth/auth.go:119-132
Timestamp: 2026-07-07T12:38:12.052Z
Learning: In this repo, do not add or recommend logging/tracing client IP addresses using naive or untrusted sources (e.g., `r.RemoteAddr` or directly trusting/deriving `X-Forwarded-For`) anywhere in the Go codebase. `middleware.RealIP` was removed due to IP-spoofing risks, and proper trusted-proxy-aware client-IP handling is intentionally deferred to issue `#333`. During code review, if proposed changes would record client IPs (including in audit paths such as `internal/auth/auth.go`), reject/redirect until `#333` lands with correct trusted-proxy configuration and safeguards.

Applied to files:

  • internal/stream/hub.go
  • internal/policy/rowfilter.go
  • internal/policy/policy.go
  • internal/policy/rowfilter_test.go
  • internal/stream/hub_test.go
📚 Learning: 2026-06-26T12:23:22.696Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 346
File: internal/stream/subscriber_test.go:9-28
Timestamp: 2026-06-26T12:23:22.696Z
Learning: In this Go repository, prefer table-driven tests (e.g., `[]struct{...}` with `t.Run(...)`) only for tests that cover multiple scenarios/inputs and can be cleanly enumerated. Do not artificially rewrite a clear single-scenario sequential behavioral-flow test into a table-driven form just to fit the pattern; if there’s only one meaningful scenario, keep the test as a straightforward linear flow (as in `TestSubscriber_SendDeliversThenDropsWhenFull`).

Applied to files:

  • internal/policy/rowfilter_test.go
  • internal/stream/hub_test.go
🔇 Additional comments (8)
internal/policy/policy.go (2)

111-175: LGTM!

Also applies to: 177-233


235-260: LGTM!

Also applies to: 262-461

internal/policy/rowfilter.go (1)

78-139: LGTM!

internal/policy/rowfilter_test.go (1)

111-167: LGTM!

Also applies to: 169-232

internal/stream/hub.go (2)

142-171: LGTM!

Also applies to: 221-236


172-173: 🩺 Stability & Availability

No race hereSubscriber.claims is set once via SetClaims before Hub.Add and documented as read-only for the subscriber’s lifetime, so Broadcast’s read does not have a concurrent writer.

			> Likely an incorrect or invalid review comment.
internal/stream/hub_test.go (1)

22-29: LGTM!

Also applies to: 188-214, 440-464, 519-526, 530-545

go.mod (1)

3-3: LGTM!

Comment thread internal/stream/hub_test.go Outdated
@github-actions github-actions Bot added the area/sdk TypeScript SDK (clients/ts/) label Jul 8, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 8, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/api HTTP handlers, routing, middleware area/docs Documentation, site/, README area/infra CI, build, deploy, Docker, release area/policy Access control policies (Hasura-style) area/query Structured query AST, SQL builder area/sdk TypeScript SDK (clients/ts/) dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation go Pull requests that update go code

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

security(streaming): SSE applies the column allowlist but not the policy row-filter — query/stream RLS drift

1 participant