feat(auth): optional WAVE_INSTALL_CHANNEL -> X-Wave-Install-Channel header (E2 usage-attribution) - #93
feat(auth): optional WAVE_INSTALL_CHANNEL -> X-Wave-Install-Channel header (E2 usage-attribution)#93yakimoto wants to merge 1 commit into
Conversation
…hannel header Client-side half of E2 usage-attribution (see the companion wave-gateway PR feat/usage-attribution-e2, and governance/plans/wave-skills-distribution/ E2-USAGE-ATTRIBUTION.md on wave-av/claude-workstation). getAuthHeaders() now adds X-Wave-Install-Channel when WAVE_INSTALL_CHANNEL is set in the environment, so a Skill/manifest-generated onboarding config can self-declare its install channel (e.g. skill-manifest) apart from a hand-written docs install (docs-manual), which wave-gateway allowlists and folds into a non-billing usage-ledger attribution dimension for quarterly reporting. Unset by default -- byte-identical to today for anyone who does not set it (verified manually: with the env var set the header is added, unset it is absent, everything else unchanged). No test runner exists on this repo main (confirmed) -- verified via type-check + lint (both clean) and a direct tsx smoke-run of getAuthHeaders() with/without the env var. Branched from origin/main rather than PR #92 (fix/gateway-base-url-91) to avoid taking a dependency on unreviewed, unmerged code -- a rebase conflict there is preferable. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_ab394472-db62-40e9-8fe4-25cc7b7097e3) |
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 49 minutes Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
Comment |
PR Summary by QodoAdd optional install-channel header for usage attribution
AI Description
Diagram
High-Level Assessment
Files changed (3)
|
| | --- | --- | --- | --- | | ||
| | `WAVE_API_KEY` | Yes | - | Your WAVE API key | | ||
| | `WAVE_BASE_URL` | No | `https://wave.online` | API base URL | | ||
| | `WAVE_INSTALL_CHANNEL` | No | - | Self-declared install-channel label sent as `X-Wave-Install-Channel` (WAVE-internal usage-attribution reporting; safe to leave unset) | |
There was a problem hiding this comment.
🔍 README env-var table edited by hand but generated from .wave/repo.json
README.md states it is machine-generated from the grounded SSOT and verified by npm run verify. The new WAVE_INSTALL_CHANNEL row was added directly to README.md, but the corresponding "Environment variables" table in .wave/repo.json:429-448 still lists only WAVE_API_KEY and WAVE_BASE_URL. The next regeneration/verification pass will either drop this row or fail the check. Consider updating the SSOT entry as well.
Was this helpful? React with 👍 or 👎 to provide feedback.
| const installChannel = process.env["WAVE_INSTALL_CHANNEL"]; | ||
| if (installChannel) headers[INSTALL_CHANNEL_HEADER] = installChannel; |
There was a problem hiding this comment.
🟨 Unvalidated environment value forwarded verbatim as an outbound HTTP header
WAVE_INSTALL_CHANNEL is copied straight into the X-Wave-Install-Channel header (src/auth.ts:40-41) with no format validation. Values containing CR/LF or other illegal header characters cause fetch to throw a TypeError, breaking every API call; arbitrary values are also sent upstream unfiltered (server-side allowlisting is only documented, not enforced here).
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Risk: medium. Left a non-blocking comment (not approved): Cursor Bugbot and Cursor Security Agent both completed as skipped, so required automated-review signals are incomplete. Human review is needed; no additional reviewers could be assigned (only the PR author is assignable).
Sent by Cursor Approval Agent: Pull Request Router and Approver
Code Review by Qodo
1. Invalid header value crash
|
| const installChannel = process.env["WAVE_INSTALL_CHANNEL"]; | ||
| if (installChannel) headers[INSTALL_CHANNEL_HEADER] = installChannel; | ||
| return headers; |
There was a problem hiding this comment.
1. Invalid header value crash 🐞 Bug ☼ Reliability
getAuthHeaders() copies WAVE_INSTALL_CHANNEL verbatim into X-Wave-Install-Channel; if the value contains characters invalid for HTTP headers (e.g., CR/LF), fetch() header construction can throw/reject and break API calls. The code paths that call fetch() with these headers don’t locally guard against this, so the error will propagate to callers at runtime.
Agent Prompt
## Issue description
`WAVE_INSTALL_CHANNEL` is injected into an outbound HTTP header without validation. If it contains illegal header characters (notably `\r` / `\n`), the runtime’s `fetch()`/`Headers` construction can throw/reject, causing request failures.
## Issue Context
`getAuthHeaders()` is used broadly as the default auth header source for API calls. Adding a quick validation here prevents configuration-induced runtime failures and gives operators a clear error message.
## Fix Focus Areas
- src/auth.ts[34-42]
## Suggested fix
- Before setting `headers["X-Wave-Install-Channel"]`, validate the env value:
- Reject (throw a descriptive Error) or ignore (treat as unset) when it contains `\r` or `\n` (and optionally other control characters).
- Optionally cap length to a reasonable size to avoid oversized headers.
- Keep behavior unchanged when the variable is unset.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Qodo FixerNo findings are within the configured fix scope. To change which findings are fixed, adjust the setting on your Qodo configuration page. |


What
Client-side half of E2 usage-attribution — the measurement mechanism the economist
synthesis named to resolve the WATCH on funding a public WAVE Skills showcase repo
(not built here). Companion PR on
wave-av/wave-gateway:feat/usage-attribution-e2(server-side allowlist + ledger threading). Fullgrounding + honest limitations:
E2-USAGE-ATTRIBUTION.md(companion PR onwave-av/claude-workstation,governance/plans/wave-skills-distribution/).getAuthHeaders()now addsX-Wave-Install-ChannelwhenWAVE_INSTALL_CHANNELisset in the environment. This lets a Skill/manifest-generated onboarding config
self-declare its install channel (e.g.
skill-manifest) apart from a hand-writtendocs install (
docs-manual) — wave-gateway allowlists the value and folds it into anon-billing usage-ledger attribution dimension for quarterly reporting.
Unset by default — byte-identical to today for anyone who doesn't set it.
Why this exists (grounding, not a guess)
No MCP client (Claude Code, Cursor, or otherwise) surfaces a genuine install-time
referrer — confirmed via WebSearch against current MCP spec docs. This package's own
getAuthHeaders()currently sends a staticUser-Agent: wave-mcp-server/0.1.0identical regardless of how the customer found WAVE, so today there is literally no
way to distinguish "discovered via the manifest" from "hand-installed after reading
docs" at the request layer.
WAVE_INSTALL_CHANNELis a new, WAVE-controlled,self-declared tag (like a UTM parameter) — not a network-verified signal, and the
phase file says so plainly.
Scope note
The other half of this tag — WAVE's manifest-generated onboarding snippet actually
setting
WAVE_INSTALL_CHANNEL=skill-manifestin the config it hands customers —belongs in
wave-docs-www#67(already shipped this session perdocs/wave-skills-distribution-e1-onboarding). Not touched here: that's a fourth reponot inspected in this pass; flagged as a fast-follow in the phase file instead of
built blind.
Branching note
Branched from
origin/main, not PR #92 (fix/gateway-base-url-91), to avoid taking adependency on unreviewed, unmerged code touching the same file (
src/auth.ts). A smallrebase conflict when #92 lands is preferable to stacking on an unreviewed PR.
Tests
This repo has no test runner on
main(confirmed — notestscript, no testfiles). Verified via:
npm run type-check— cleannpm run lint— clean (0 warnings)tsxexecution ofgetAuthHeaders()with and withoutWAVE_INSTALL_CHANNELset (output above) — proves the additive, byte-identical-when-unset behavior.
Also updated
README.md's environment-variable table andCHANGELOG.md.Deploy
No deploy — this is an npm package change; publishing a new version to npm is a
separate, explicit step not taken here. GitHub Actions is in a platform-wide outage
right now (per this task's brief) — not waiting on CI for that reason.
🤖 Generated with Claude Code
Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
Note
Low Risk
Additive optional header on auth utilities with no change when unset; no auth or billing logic modified in this repo.
Overview
Adds optional install-channel attribution on outbound WAVE API calls: when
WAVE_INSTALL_CHANNELis set,getAuthHeaders()includesX-Wave-Install-Channelon every request (viawaveFetchWithRateLimitand directgetAuthHeaders()usage). Unset env leaves headers byte-identical to current behavior.README documents the new variable; CHANGELOG records it under Unreleased → Added. Server-side allowlisting and ledger threading live in the companion wave-gateway work; this PR is the MCP client half for manifest vs manual install tagging in quarterly usage reporting.
Reviewed by Cursor Bugbot for commit f26adbe. Configure here.