Skip to content

feat(api): trusted-proxy client address, and gate traceparent on it - #855

Merged
taitelee merged 16 commits into
mainfrom
claude/333-trusted-proxy-client-ip-cvhpw2
Oct 20, 2026
Merged

taitelee merged 16 commits into
mainfrom
claude/333-trusted-proxy-client-ip-cvhpw2

Conversation

@taitelee

@taitelee taitelee commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

Requested by Taite · project thread

Summary

Before: behind a proxy, WaveHouse's logs only had the proxy's address, and the server span's client.address was whatever the client wrote first in X-Forwarded-For (otelhttp reads the leftmost entry unconditionally). Any client's traceparent was adopted as the parent of the request's span.

After: server.trusted_proxies (WH_SERVER_TRUSTED_PROXIES, CIDRs or bare addresses, default empty) lists the proxies to believe. From a listed peer, X-Forwarded-For is walked right to left and the first hop that isn't a listed proxy is the client; from anyone else the client is the connection's own address and the header is ignored. The result is client.address on every request-scoped log record (operator-key audit line included) and on the server span, replacing otelhttp's value. A traceparent is joined only from a listed proxy; from anyone else otelhttp starts a new root linked to it (WithPublicEndpointFn). r.RemoteAddr is never rewritten.

How: internal/config/proxies.go parses and validates the list (bad entry refuses boot, a /0 entry is a boot warning). internal/api/clientip.go holds the walk and a clientAddress middleware right after requestID; observability.SetRequestClient stores it on the existing request scope from #771, and AnnotateSpan/TraceHandler emit it. The otelhttp middleware is now built once per router instead of once per request.

Behavior change: with the default empty list, a tracing proxy's traceparent is now linked rather than continued until the proxy is listed. It has its own ### Changed CHANGELOG entry. The integration suite now lists loopback as trusted so TestOTel_IngestTraceContinuity still joins the caller's trace.

Call sites in files open PRs #712/#770 also edit: one line in internal/app/wire.go (deps.TrustedProxies = …). wire_ops.go gets the same line.

Test plan

  • make build, go vet ./... (and -tags integration ./tests/integration/)
  • go test -race ./internal/api ./internal/config ./internal/observability; go test ./internal/app ./internal/auth
  • Table tests for the walk: spoofed leftmost entries, trusted-looking spoof left of the client, chained proxies across header lines, all-trusted chain, empty and unreadable hops, ports, bracketed IPv6, IPv4-mapped and zoned peers, non-IP peer
  • Router test: one client.address on the span with the resolved value; traceparent joined from a trusted peer, linked (new root) from an untrusted one
  • Code and docs pre-push reviewers: ship_it
  • GitHub CI green: lint, unit, integration (app and backends), e2e, coverage (new files at 100%)

Related Issues

Closes #333

Part of #777. Reviewer noted a possible follow-up outside this scope: baggage is still extracted from any peer (pre-existing; nothing reads it today).

🤖 Generated with Claude Code

https://claude.ai/code/session_01UbPNcxn6XzeNyU3qBMohvK

taitelee and others added 2 commits October 15, 2026 01:47
…parent

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbPNcxn6XzeNyU3qBMohvK
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbPNcxn6XzeNyU3qBMohvK
@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Review in 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: Advanced
  • Run ID: 386d26dd-a4ff-4490-ac29-f7d801ce3b38
📥 Commits

Reviewing files that changed from the base of the PR and between 975dbc8 and 59d2194.

📒 Files selected for processing: 3
  • docs/src/content/docs/configuration.mdx
  • internal/config/proxies.go
  • internal/config/proxies_test.go

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
🧰 Additional context used
📚 Code guidelines: 3
.github/prompts/docs-review.md — auto-discovered
AGENTS.md — auto-discovered
CONTRIBUTING.md — auto-discovered
📓 Path-based instructions: 9
Applicability reference: AGENTS.md: # AGENTS.md — AI Agent Instructions for WaveHouse Documentation *prose* — accuracy against the code, runnable examples, clarity, completeness — **and code↔docs sync** (code that changed...

📄 CodeRabbit inference engine (.github/prompts/docs-review.md)

Files:

  • docs/src/content/docs/configuration.mdx
Source excerpt: Source-of-truth pairs that must agree: Config struct tags in `internal/config/config.go` ↔ `docs/src/content/docs/configuration.mdx`, `config.yaml`, compose env blocks, `docs/src/content/docs/deployment.md`

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/src/content/docs/configuration.mdx
Source excerpt: **Package naming**: Lowercase, single word (or abbreviated).

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • internal/config/proxies_test.go
  • internal/config/proxies.go
Source excerpt: Create `*_test.go` files in the same package as the code under test.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • internal/config/proxies_test.go
Applicability reference: AGENTS.md: # AGENTS.md — AI Agent Instructions for WaveHouse **Scope** is the canonical docs-prose set from `scripts/docs-prose.sh` — a *denylist*: every tracked `.md`/`.mdx` EXCEPT `.claude/**`, `.github/**`, `CHAN...

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Files:

  • docs/src/content/docs/configuration.mdx
Source excerpt: **WH001 applies to every tracked Markdown file, with no carve-out** — `AGENTS.md`, `CHANGELOG.md`, `.github/` CI docs and `.claude/` agent prompts included.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/src/content/docs/configuration.mdx
Source excerpt: **In MDX, leave a blank line between a JSX tag and a code fence.**

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/src/content/docs/configuration.mdx
Source excerpt: **Go 1.26**, strict formatting (`gofumpt`, enforced by CI)

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • internal/config/proxies_test.go
  • internal/config/proxies.go
Cross-cutting habits that keep the codebase reviewable — they apply to every change, in every language.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/src/content/docs/configuration.mdx
  • internal/config/proxies_test.go
  • internal/config/proxies.go
🧠 Learnings: 1
📓 Common learnings
Learnt from: taitelee
URL: https://github.com/Wave-RF/WaveHouse/pull/855

Timestamp: 2026-10-20T04:35:53.094Z
Learning: In Wave-RF/WaveHouse, internal/api/clientip.go normalizes peer addresses with Unmap before trusted-proxy matching. Therefore, server.trusted_proxies entry 0.0.0.0/0 trusts every IPv4 peer, including IPv4-mapped IPv6 peers after normalization, while ::/0 trusts only IPv6 peers. internal/config/proxies.go emits one address-family-specific warning per /0 entry, and docs/src/content/docs/configuration.mdx documents that distinction.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-20T05:23:58.154Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Testing Conventions

- **Every new function should have corresponding test cases.** Run `make lint` and `make test` before considering work complete.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-20T05:23:58.154Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Testing Conventions

- **Coverage target**: 80% project-wide (CI enforces `threshold.total` in `.testcoverage.yml` against the merged unit + integration + e2e profile). Per-suite minima also enforced: unit 80%, integration 20%, e2e 60%, sdk 50%. Aim for 80%+ on new code. Coverage is published as PR comments via GitHub Code Quality — see `.github/workflows/README.md` "Coverage publishing"; the gate is unchanged.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-20T05:23:58.154Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Documentation Sync

Every code change should update the corresponding docs in the same PR. A code change without its doc update is incomplete.

| Change | Files to update |
| ------ | --------------- |
| Add/modify API endpoint | `docs/src/content/docs/api.md`, `README.md` (if user-facing) |
| Add/modify boot config option | `docs/src/content/docs/configuration.mdx`, `config.yaml`, `deployments/compose/*` env blocks, `docs/src/content/docs/deployment.md` |
| Add/modify a settings-directory key (`config.json`, `roles.json`, `policies.json`, `pipes.json`) | `docs/src/content/docs/settings-directory.mdx` (table **and** the example block), `pkg/settings/seed/*.json`, `deployments/compose/settings/*.json`, `tests/e2e/fixtures/settings/*.json`, `config.yaml` (the `settings:` comment enumerates the keys) |
| Change architecture / add a package | `docs/src/content/docs/architecture.md`, `AGENTS.md` |
| Change ingest / event ...
🔇 Additional comments: 3
internal/config/proxies.go: 1

67-69: LGTM!

Also applies to: 71-71, 73-78, 80-80, 82-82

internal/config/proxies_test.go: 1

66-66: LGTM!

Also applies to: 71-71, 73-77

docs/src/content/docs/configuration.mdx: 1

127-127: LGTM!

📝 Summary

Summary by CodeRabbit

  • New Features

    • Added configurable trusted proxies for resolving client addresses from X-Forwarded-For. Resolved addresses appear in request and audit logs and server spans, but not metric labels.
    • Trusted proxies can continue incoming traces; context from untrusted peers is linked to a new trace.
    • Configure trusted proxy addresses or CIDRs through YAML or an environment variable. Invalid entries prevent startup, and trusting every peer triggers a warning.
  • Documentation

    • Updated configuration, deployment, architecture, and reverse-proxy guidance with setup details and security considerations.

The change adds trusted-proxy configuration, resolves client addresses for request logs and spans, gates incoming trace-context continuation by peer trust, and documents the behavior.

Changes: Trusted proxy observability

Layer / File(s) Summary
Trusted proxy configuration
config.yaml, internal/config/*, docs/src/content/docs/configuration.mdx
Adds trusted-proxy address and CIDR parsing, validation, environment loading, configuration examples, and /0 warnings.
Client-address resolution and observability
internal/api/clientip.go, internal/observability/*, internal/auth/auth.go, internal/api/clientip_test.go, internal/observability/request_test.go, docs/src/content/docs/architecture.md, docs/src/content/docs/deployment.md
Uses the connection address unless the peer is trusted, then scans X-Forwarded-For from right to left. Adds the resolved address to request scope, logs, and spans.
Router integration and trace-context gating
internal/api/router.go, internal/app/wire*.go, internal/api/clientip_test.go, tests/integration/*, CHANGELOG.md, AGENTS.md, docs/src/content/docs/{architecture.md,reverse-proxy.mdx}
Passes trusted-proxy prefixes to both routers. Trusted peers can continue incoming trace context. Untrusted peers start a new trace and link the incoming context. Tests cover address attributes and trace behavior.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant APIRouter
  participant clientAddr
  participant TraceMiddleware
  participant RequestScope
  Client->>APIRouter: Send request with headers
  APIRouter->>clientAddr: Resolve address from peer and trusted forwarded chain
  clientAddr-->>APIRouter: Return resolved client address
  APIRouter->>TraceMiddleware: Apply peer-trust rule to traceparent
  TraceMiddleware-->>APIRouter: Continue trusted trace or link untrusted trace
  APIRouter->>RequestScope: Store client.address
Loading

Suggested reviewers: ericandrechek

Merge Risk: ⚪ Minimal · up to 59d21

No actionable merge-blocking risk remains from the reviewed changes.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 67.86% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 28 functions across 15 files. (1 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main changes: trusted-proxy client-address resolution and gated traceparent continuation.
Description check Passed The description directly explains the trusted-proxy configuration, client-address resolution, traceparent behavior, implementation, tests, and documentation updates.
Linked Issues check Passed The PR meets the coding requirements in [#333]. It adds empty-by-default trusted-proxy configuration with validation and family-specific /0 warnings. It resolves X-Forwarded-For right to left only…
Out of Scope Changes check Passed The changes remain within [#333]. Router wiring, integration loopback configuration, observability updates, configuration warnings, changelog entries, and documentation support trusted-proxy client-ad…
Full details: Linked Issues check

Explanation

The PR meets the coding requirements in [#333]. It adds empty-by-default trusted-proxy configuration with validation and family-specific /0 warnings. It resolves X-Forwarded-For right to left only for trusted peers and publishes the result as client.address in request logs and server spans. It gates traceparent continuation at the trusted-peer boundary and links context from untrusted peers. Tests cover proxy chains, spoofing, address formats, invalid input, logging, spans, and trace behavior. Documentation covers configuration, client-address handling, and trace continuation.

Full details: Out of Scope Changes check

Explanation

The changes remain within [#333]. Router wiring, integration loopback configuration, observability updates, configuration warnings, changelog entries, and documentation support trusted-proxy client-address resolution or trace-context gating. The PR does not demonstrate an unrelated feature or change.

Full details: Docstring Coverage

Explanation

Docstring coverage is 67.86% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 28 functions across 15 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
✨ Simplify code
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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 added documentation Improvements or additions to documentation go Pull requests that update go code area/observability Metrics, logs, traces, health, profiling area/api HTTP handlers, routing, middleware area/docs Documentation, site/, README area/app Process wiring (internal/app): component build, run, release labels Oct 15, 2026
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

📚 Docs preview

✅ Live: https://pr-855-wavehouse-docs.wave-rf.workers.dev (follows this PR)
This commit (59d2194): https://61edb376-wavehouse-docs.wave-rf.workers.dev (exactly this commit; open it if the link above looks stale)

  • Commit: 59d2194 fix(config): scope the /0 trusted-proxy warning to its address family
  • Author: @taitelee, Claude Opus 5.5 (1M context)
  • Committed: 2026-10-20 00:32 EDT
  • Uploaded: 2026-10-20 08:48 EDT (run)
Earlier commits (9)
Commit Preview (that commit only) Uploaded
975dbc8 https://77e80379-wavehouse-docs.wave-rf.workers.dev 2026-10-19 23:26 EDT
33de559 https://8f3625c0-wavehouse-docs.wave-rf.workers.dev 2026-10-19 22:13 EDT
a588661 https://d3beb1b0-wavehouse-docs.wave-rf.workers.dev 2026-10-19 21:15 EDT
62ac8e4 https://bc0c55d6-wavehouse-docs.wave-rf.workers.dev 2026-10-19 17:14 EDT
add675d https://2eb540d8-wavehouse-docs.wave-rf.workers.dev 2026-10-19 15:02 EDT
d85e854 https://c98d579b-wavehouse-docs.wave-rf.workers.dev 2026-10-19 12:51 EDT
3dc3574 https://87b56d4a-wavehouse-docs.wave-rf.workers.dev 2026-10-19 12:13 EDT
e27efd2 https://6ae96bcc-wavehouse-docs.wave-rf.workers.dev 2026-10-19 10:30 EDT
2b00a7f https://d202c395-wavehouse-docs.wave-rf.workers.dev 2026-10-14 21:59 EDT

@github-code-quality

github-code-quality Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Code coverage overview

Languages: Go

Go

main 4ef3ae1 claude/333-trusted-p... 59d2194 +/-
93% 93% 0%
View coverage of the 10 most impacted files.
File main 4ef3ae1 claude/333-trusted-p... 59d2194 +/-
internal/mq/embedded.go 90% 90% 0%
internal/ingest/worker.go 97% 97% 0%
internal/app/wire_ops.go 90% 90% 0%
internal/config/config.go 97% 97% 0%
internal/api/router.go 99% 99% 0%
internal/app/wire.go 93% 93% 0%
internal/config/backends.go 100% 100% 0%
internal/mq/nats_topology.go 91% 92% +1%
internal/api/clientip.go 0% 100% +100%
internal/config/proxies.go 0% 100% +100%

ℹ️ Calculations represent line coverage. Results may change as calculations are updated.


Updated October 20, 2026 12:49 UTC

taitelee and others added 2 commits October 19, 2026 14:23
…oxy-client-ip-cvhpw2

# Conflicts:
#	CHANGELOG.md
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbPNcxn6XzeNyU3qBMohvK
@taitelee

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@taitelee

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 2d9d17ff-db7b-4f41-b8c8-a81f4485f1a4
📥 Commits

Reviewing files that changed from the base of the PR and between 4ef3ae1 and e27efd2.

📒 Files selected for processing: 22
  • AGENTS.md
  • CHANGELOG.md
  • config.yaml
  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/configuration.mdx
  • docs/src/content/docs/deployment.md
  • docs/src/content/docs/reverse-proxy.mdx
  • internal/api/clientip.go
  • internal/api/clientip_test.go
  • internal/api/router.go
  • internal/app/wire.go
  • internal/app/wire_ops.go
  • internal/auth/auth.go
  • internal/config/backends.go
  • internal/config/config.go
  • internal/config/proxies.go
  • internal/config/proxies_test.go
  • internal/observability/logger.go
  • internal/observability/request.go
  • internal/observability/request_test.go
  • tests/integration/otel_test.go
  • tests/integration/setup_test.go

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Review details
🧰 Additional context used
📚 Code guidelines: 5
.github/prompts/docs-review.md — auto-discovered
docs/src/content/docs/architecture.md — auto-discovered
AGENTS.md — auto-discovered
.github/copilot-instructions.md — auto-discovered
CONTRIBUTING.md — auto-discovered
📓 Path-based instructions: 7
Applicability reference: AGENTS.md: # AGENTS.md — AI Agent Instructions for WaveHouse Documentation *prose* — accuracy against the code, runnable examples, clarity, completeness — **and code↔docs sync** (code that changed...

📄 CodeRabbit inference engine (.github/prompts/docs-review.md)

Files:

  • docs/src/content/docs/deployment.md
  • docs/src/content/docs/configuration.mdx
  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/reverse-proxy.mdx
Applicability reference: AGENTS.md: # AGENTS.md — AI Agent Instructions for WaveHouse The invariant index — what must stay true.

📄 CodeRabbit inference engine (docs/src/content/docs/architecture.md)

Files:

  • internal/app/wire.go
  • internal/observability/logger.go
  • internal/config/config.go
  • internal/observability/request_test.go
  • internal/observability/request.go
Source excerpt: **Observability invariants** — stdout always 100% (sampling is OTLP-push-only); WARN+ERROR always export at 100% (a non-configurable floor — don't expose it); gRPC OTel exporters dial lazily so an unreachable collector never...

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • internal/observability/logger.go
  • internal/observability/request_test.go
  • internal/observability/request.go
Source excerpt: **Bearer-token-only CORS posture (security)** — Bearer JWT on every request, no cookies/sessions; `corsMiddleware` deliberately **never** emits `Access-Control-Allow-Credentials` (not needed, and `*` + credentials is a spec...

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • internal/api/router.go
Source excerpt: **Sealed MQ boundary** — only `internal/mq` imports NATS/JetStream (`github.com/nats-io/…`), enforced by the `depguard` rule in `.golangci.yml`, so `make lint` fails on a leak in every package it builds, including the files...

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • internal/config/backends.go
  • internal/app/wire.go
  • internal/auth/auth.go
  • internal/observability/logger.go
  • internal/config/config.go
  • internal/observability/request_test.go
  • internal/config/proxies.go
  • internal/app/wire_ops.go
  • internal/api/clientip_test.go
  • internal/observability/request.go
  • internal/api/clientip.go
  • internal/api/router.go
  • internal/config/proxies_test.go
See [AGENTS.md](../AGENTS.md) for project conventions, architecture notes, and AI agent instructions.

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • AGENTS.md
Applicability reference: AGENTS.md: # AGENTS.md — AI Agent Instructions for WaveHouse **Scope** is the canonical docs-prose set from `scripts/docs-prose.sh` — a *denylist*: every tracked `.md`/`.mdx` EXCEPT `.claude/**`, `.github/**`, `CHAN...

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Files:

  • docs/src/content/docs/deployment.md
  • AGENTS.md
  • docs/src/content/docs/configuration.mdx
  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/reverse-proxy.mdx
  • CHANGELOG.md
🧠 Learnings: 4
📓 Common learnings
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-19T15:11:00.499Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Code Conventions

- **Go 1.26**, strict formatting (`gofumpt`, enforced by CI)
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-19T15:11:00.499Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Documentation Sync

Source-of-truth pairs that must agree:

- Config struct tags in `internal/config/config.go` ↔ `docs/src/content/docs/configuration.mdx`, `config.yaml`, compose env blocks, `docs/src/content/docs/deployment.md`
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-19T15:11:00.499Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Operating Rules

3. **Every code change updates its docs + `CHANGELOG.md` in the same PR** — a code change without its doc update is incomplete ([§Documentation Sync](#documentation-sync)).
📚 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/observability/request.go
  • internal/api/clientip.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:

  • CHANGELOG.md
📚 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/config/proxies_test.go
🪛 LanguageTool
docs/src/content/docs/architecture.md

[style] ~138-~138: Since ownership is already implied, this phrasing may be redundant.
Context: ...endisembedded, dedupe.lease plus its own ceiling to the next whole second (ceil...

(PRP_OWN)


[style] ~200-~200: Consider shortening this phrase to just ‘whether’, unless you mean ‘regardless of whether’.
Context: ...ogger in it too, so stdout carries them whether or not OTLP logs are on. otlpSamplerFn is ex...

(WHETHER)


[style] ~201-~201: Since ownership is already implied, this phrasing may be redundant.
Context: ...ds and spans alike; a record that names its own tenant keeps it. They are never metric ...

(PRP_OWN)

docs/src/content/docs/reverse-proxy.mdx

[style] ~201-~201: Since ownership is already implied, this phrasing may be redundant.
Context: ...arded-Proto/Host`** — set these for your own logs and any upstream that reads them. ...

(PRP_OWN)


[style] ~222-~222: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional.
Context: ..., ingress pods ``` How the client is chosen. When the connection comes from a lis...

(EN_REPEATEDWORDS_CHOOSE)

Comment thread docs/src/content/docs/reverse-proxy.mdx Outdated
Comment thread internal/api/router.go
Comment thread internal/config/proxies.go Outdated
@github-project-automation github-project-automation Bot moved this from Backlog to In review in WaveHouse Task Board Oct 19, 2026
taitelee and others added 4 commits October 19, 2026 15:22
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbPNcxn6XzeNyU3qBMohvK
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbPNcxn6XzeNyU3qBMohvK
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbPNcxn6XzeNyU3qBMohvK
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbPNcxn6XzeNyU3qBMohvK
@taitelee

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: Advanced
  • Run ID: 8d6743cc-138e-43ff-9193-5c1d9809da88
📥 Commits

Reviewing files that changed from the base of the PR and between e27efd2 and 3dc3574.

📒 Files selected for processing: 5
  • CHANGELOG.md
  • docs/src/content/docs/configuration.mdx
  • docs/src/content/docs/reverse-proxy.mdx
  • internal/config/proxies.go
  • internal/config/proxies_test.go

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Review details
🧰 Additional context used
📚 Code guidelines: 3
.github/prompts/docs-review.md — auto-discovered
AGENTS.md — auto-discovered
CONTRIBUTING.md — auto-discovered
📓 Path-based instructions: 9
Applicability reference: AGENTS.md: # AGENTS.md — AI Agent Instructions for WaveHouse Documentation *prose* — accuracy against the code, runnable examples, clarity, completeness — **and code↔docs sync** (code that changed...

📄 CodeRabbit inference engine (.github/prompts/docs-review.md)

Files:

  • docs/src/content/docs/reverse-proxy.mdx
  • docs/src/content/docs/configuration.mdx
Source excerpt: Source-of-truth pairs that must agree: Config struct tags in `internal/config/config.go` ↔ `docs/src/content/docs/configuration.mdx`, `config.yaml`, compose env blocks, `docs/src/content/docs/deployment.md`

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/src/content/docs/configuration.mdx
Source excerpt: Three invariants the docs site enforces in code, each of which fails quietly rather than loudly if you hand-write around it: **Opt a page into the Cloud CTA with `cloudCta` frontmatter**, not by importing the component.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/src/content/docs/reverse-proxy.mdx
  • docs/src/content/docs/configuration.mdx
Source excerpt: **Table-driven tests**: Use `tests := []struct{ name string; ...

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • internal/config/proxies_test.go
Applicability reference: AGENTS.md: # AGENTS.md — AI Agent Instructions for WaveHouse **Scope** is the canonical docs-prose set from `scripts/docs-prose.sh` — a *denylist*: every tracked `.md`/`.mdx` EXCEPT `.claude/**`, `.github/**`, `CHAN...

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Files:

  • CHANGELOG.md
  • docs/src/content/docs/reverse-proxy.mdx
  • docs/src/content/docs/configuration.mdx
Source excerpt: **In MDX, leave a blank line between a JSX tag and a code fence.**

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/src/content/docs/reverse-proxy.mdx
  • docs/src/content/docs/configuration.mdx
Source excerpt: **Go 1.26**, strict formatting (`gofumpt`, enforced by CI)

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • internal/config/proxies_test.go
  • internal/config/proxies.go
Source excerpt: **Editors see WH001 in `.md` only.**

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • CHANGELOG.md
Source excerpt: **Never hard-wrap prose.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • CHANGELOG.md
  • docs/src/content/docs/reverse-proxy.mdx
  • docs/src/content/docs/configuration.mdx
🪛 LanguageTool
docs/src/content/docs/reverse-proxy.mdx

[style] ~236-~236: Since ownership is already implied, this phrasing may be redundant.
Context: ...72.17.0.1`), and a proxy container from its own address on that network. To find the ad...

(PRP_OWN)

🔇 Additional comments: 4
internal/config/proxies.go: 1

4-4: LGTM!

Also applies to: 22-22, 29-29, 35-35, 42-42, 44-49

internal/config/proxies_test.go: 1

28-28: LGTM!

docs/src/content/docs/configuration.mdx: 1

162-162: LGTM!

CHANGELOG.md: 1

15-15: LGTM!

Also applies to: 30-30

Comment thread docs/src/content/docs/reverse-proxy.mdx Outdated
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbPNcxn6XzeNyU3qBMohvK
@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Member Author

@coderabbitai review


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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

Caution

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

⚠️ Outside diff range comments (1)

🟡 Minor · Apply environment overrides after reading YAML. · config.go:388-399

internal/config/config.go:388-399
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Apply environment overrides after reading YAML.

When a YAML file exists, Load calls cleanenv.ReadConfig but does not call cleanenv.ReadEnv. Therefore, WH_SERVER_TRUSTED_PROXIES is ignored when both sources are present. The YAML trusted-proxy policy is used instead of the documented environment override. Move cleanenv.ReadEnv(&cfg) after the YAML branch.

Suggested fix
 		if err := rejectUnknownKeys(path); err != nil {
 			return nil, err
 		}
-	} else {
-		if err := cleanenv.ReadEnv(&cfg); err != nil {
-			return nil, fmt.Errorf("read env: %w", err)
-		}
 	}
+	if err := cleanenv.ReadEnv(&cfg); err != nil {
+		return nil, fmt.Errorf("read env: %w", err)
+	}

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 5e67dd6b-01b3-4298-8870-f6e1b72f171c
📥 Commits

Reviewing files that changed from the base of the PR and between 62ac8e4 and a588661.

📒 Files selected for processing: 5
  • docs/src/content/docs/reverse-proxy.mdx
  • internal/api/clientip.go
  • internal/api/clientip_test.go
  • internal/observability/request.go
  • internal/observability/request_test.go

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Review details
🧰 Additional context used
📚 Code guidelines: 4
.github/prompts/docs-review.md — auto-discovered
docs/src/content/docs/architecture.md — auto-discovered
AGENTS.md — auto-discovered
CONTRIBUTING.md — auto-discovered
📓 Path-based instructions: 8
Applicability reference: AGENTS.md: # AGENTS.md — AI Agent Instructions for WaveHouse Documentation *prose* — accuracy against the code, runnable examples, clarity, completeness — **and code↔docs sync** (code that changed...

📄 CodeRabbit inference engine (.github/prompts/docs-review.md)

Files:

  • docs/src/content/docs/reverse-proxy.mdx
Applicability reference: AGENTS.md: # AGENTS.md — AI Agent Instructions for WaveHouse The invariant index — what must stay true.

📄 CodeRabbit inference engine (docs/src/content/docs/architecture.md)

Files:

  • internal/observability/request_test.go
  • internal/observability/request.go
Source excerpt: **Schema-driven ingest** — `POST /v1/ingest?table={table}` takes flat JSON, validated against the discovered schema (unknown fields rejected, types/nullability enforced).

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • internal/api/clientip_test.go
  • internal/api/clientip.go
Source excerpt: **Table-driven tests**: Use `tests := []struct{ name string; ...

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • internal/observability/request_test.go
  • internal/api/clientip_test.go
Applicability reference: AGENTS.md: # AGENTS.md — AI Agent Instructions for WaveHouse **Scope** is the canonical docs-prose set from `scripts/docs-prose.sh` — a *denylist*: every tracked `.md`/`.mdx` EXCEPT `.claude/**`, `.github/**`, `CHAN...

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Files:

  • docs/src/content/docs/reverse-proxy.mdx
Source excerpt: **Go 1.26**, strict formatting (`gofumpt`, enforced by CI) Source excerpt: **Structured logging** with `log/slog` (JSON handler), through the default logger: call `slog.InfoContext(ctx, …)` and its siblings (the context carr...

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • internal/observability/request_test.go
  • internal/observability/request.go
  • internal/api/clientip_test.go
  • internal/api/clientip.go
Source excerpt: **WH001 applies to every tracked Markdown file, with no carve-out** — `AGENTS.md`, `CHANGELOG.md`, `.github/` CI docs and `.claude/` agent prompts included.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/src/content/docs/reverse-proxy.mdx
Source excerpt: **In MDX, leave a blank line between a JSX tag and a code fence.**

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/src/content/docs/reverse-proxy.mdx
🧠 Learnings: 4
📓 Common learnings
Learnt from: taitelee
URL: https://github.com/Wave-RF/WaveHouse/pull/855

Timestamp: 2026-10-20T01:12:56.567Z
Learning: In Wave-RF/WaveHouse, the unresolved RemoteAddr issue in internal/api/clientip.go affected the server span's client.address, not request-scoped logs. The request scope was never populated from X-Forwarded-For directly. internal/observability/request.go omits empty client addresses from logs, while AnnotateSpan must overwrite otelhttp's header-derived client.address with an empty string when the peer cannot be resolved.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-20T02:03:28.428Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Documentation Sync

| Change | Files to update |
| ------ | --------------- |
| Add/modify API endpoint | `docs/src/content/docs/api.md`, `README.md` (if user-facing) |
| Add/modify boot config option | `docs/src/content/docs/configuration.mdx`, `config.yaml`, `deployments/compose/*` env blocks, `docs/src/content/docs/deployment.md` |
| Add/modify a settings-directory key (`config.json`, `roles.json`, `policies.json`, `pipes.json`) | `docs/src/content/docs/settings-directory.mdx` (table **and** the example block), `pkg/settings/seed/*.json`, `deployments/compose/settings/*.json`, `tests/e2e/fixtures/settings/*.json`, `config.yaml` (the `settings:` comment enumerates the keys) |
| Change architecture / add a package | `docs/src/content/docs/architecture.md`, `AGENTS.md` |
| Change ingest / event format | `docs/src/content/docs/api.md`, `docs/src/content/docs/deployment.md` (CH schema) |
| Change deployment / Docker | ...
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-20T02:03:28.428Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Testing Conventions

- **Every new function should have corresponding test cases.** Run `make lint` and `make test` before considering work complete.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-20T02:03:28.428Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Operating Rules

3. **Every code change updates its docs + `CHANGELOG.md` in the same PR** — a code change without its doc update is incomplete ([§Documentation Sync](#documentation-sync)).
📚 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/observability/request_test.go
📚 Learning: 2026-08-13T12:17:52.620Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 470
File: docs/src/content/docs/reverse-proxy.mdx:137-144
Timestamp: 2026-08-13T12:17:52.620Z
Learning: For Wave-RF/WaveHouse documentation, verify claims about implementation control flow against the authoritative implementation source (for example, internal/auth/auth.go) rather than relying solely on docs/** content. Documentation may lag behind or paraphrase behavior, so control-flow claims should be confirmed in source code.

Applied to files:

  • docs/src/content/docs/reverse-proxy.mdx
📚 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/clientip.go
🔇 Additional comments: 5
internal/api/clientip.go: 1

113-117: LGTM!

internal/observability/request.go: 1

65-67: LGTM!

Also applies to: 106-108

internal/observability/request_test.go: 1

198-207: LGTM!

internal/api/clientip_test.go: 1

123-125: LGTM!

docs/src/content/docs/reverse-proxy.mdx: 1

224-224: LGTM!

Comment thread docs/src/content/docs/reverse-proxy.mdx
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbPNcxn6XzeNyU3qBMohvK

Copy link
Copy Markdown
Member Author

@coderabbitai Answers to your review of a588661:

  • client.address log guarantee (reverse-proxy.mdx:224, deployment.md:766): fixed in 33de559. Both pages now say that a connection whose own address can't be read has no client address. Its log records leave client.address out, and its span's is empty. The reverse-proxy page also notes that WaveHouse listens on TCP, where the address is always readable.
  • Env overrides after YAML (config.go:388-399): no change needed. cleanenv.ReadConfig parses the file and then calls readEnvVars(cfg, false) (cleanenv.go:97-104), so WH_* variables, WH_SERVER_TRUSTED_PROXIES included, already override YAML when a file exists. The else branch covers only the no-file case. Moving ReadEnv out would read the environment twice.

Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Oct 20, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
fd '^(reverse-proxy\.mdx|deployment\.md|config\.go|go\.mod|cleanenv\.go)$'
ast-grep outline internal/config/config.go --match 'Load' --view expanded
rg -n -C 5 'address.*(read|empty)|client\.address|TCP' docs/src/content/docs/reverse-proxy.mdx docs/src/content/docs/deployment.md
sed -n '350,425p' internal/config/config.go
rg -n -C 2 'cleanenv|^replace' go.mod
version=$(awk '$1 == "github.com/ilyakaznacheev/cleanenv" {print $2; exit}' go.mod)
if [[ -n "$version" ]]; then
  curl --fail --silent --show-error --max-time 30 "https://raw.githubusercontent.com/ilyakaznacheev/cleanenv/${version}/cleanenv.go" |
    awk '/^func ReadConfig\(/ {show=1} show {print} show && /^}/ {exit}'
fi

Length of output: 48170


@taitelee Thanks for the fixes and the clarification. Inspection confirms both points:

  • Documentation: reverse-proxy.mdx and deployment.md now state that logs omit client.address when the connection address cannot be read. The reverse-proxy guide also documents the empty span attribute and the TCP context. This addresses the documentation finding.
  • Environment overrides: The pinned cleanenv v1.5.0 calls parseFile and then readEnvVars(cfg, false) in ReadConfig. The existing Load branches are correct. Moving ReadEnv outside the else branch would read the environment twice when a file exists. I withdraw this finding.

I did not run tests.


✏️ Learnings added
Learnt from: taitelee
URL: https://github.com/Wave-RF/WaveHouse/pull/855

Timestamp: 2026-10-20T02:11:40.241Z
Learning: In Wave-RF/WaveHouse, internal/config/config.go Load uses github.com/ilyakaznacheev/cleanenv v1.5.0. cleanenv.ReadConfig parses the configuration file and then calls readEnvVars(cfg, false), so bound WH_* environment variables, including WH_SERVER_TRUSTED_PROXIES, already override YAML values. The separate cleanenv.ReadEnv call belongs in the no-file branch; moving it outside that branch would read the environment twice when a file exists.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: taitelee
URL: https://github.com/Wave-RF/WaveHouse/pull/855

Timestamp: 2026-10-20T01:12:56.567Z
Learning: In Wave-RF/WaveHouse, the unresolved RemoteAddr issue in internal/api/clientip.go affected the server span's client.address, not request-scoped logs. The request scope was never populated from X-Forwarded-For directly. internal/observability/request.go omits empty client addresses from logs, while AnnotateSpan must overwrite otelhttp's header-derived client.address with an empty string when the peer cannot be resolved.

You are interacting with an AI system.

Copy link
Copy Markdown
Member Author

@coderabbitai review


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: Advanced
  • Run ID: ff7cf09f-128c-464d-bb41-6ce008be4999
📥 Commits

Reviewing files that changed from the base of the PR and between a588661 and 33de559.

📒 Files selected for processing: 2
  • docs/src/content/docs/deployment.md
  • docs/src/content/docs/reverse-proxy.mdx

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Review details
🧰 Additional context used
📚 Code guidelines: 3
.github/prompts/docs-review.md — auto-discovered
CONTRIBUTING.md — auto-discovered
AGENTS.md — auto-discovered
📓 Path-based instructions: 4
Applicability reference: AGENTS.md: # AGENTS.md — AI Agent Instructions for WaveHouse Documentation *prose* — accuracy against the code, runnable examples, clarity, completeness — **and code↔docs sync** (code that changed...

📄 CodeRabbit inference engine (.github/prompts/docs-review.md)

Files:

  • docs/src/content/docs/deployment.md
  • docs/src/content/docs/reverse-proxy.mdx
Applicability reference: AGENTS.md: # AGENTS.md — AI Agent Instructions for WaveHouse **Scope** is the canonical docs-prose set from `scripts/docs-prose.sh` — a *denylist*: every tracked `.md`/`.mdx` EXCEPT `.claude/**`, `.github/**`, `CHAN...

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Files:

  • docs/src/content/docs/deployment.md
  • docs/src/content/docs/reverse-proxy.mdx
Source excerpt: **Never hard-wrap prose.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/src/content/docs/deployment.md
  • docs/src/content/docs/reverse-proxy.mdx
Source excerpt: **In MDX, leave a blank line between a JSX tag and a code fence.**

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/src/content/docs/reverse-proxy.mdx
🧠 Learnings: 2
📓 Common learnings
Learnt from: taitelee
URL: https://github.com/Wave-RF/WaveHouse/pull/855

Timestamp: 2026-10-20T01:12:56.567Z
Learning: In Wave-RF/WaveHouse, the unresolved RemoteAddr issue in internal/api/clientip.go affected the server span's client.address, not request-scoped logs. The request scope was never populated from X-Forwarded-For directly. internal/observability/request.go omits empty client addresses from logs, while AnnotateSpan must overwrite otelhttp's header-derived client.address with an empty string when the peer cannot be resolved.
Learnt from: taitelee
URL: https://github.com/Wave-RF/WaveHouse/pull/855

Timestamp: 2026-10-20T02:11:42.589Z
Learning: In Wave-RF/WaveHouse, internal/config/config.go Load uses github.com/ilyakaznacheev/cleanenv v1.5.0. cleanenv.ReadConfig parses the configuration file and then calls readEnvVars(cfg, false), so bound WH_* environment variables, including WH_SERVER_TRUSTED_PROXIES, already override YAML values. The separate cleanenv.ReadEnv call belongs in the no-file branch; moving it outside that branch would read the environment twice when a file exists.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-20T03:17:13.125Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Documentation Sync

| Change | Files to update |
| ------ | --------------- |
| Any notable change | `CHANGELOG.md` under `[Unreleased]` |
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-20T03:17:13.125Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Documentation Sync

Source-of-truth pairs that must agree:

- Config struct tags in `internal/config/config.go` ↔ `docs/src/content/docs/configuration.mdx`, `config.yaml`, compose env blocks, `docs/src/content/docs/deployment.md`
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-20T03:17:13.125Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Documentation Sync

### Markdown authoring rules

- **WH001 applies to every tracked Markdown file, with no carve-out** — `AGENTS.md`, `CHANGELOG.md`, `.github/` CI docs and `.claude/` agent prompts included. Path-scoped `.markdownlint.json` files under `.github/` and `.claude/` used to switch it off there; [#521](https://github.com/Wave-RF/WaveHouse/issues/521) deleted them, because a config that silently contradicts `CONTRIBUTING.md` teaches a reviewer reading the config the wrong invariant. Don't reintroduce one — if a file genuinely can't satisfy WH001, say why in a `markdownlint-disable` comment in the file itself, where the next reader sees it. This makes WH001 *broader* than `scripts/docs-prose.sh`, which still skips `.github/` and `.claude/`: mechanical style is cheap to enforce everywhere, a prose review is not.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-20T03:17:13.125Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Documentation Sync

| Change | Files to update |
| ------ | --------------- |
| Add/modify boot config option | `docs/src/content/docs/configuration.mdx`, `config.yaml`, `deployments/compose/*` env blocks, `docs/src/content/docs/deployment.md` |
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-20T03:17:13.125Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Operating Rules

3. **Every code change updates its docs + `CHANGELOG.md` in the same PR** — a code change without its doc update is incomplete ([§Documentation Sync](#documentation-sync)).
📚 Learning: 2026-08-13T12:17:52.620Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 470
File: docs/src/content/docs/reverse-proxy.mdx:137-144
Timestamp: 2026-08-13T12:17:52.620Z
Learning: For Wave-RF/WaveHouse documentation, verify claims about implementation control flow against the authoritative implementation source (for example, internal/auth/auth.go) rather than relying solely on docs/** content. Documentation may lag behind or paraphrase behavior, so control-flow claims should be confirmed in source code.

Applied to files:

  • docs/src/content/docs/reverse-proxy.mdx
🔇 Additional comments: 2
docs/src/content/docs/deployment.md: 1

766-766: LGTM!

docs/src/content/docs/reverse-proxy.mdx: 1

226-226: LGTM!

Comment thread docs/src/content/docs/deployment.md Outdated
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbPNcxn6XzeNyU3qBMohvK

Copy link
Copy Markdown
Member Author

@coderabbitai review


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

Caution

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

⚠️ Outside diff range comments (1)

🟡 Minor · Qualify /0 trust by address family. · proxies.go:67-73

internal/config/proxies.go:67-73
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Qualify /0 trust by address family.

The matcher trusts a peer only when p.Contains(a) succeeds. With only 0.0.0.0/0, an IPv6 peer is not trusted, so clientAddr returns the peer address without reading X-Forwarded-For. The warning and configuration documentation say “every peer,” which overstates the behavior and can mislead operators configuring a single-family proxy. Scope both statements to the entry’s address family.

Suggested fix
--- a/internal/config/proxies.go
+++ b/internal/config/proxies.go
@@
-// trustedProxyWarnings flags a /0 entry: it trusts every peer, so a client
-// reaching the port directly chooses the address it is logged under.
+// trustedProxyWarnings flags a /0 entry: it trusts every peer in its address
+// family, so a direct client in that family chooses the address it is logged under.
@@
-			return []string{"server.trusted_proxies includes " + p.String() + ": every peer is trusted, so a client that reaches server.port directly sets its own client.address through X-Forwarded-For and has its traceparent adopted; list only your proxies' addresses"}
+			return []string{"server.trusted_proxies includes " + p.String() + ": every peer in that address family is trusted, so a client in that family that reaches server.port directly sets its own client.address through X-Forwarded-For and has its traceparent adopted; list only your proxies' addresses"}
--- a/docs/src/content/docs/configuration.mdx
+++ b/docs/src/content/docs/configuration.mdx
@@
-- **`server.trusted_proxies` with a `/0` entry** (`0.0.0.0/0`, `::/0`), in every process: every peer is trusted, so a client that reaches `server.port` directly picks its own `client.address` and joins its `traceparent` to your traces (see [Reverse proxy](/reverse-proxy#client-address-and-trace-context)).
+- **`server.trusted_proxies` with a `/0` entry** (`0.0.0.0/0`, `::/0`), in every process: every peer in that entry's address family is trusted, so a client in that family that reaches `server.port` directly picks its own `client.address` and joins its `traceparent` to your traces (see [Reverse proxy](/reverse-proxy#client-address-and-trace-context)).

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 404355bc-0717-4c61-8bc0-5f85b2e9c98c
📥 Commits

Reviewing files that changed from the base of the PR and between 33de559 and 975dbc8.

📒 Files selected for processing: 1
  • docs/src/content/docs/deployment.md

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Review details
🧰 Additional context used
📚 Code guidelines: 3
.github/prompts/docs-review.md — auto-discovered
CONTRIBUTING.md — auto-discovered
AGENTS.md — auto-discovered
📓 Path-based instructions: 4
Applicability reference: AGENTS.md: # AGENTS.md — AI Agent Instructions for WaveHouse Documentation *prose* — accuracy against the code, runnable examples, clarity, completeness — **and code↔docs sync** (code that changed...

📄 CodeRabbit inference engine (.github/prompts/docs-review.md)

Files:

  • docs/src/content/docs/deployment.md
Applicability reference: AGENTS.md: # AGENTS.md — AI Agent Instructions for WaveHouse **Scope** is the canonical docs-prose set from `scripts/docs-prose.sh` — a *denylist*: every tracked `.md`/`.mdx` EXCEPT `.claude/**`, `.github/**`, `CHAN...

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Files:

  • docs/src/content/docs/deployment.md
Source excerpt: **Never hard-wrap prose.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/src/content/docs/deployment.md
Cross-cutting habits that keep the codebase reviewable — they apply to every change, in every language.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/src/content/docs/deployment.md
🧠 Learnings: 1
📓 Common learnings
Learnt from: taitelee
URL: https://github.com/Wave-RF/WaveHouse/pull/855

Timestamp: 2026-10-20T01:12:56.567Z
Learning: In Wave-RF/WaveHouse, the unresolved RemoteAddr issue in internal/api/clientip.go affected the server span's client.address, not request-scoped logs. The request scope was never populated from X-Forwarded-For directly. internal/observability/request.go omits empty client addresses from logs, while AnnotateSpan must overwrite otelhttp's header-derived client.address with an empty string when the peer cannot be resolved.
Learnt from: taitelee
URL: https://github.com/Wave-RF/WaveHouse/pull/855

Timestamp: 2026-10-20T02:11:42.589Z
Learning: In Wave-RF/WaveHouse, internal/config/config.go Load uses github.com/ilyakaznacheev/cleanenv v1.5.0. cleanenv.ReadConfig parses the configuration file and then calls readEnvVars(cfg, false), so bound WH_* environment variables, including WH_SERVER_TRUSTED_PROXIES, already override YAML values. The separate cleanenv.ReadEnv call belongs in the no-file branch; moving it outside that branch would read the environment twice when a file exists.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-20T04:20:32.371Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Documentation Sync

Source-of-truth pairs that must agree:

- Config struct tags in `internal/config/config.go` ↔ `docs/src/content/docs/configuration.mdx`, `config.yaml`, compose env blocks, `docs/src/content/docs/deployment.md`
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-20T04:20:32.371Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Testing Conventions

- **Every new function should have corresponding test cases.** Run `make lint` and `make test` before considering work complete.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-20T04:20:32.371Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Operating Rules

3. **Every code change updates its docs + `CHANGELOG.md` in the same PR** — a code change without its doc update is incomplete ([§Documentation Sync](#documentation-sync)).
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-20T04:20:32.371Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Documentation Sync

### Markdown authoring rules

- **WH001 applies to every tracked Markdown file, with no carve-out** — `AGENTS.md`, `CHANGELOG.md`, `.github/` CI docs and `.claude/` agent prompts included. Path-scoped `.markdownlint.json` files under `.github/` and `.claude/` used to switch it off there; [#521](https://github.com/Wave-RF/WaveHouse/issues/521) deleted them, because a config that silently contradicts `CONTRIBUTING.md` teaches a reviewer reading the config the wrong invariant. Don't reintroduce one — if a file genuinely can't satisfy WH001, say why in a `markdownlint-disable` comment in the file itself, where the next reader sees it. This makes WH001 *broader* than `scripts/docs-prose.sh`, which still skips `.github/` and `.claude/`: mechanical style is cheap to enforce everywhere, a prose review is not.
🔇 Additional comments: 1
docs/src/content/docs/deployment.md: 1

766-766: LGTM!

coderabbitai[bot]
coderabbitai Bot previously approved these changes Oct 20, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UbPNcxn6XzeNyU3qBMohvK

Copy link
Copy Markdown
Member Author

@coderabbitai Thanks, the outside-diff finding on internal/config/proxies.go:67-73 (qualify /0 trust by address family) is fixed in 59d2194. Peers are unmapped before they are matched, so 0.0.0.0/0 covers only IPv4 peers and ::/0 only IPv6 ones.

  • Warning: there is now one warning for each /0 entry, and it names that entry's family ("every IPv4 peer is trusted, so an IPv4 client that reaches server.port directly…").
  • Docs: the configuration.mdx bullet says the same, so listing both entries gives two warnings.
  • Tests: TestWarnings_TrustEveryPeer checks each family on its own and both entries together.

Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Oct 20, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
fd '^(proxies\.go|proxies_test\.go|configuration\.mdx|clientip\.go)$'
ast-grep outline internal/config/proxies.go
ast-grep outline internal/config/proxies_test.go --match TestWarnings_TrustEveryPeer --view expanded
ast-grep outline internal/api/clientip.go
sed -n '1,140p' internal/config/proxies.go
rg -n -A65 -B3 'func TestWarnings_TrustEveryPeer\(' internal/config/proxies_test.go
rg -n -C4 '/0|every IPv4|every IPv6' docs/src/content/docs/configuration.mdx
sed -n '1,150p' internal/api/clientip.go

Length of output: 10530


@taitelee Thanks for the fix and the clarification. Code inspection confirms the finding is addressed:

  • trustedProxyWarnings emits one warning per /0 entry and names its address family.
  • configuration.mdx documents the same family-specific behavior.
  • TestWarnings_TrustEveryPeer checks each family separately and checks that both entries produce two warnings.
  • Peer normalization unmaps IPv4-mapped IPv6 addresses before matching.

I did not run tests.


✏️ Learnings added
Learnt from: taitelee
URL: https://github.com/Wave-RF/WaveHouse/pull/855

Timestamp: 2026-10-20T04:35:50.907Z
Learning: In Wave-RF/WaveHouse, internal/api/clientip.go normalizes peer addresses with Unmap before trusted-proxy matching. Therefore, server.trusted_proxies entry 0.0.0.0/0 trusts every IPv4 peer, including IPv4-mapped IPv6 peers after normalization, while ::/0 trusts only IPv6 peers. internal/config/proxies.go emits one address-family-specific warning per /0 entry, and docs/src/content/docs/configuration.mdx documents that distinction.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

Copy link
Copy Markdown
Member Author

@coderabbitai review


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@taitelee
taitelee marked this pull request as ready for review October 20, 2026 12:45
@taitelee
taitelee requested review from a team and EricAndrechek October 20, 2026 12:45
@taitelee
taitelee merged commit 6179c67 into main Oct 20, 2026
34 checks passed
@taitelee
taitelee deleted the claude/333-trusted-proxy-client-ip-cvhpw2 branch October 20, 2026 13:04
@github-project-automation github-project-automation Bot moved this from In review to Done in WaveHouse Task Board Oct 20, 2026
taitelee added a commit that referenced this pull request Oct 20, 2026
…ls (#866)

<!-- claude-projects-attribution: {"github_login":"taitelee"} -->
_Requested by **Taite** · [project
thread](https://claude.ai/code/project/chan_019fe1KaM5pLaH8s2bVcLf4T?thread=cmsg_019fe1KaM5pLaH8s2bVcLf4TDQK62Tr5rS3frnE6fVLTPT)_

## Summary

The first tier of #94. Before this, the only request metrics came from
otelhttp. Those carry no route, so a slow or failing endpoint could not
be told apart from the rest. Nothing timed the statements the query
paths send to ClickHouse.

- `wavehouse_http_request_duration_seconds{route, method, status_class}`
times every request on `server.port` except SSE streams. The middleware
lives in `newProbeRouter`, so both the API router and the ops-only
router get it. It sits outside `jsonRecoverer`, so a panic counts as a
`5xx`. `route` is the chi pattern (`/v1/pipes/{name}`), never the path.
It is the mount's own pattern (`/v1/*`, `/v1/ops/*`) for a request a
mount answers short of a route, and `unmatched` for one that reached no
mount. `method` is net/http's or `other`.
- `wavehouse_clickhouse_query_duration_seconds{operation, result}` times
each statement sent by `POST /v1/query` (`query`), a pipe (`pipe`,
`pipe_write`) or `POST /v1/ops/query` (`ops_query`). `result` is `ok`,
`canceled` (the client went away), or the response's error code without
its `clickhouse.` prefix, through the same `chFailureOf` the response
uses. It is recorded inside the singleflight closure, so a cache hit
records nothing and coalesced requests record once.
- A histogram's `_count` gives both the rate and the error rate, so
there is no separate `_errors_total`. Neither metric carries a tenant or
table label.
- The docs are in deployment.md § Request latency and ClickHouse queries
and in architecture.md. The CHANGELOG and AGENTS.md are updated too.

This touches `router.go` (one line), which #855 also edits. It should
merge after #855.

## Test plan

- [x] `internal/api/metrics_test.go` covers:
- Route-pattern labeling: pattern not path, `/v1/*`, `/v1/ops/*`,
`unmatched`. Panic as 5xx, `/v1/stream` skipped.
  - The ops router's wiring.
- Every row of the ClickHouse error matrix, across `query`, `pipe` and
`pipe_write`.
- The proxy's ok, rejected and response-cap paths, plus a refused
request recording nothing.
  - Client gone vs. the time-cap backstop.
  - Nil no-op.
- [x] `go test -race ./internal/api/ ./internal/app/` and golangci-lint
on `./internal/api/...`
- [ ] GitHub CI

## Related Issues

Part of #94, part of #777. The worker's `INSERT` timing, the next tier,
waits on #770.

---
_Generated by [Claude
Code](https://claude.ai/code/session_019YVNGPfArHqjc3Xt6N9App)_

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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/app Process wiring (internal/app): component build, run, release area/docs Documentation, site/, README area/observability Metrics, logs, traces, health, profiling documentation Improvements or additions to documentation go Pull requests that update go code

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

feat(api): trusted-proxy-aware client IP in traces & logs (+ gate trace-context propagation)

1 participant