0005. Sentry for errors, pino for logs
- Status: accepted
- Date: 2026-10-03
- Issue: #1
Context
Operators need to see unexpected failures and which command ran, without putting bot tokens, message bodies or command arguments in a third-party service. Sentry is optional locally. The original decision-log line said "no PII": no IPs, no locals, no secrets, no Discord message content. The stable platform ID is attached on purpose so an issue can be traced and a person banned.
Decision
- Sentry (
@sentry/node, loaded fromsrc/instrument.tsbefore the app) for unexpected errors.UserFacingErroris shown to the user and not reported. No DSN means Sentry does nothing. - The Sentry user is the stable platform ID (
discord:<id>), with display name and handle so people can recognise them. AlldataCollectioncategories are off (including stack-frame locals and IP inference).beforeSendscrubs anything that looks like a token. - pino is the only logger. No
console.log. Each line goes to stdout, a rotating file inPIXEL_LOG_DIR(about two weeks; empty turns the file off), and Sentry Logs (infoand above). Tokens and authorization headers are redacted. - Action logs use
actorLogFields():user(discord:<id>),userNameanduserHandle. Act on the ID, never the name. Do not log message content or command arguments unless an ADR says so. - Outgoing HTTP is not collected wholesale (
httpBodiesandurlQueryParamsstay off), because the space state API takes its credentials in the query string. InsteadHttpSpaceStateApihands a sanitised exchange (redacted address, state, status, capped body) to the reporter'sattach, which sets it as a Sentry context and span attributes. - A small HTTP server exposes
/healthz(process alive) and/readyz(Discord gateway connected). Incoming probes are not Sentry request sessions.
Consequences
- Local runs without a DSN are fine. CD /
just sentry-releasefails closed ifSENTRY_ORG,SENTRY_PROJECTorSENTRY_AUTH_TOKENare missing. - Moderators can find and ban someone from the logs. Names are for humans only.
- A rotating file on disk is operator-only; it is not a public audit log (#31).