Skip to content

feat(api): give every request an id and log under a written privacy policy - #597

Merged
salazarsebas merged 5 commits into
mainfrom
feat/api-request-id-and-log-privacy
Oct 10, 2026
Merged

salazarsebas merged 5 commits into
mainfrom
feat/api-request-id-and-log-privacy

Conversation

@salazarsebas

Copy link
Copy Markdown
Member

What

  • Every API response carries x-request-id; every error body repeats it as error.requestId. The id is generated server-side per request (a caller-supplied value is ignored) and is set by the first middleware, ahead of the rate limiter, body parsing and auth, so a 429, a 401, a 404 and body errors carry it, together with Cache-Control: no-store and the rate-limit headers.
  • The Nest text logger is replaced with JSON lines on stdout (severity mapped to Cloud Logging levels, requestId, time, context). One access line per request: requestId, route (pattern, never the path), status, latencyMs, keyLabel, network. No IP, headers or bodies.
  • The log privacy policy is enforced where lines are written, not at 23 call sites: secret-shaped strings, bearer tokens and long encoded blobs are redacted; addresses become first4...last4 plus an HMAC keyed hash (LOG_HASH_KEY, random per process when unset); any address in the exchange registry becomes the fixed token [exchange]; the memo a request carried (body field or inside a signed envelope) is removed from every line that request writes.
  • Policy page docs/reference/logging-and-privacy.mdx (in the docs navigation), plus request-id notes in the API intro and SDK errors page.
  • StructuredApiError.error.requestId and the OpenAPI error schema gain the optional field. SDK bumped to 0.4.3 with a changelog entry (it already read X-Request-Id; no runtime change).
  • Web: the proxy relays the upstream id as a response header, ApiRequestError carries it, and the failed-close alert shows "Reference: " beside the unchanged message (toUserMessage output is identical; test asserts it).

Closes #488

Tests

  • e2e: 401, 429 (limiter), 404, 400, 413 and a controller error each carry the same id in header and envelope plus no-store and rate-limit headers; ids are unique and client-supplied ones are ignored.
  • e2e: an exchange close failing at plan, transactions and submit with a capturing logger asserts every line is valid JSON and contains no full address, no exchange address prefix/suffix/hash, no memo, no secret-shaped string, no XDR, and that access lines hold the route pattern only.
  • unit: logger severity mapping and JSON validity, scrubbing rules; web unit tests for the id helpers, the close client, the hook and the unchanged user message. Existing e2e assertions on exact error bodies now tolerate the added requestId.
  • Failing on main: with the app wiring and web libs reverted, the 5 request-id e2e tests and 2 log-privacy e2e tests failed (7 fail, 0 pass) and the web request-id file failed to load; they pass with the change.
  • Local: type-check, lint (only a pre-existing web warning), test across all packages, format:check, sdk check-api and the docs nav check pass.

Risks

  • Scrubbing is regex based and cannot prove absence of every sensitive string: memos shorter than 3 characters are not redacted, and non-exchange addresses keep 4+4 characters plus the keyed hash as the issue specifies. The policy relies on layers: no bodies, route patterns, central scrubbing, tests.
  • Error bodies are wrapped at res.json to inject the id; chosen over editing each of the ~10 emit sites so none can be missed.
  • Test runs now print JSON log lines to stdout.

Security review

Security-sensitive (what reaches logs). security-review was run from the worktree before the commits; it received an empty diff because the skill diffs the main checkout, so the change was hand-reviewed instead: id is server-generated and header-safe (UUID), the web relays an id only when it matches ^[A-Za-z0-9-]{8,64}$, rendered through React text, no new endpoint or auth change, no secret reaches logs, scrub regexes are linear. No findings.

@vercel

vercel Bot commented Oct 10, 2026

Copy link
Copy Markdown

Deployment failed for project playground with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/salazarsebas?upgradeToPro=build-rate-limit

@vercel

vercel Bot commented Oct 10, 2026

Copy link
Copy Markdown

Deployment failed for project lumenwipe with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/salazarsebas?upgradeToPro=build-rate-limit

@salazarsebas
salazarsebas force-pushed the feat/api-request-id-and-log-privacy branch from b011428 to 52caf33 Compare October 10, 2026 18:32
@vercel

vercel Bot commented Oct 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
playground Skipped Skipped Oct 10, 2026 6:41pm UTC

@vercel
vercel Bot temporarily deployed to Preview – playground October 10, 2026 18:41 Inactive
@salazarsebas salazarsebas self-assigned this Oct 10, 2026
@salazarsebas
salazarsebas merged commit de7f514 into main Oct 10, 2026
22 of 23 checks passed
@salazarsebas
salazarsebas deleted the feat/api-request-id-and-log-privacy branch October 10, 2026 18:44

This branch was previously deployed

1 inactive deployment
Preview – playground — 4241f670 Deployed Oct 10, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[api] Give every request an id and log under a written privacy policy

1 participant