Skip to content

feat: attribute requests to clients and API keys in logs - #265

Merged
Zingzy merged 2 commits into
mainfrom
feat/client-source-telemetry
Jul 22, 2026
Merged

feat: attribute requests to clients and API keys in logs#265
Zingzy merged 2 commits into
mainfrom
feat/client-source-telemetry

Conversation

@Zingzy

@Zingzy Zingzy commented Jul 22, 2026

Copy link
Copy Markdown
Member

What

Adds request source attribution to the structured logs.

  • New X-Spoo-Client request header, parsed in the logging middleware and bound to the log context as client and client_version. The value is <slug> or <slug>/<version> (for example snap/2.1.0). First party clients will send dashboard, landing, snap, raycast, cli, and bot. Values that do not match the shape are treated as absent, never rejected.
  • API key auth now binds key_id and key_prefix to the log context, so traffic and service events can be attributed to a specific key instead of just a user.
  • Log redaction now uses an explicit safe list for field names that trip the substring heuristic but carry no secrets (has_password, password_protected, key_id, key_prefix, token_prefix, query_keys). Previously these were logged as ***REDACTED***, which made password usage and key identifiers unqueryable.
  • _auth_kind now recognizes the access_token cookie as jwt_cookie. Cookie authenticated browser sessions were previously classified as anonymous.
  • X-Spoo-Client added to CORS allowed headers so browser clients can send it.

Why

Log queries can currently tell API key traffic from JWT traffic, but not which client an anonymous or authenticated request came from, nor which specific key produced it. This makes questions like where links are created from and which key is responsible for a traffic spike answerable with a group by.

Notes

Summary by CodeRabbit

  • Logging
    • Added client and client-version details to request logs when provided.
    • Improved API-key authentication logging with key identification details.
    • Expanded “safe” logging fields that bypass sensitive-field redaction while preserving existing redaction behavior.
    • Enhanced logging classification for authentication type, including cookie-based access tokens.
  • Security
    • Updated CORS preflight allowed headers to include the client identification header.
  • Tests
    • Added unit tests covering sensitive-field redaction (including safe-field passthrough) and request logging parsing/classification.

Parse the X-Spoo-Client header and bind client/client_version to the
request log context, bind key_id/key_prefix on API key auth, exempt
derived analytics fields like has_password and key_id from log
redaction, classify access_token cookie auth as jwt_cookie, and allow
X-Spoo-Client in CORS preflight.
Copilot AI review requested due to automatic review settings July 22, 2026 13:11

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5d937c18-9136-45d9-aa03-aa07e3afabc2

📥 Commits

Reviewing files that changed from the base of the PR and between 09235c7 and eb1f53f.

📒 Files selected for processing (2)
  • middleware/logging.py
  • tests/unit/middleware/test_logging.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • middleware/logging.py
  • tests/unit/middleware/test_logging.py

📝 Walkthrough

Walkthrough

The changes extend structured logging with API-key identifiers and request client metadata, preserve approved fields during redaction, classify cookie-based JWT authentication, allow the client header through CORS, and add unit coverage.

Changes

Structured logging

Layer / File(s) Summary
Safe structured log fields
infrastructure/logging.py, dependencies/auth.py, tests/unit/infrastructure/test_logging_redaction.py
API-key logs include key_id and key_prefix; approved fields bypass sensitive-name redaction, with coverage for secret, safe, structural, and mixed fields.
Request metadata propagation
middleware/logging.py, middleware/security.py, tests/unit/middleware/test_logging.py
Request logging parses X-Spoo-Client, binds client metadata, classifies access_token cookies as jwt_cookie, and allows the header through CORS.

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

Sequence Diagram(s)

sequenceDiagram
  participant Request
  participant LoggingMiddleware
  participant Structlog
  Request->>LoggingMiddleware: Send X-Spoo-Client and authentication headers/cookies
  LoggingMiddleware->>LoggingMiddleware: Parse client tag and classify authentication
  LoggingMiddleware->>Structlog: Bind client metadata and auth context
  Structlog-->>Request: Continue request with inherited logging context
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: attributing requests to clients and API keys in logs.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 feat/client-source-telemetry

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.

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@middleware/logging.py`:
- Around line 51-52: Update the bearer detection helper in middleware/logging.py
to match dependencies/auth.py: recognize the Authorization scheme
case-insensitively and normalize surrounding whitespace before selecting the
API-key/bearer classification. Ensure a valid bearer header takes precedence
over the access_token cookie so such requests are not labeled jwt_cookie.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d4926a79-eb6e-4b34-a9b4-8cfa00aad5e6

📥 Commits

Reviewing files that changed from the base of the PR and between 138bbb0 and 09235c7.

📒 Files selected for processing (6)
  • dependencies/auth.py
  • infrastructure/logging.py
  • middleware/logging.py
  • middleware/security.py
  • tests/unit/infrastructure/test_logging_redaction.py
  • tests/unit/middleware/test_logging.py

Comment thread middleware/logging.py

@Zingzy Zingzy left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Mergeable, nothing blocking. Traced the redaction and CORS paths rather than trusting the description.

  • Redaction: the safe list is exact-match and checked before the substring heuristic, so every secret-bearing name stays covered (jwt_secret, client_secret, device_token still redact; the new tests pin this). The unredacted fields are derived booleans, display prefixes, and identifiers only.
  • key_id/key_prefix bound in the auth dependency ride service-layer events but not request_completed (the BaseHTTPMiddleware task boundary). #267 in this stack closes that; landing this first is still worth it for the service events alone.
  • Keeping auth_kind (header shape, middleware) and auth_method (resolved identity, auth dep) as separate fields is right: a mismatch between the two is itself a useful signal.
  • Hot path cost is one regex match on a usually empty header. Negligible, including on redirects.
  • CORS: _ALLOWED_HEADERS is shared by the public and private classes, so the header is preflight-allowed everywhere. Harmless header, fine.

CI matrix is green here. I also ran the full unit suite at the stack tip locally, 2001 pass.

Aligns the log tag with dependencies/auth.py so a lowercase bearer
API key with a cookie present is not misclassified as jwt_cookie.
@Zingzy

Zingzy commented Jul 22, 2026

Copy link
Copy Markdown
Member Author

Re-verified at eb1f53f. The _auth_kind fix now matches dependencies/auth.py:82-83 exactly (lowercased scheme, split and stripped token), and the regression test pins the api_key-over-cookie precedence for the lowercase scheme.

For the record: my first pass cleared this file without catching the case-sensitive scheme check, and the mismatch was real. A bearer spoo_... request with an access_token cookie present would have been tagged jwt_cookie while actually authenticating as an API key, which is exactly the kind of auth_kind/auth_method disagreement the field pair exists to surface. Good catch by the rabbit.

Full test matrix is green on the new head; the unit suite passes at the chain tip locally (2010).

@Zingzy
Zingzy merged commit 18f4711 into main Jul 22, 2026
12 checks passed
@Zingzy
Zingzy deleted the feat/client-source-telemetry branch July 22, 2026 15:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✔️ Done

Development

Successfully merging this pull request may close these issues.

2 participants