fix(api): send X-Keenable-Title on every request (unbreak free-tier) - #45
Conversation
The backend now requires X-Keenable-Title on token-less (public) endpoints and rejects requests without it (400 "Missing app identifier"). The CLI sent no such header, so the unauthenticated free-tier flow (search/fetch/ feedback with no API key) broke against production, and the nightly e2e free-tier + logout-fallback tests went red. Send X-Keenable-Title on both the api-key and bare clients, defaulting to "keenable-cli". The value is overridable via KEENABLE_APP_TITLE so first-party automation can self-identify: the e2e workflow sets "keenable-cli-e2e", letting Grafana tell CI traffic apart from real CLI users (both land in the server-side app_title field). The daemon inherits the env var, so daemon-routed requests carry the same title. Tests: pure resolve_app_title unit tests; free-tier and logout-fallback e2e now assert the public call never fails with "Missing app identifier", locking in the contract so a dropped header fails loudly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code Review by Qodo
1. Login misses app title
|
PR Summary by Qodofix(api): always send X-Keenable-Title to restore free-tier requests Description
Diagram
High-Level Assessment
Files changed (4)
|
| pub fn bare_client() -> Client { | ||
| Client::builder() | ||
| .user_agent(USER_AGENT) | ||
| .default_headers(base_headers()) | ||
| .timeout(std::time::Duration::from_secs(60)) | ||
| .build() | ||
| .unwrap() |
There was a problem hiding this comment.
1. Login misses app title 🐞 Bug ≡ Correctness
src/commands/login.rs uses reqwest::Client::new() for /v1/auth/agent/code and /v1/auth/agent/token, so these token-less requests do not include X-Keenable-Title even after this PR. If the backend enforces the header for all token-less endpoints (not only /public), login can fail with 400 "Missing app identifier" and users cannot authenticate.
Agent Prompt
## Issue description
The PR adds X-Keenable-Title only to `api_key_client()`/`bare_client()`, but the login flow constructs its own `reqwest::Client` and will therefore omit the header on token-less auth endpoints.
## Issue Context
`src/commands/login.rs` uses `Client::new()` in both `request_code()` and `poll_for_token()`. Those should use a client that includes the new base headers (and ideally the same user-agent).
## Fix Focus Areas
- src/commands/login.rs[27-45]
- src/commands/login.rs[69-106]
- src/api.rs[130-137]
## Suggested fix
- Replace `Client::new()` in login with `crate::api::bare_client()` (or add a small `crate::api::public_client()` wrapper if you don’t want the 60s default timeout).
- Keep the existing per-request `.timeout(Duration::from_secs(10))` calls (they already override defaults).
- Optionally add/adjust a unit/integration test that asserts login endpoints include X-Keenable-Title (if your test harness can observe request headers).
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Problem
The backend now requires the
X-Keenable-Titleheader on token-less (public) endpoints and rejects requests without it with400 Missing app identifier(keenable-backend-ts commitsce1029e/78ddd22, deployed20260615220413). The CLI sent no such header, so the unauthenticated free-tier flow broke against production, and the 2026-06-16 nightly e2e went red on:test_free_tier_search_hits_public_endpointtest_free_tier_fetch_hits_public_endpointtest_logout_clears_key_and_falls_back_to_publicFix
src/api.rs: sendX-Keenable-Titleon both clients (api_key_client,bare_client), defaulting tokeenable-cli.KEENABLE_APP_TITLEenv var.e2e.ymlsets it tokeenable-cli-e2e, so first-party CI traffic can be told apart from real CLI users in Grafana via the server-sideapp_titlefield (keenable-clivskeenable-cli-e2e). The daemon inherits the env var, so daemon-routed requests carry the same title.Tests
resolve_app_titleunit tests (default / trim / blank handling).Missing app identifier, so a dropped header fails loudly.Verified locally against the live API: free-tier
search/fetchreturn 200 (both with the default title and with the e2e title via the daemon); the 3 free-tier tests pass. Login tests requireKEENABLE_API_KEYand will run in CI.🤖 Generated with Claude Code