B2: scrub Sentry events and add per-request scope tags#403
Open
MrTravisB wants to merge 1 commit into
Open
Conversation
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Second PR in Stack B (server observability). Stacked on #402 (B1).
Configures Sentry's
beforeSendandbeforeBreadcrumbhooks to scrubpotentially sensitive content out of every event/breadcrumb that reaches
the transport, and adds a per-request middleware that tags the active
scope with bounded metadata so events are filterable by
taskId, route,method, and status.
What gets scrubbed (
beforeSend)request.data(request body)request.query_string(URL query - leaks tokens / search terms)request.cookiesAuthorization,Cookie,Set-Cookieheaders (case-insensitive)exception.valuereduced toexception.type(dropserror.message,which can quote task text or page content)
exception.stacktrace(frames can include selectors derived from pagecontent via Playwright)
contexts.response.bodyevent.extra(arbitrary attached data)What gets scrubbed (
beforeBreadcrumb)consolebreadcrumbs are dropped (verbose, often contain user content)breadcrumb.datais filtered to an allowlist of bounded keys:taskId,method,route,status,phase,reason,result,provider,model,iteration,tool,error_class,duration_ms. Anythingelse is dropped.
What gets tagged (
sentryContextmiddleware)After every request: sets
method,route(matched pattern),status,and
taskId(when the response carriesx-pilo-task-id) on therequest-scoped Toucan instance. No request-derived strings.
Defense in depth
Stack A's helpers (
createErrorResponse,errorResponseFromError,recordSanitizedException) prevent leaks at the Pilo code layer. Thesescrubbers are the boundary layer for the Sentry transport, catching
anything that leaks through future regressions, third-party middleware,
or Sentry's own auto-instrumentation (which captures
request.dataandunhandled exceptions by default).
Changes
packages/server/src/middleware/sentryScrubber.ts-scrubBeforeSend,scrubBeforeBreadcrumbpure functionspackages/server/src/middleware/sentryContext.ts- per-request scopetags via
getSentry(c)packages/server/src/index.ts- wire scrubbers into thesentry()init,register
sentryContext()middleware afterrequestLog()packages/server/package.json- add@sentry/typesas devDependency(types-only; the runtime types come from toucan-js transitively)
sentryScrubber.test.tsandsentryContext.test.tsincluding sentinel canaries on every scrub path
Deferred
Task lifecycle breadcrumbs (
task.start,task.iteration_start,tool.call,error.recoverable_caught) are deferred to a follow-up PR.The scrubber + tags are valuable on their own (they sanitize what Sentry
already auto-captures), and breadcrumbs require touching routes and
the agent event flow - better as a focused follow-up than bundled here.
Test plan
pnpm --filter pilo-server run test(114 passing: 94 prior + 20 new)pnpm --filter pilo-core run test(still green)pnpm run typecheckgreenpnpm run format:checkgreennode scripts/check-dep-versions.mjsconfirms shared dep alignmentNotes
travis/pilo-request-logging(stacks on B1: structured request access log middleware #402)SENTRY_DSNbeing set in deployed environments -if not set today, this PR is preparation work, silent until DSN is wired