Stop logging /healthz + internal SSR hop, truncate logged IPs #35
No reviewers
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: Jinemi/thermograph#35
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/healthz-ip-privacy-event-cume"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Logging waste + IP privacy (fixed)
/healthzwas ~46.7% of prod's daily access log and the internalfrontend_ssr -> backend content-API hop (
backend/api/content_routes.py,called only by
frontend/api_client.py, never by a browser) another~32.2% -- both cost a threadpool dispatch + lock + file write per request
for traffic that isn't meaningful to retain.
core/metrics.py'sclassify_inboundgains"health"(for/healthz)and
"internal"(for/api/v2/content/*) categories.web/app.py's request-logging middleware excludes both, alongside theexisting
static/metrics/eventexclusions, fromaudit.log_access--so they're never dispatched to the threadpool at all, not just cheaply
logged once there.
(IPv6) before being persisted (
_loggable_ip) -- coarse geo/abuse signalwithout a full, joinable address sitting in Loki for 30 days, matching
the privacy page's claim that IPs aren't logged beyond normal request
handling.
_client_ipitself is untouched and still full precision --the event-beacon rate limiter (
metrics._rate_ok) keys off that sameuntruncated value, so a NAT'd office still gets one shared quota, not one
per truncated /24.
deploy/entrypoint.shadds--no-access-logto the uvicorn invocation:it was duplicating every line
audit.log_accessalready writes.Tests:
tests/core/test_metrics.pycovers the two new categories;tests/web/test_request_logging.pyis new and covers the ASGI-levelbehavior --
/healthzand an"internal"-classified route never reachaudit.log_access, the logged IP is the truncated one, the rate limiterstill receives the untruncated one, and a full HTTP round trip through
/api/v2/eventis asserted to land inmetrics.snapshot()(nothingpreviously exercised that route through the real ASGI stack).
event_cume investigation
Went in assuming the stated hypothesis (
sendBeacondying silently to across-origin
frontend_ssrvs backend split) and could not confirm itagainst how this is actually wired today:
content.py'sASSET_BASE(what{{ asset_base }}/digest.jsetc.resolve against, and therefore what
account.js'simport.meta.url-derivedAPP_BASE/uv()end up targeting) only goescross-origin when
THERMOGRAPH_API_BASE_PUBLICis set. It is unset inevery compose/stack file in this repo (
infra/docker-compose.yml,infra/docker-compose.dev.yml,infra/deploy/stack/thermograph-stack.yml) --same for
THERMOGRAPH_CORS_ORIGINS(noCORSMiddlewareis even added).infra/deploy/Caddyfile, and the Terraform templateboth are rendered from) path-splits
/api/*,/digest,/discord/interactionsto backend and everything else to frontend underone apparent origin -- a browser never sees two origins.
all and is reached solely through backend's own
_proxy_to_frontendfallback (web/app.py), which is single-origin byconstruction -- its own docstring already says as much ("the primary
same-origin mechanism is host Caddy in prod/beta ... this proxy is never
actually exercised there").
So in every environment this repo actually deploys, the event beacon's
target is same-origin, and no CORS preflight is even in play. I also
walked the rest of the client chain looking for anything that would drop
the beacon regardless of origin: the
EVENTS/NAV_SURFACESallowlistsmatch every
data-event/track()call site exactly,digest.js's globalclick listener is registered as a side effect of
app.js'simport { track }even on the homepage (which overridesbase_scriptswithout{{ super() }}, so it never gets its own literal<script>tag fordigest.js/account.js-- but the transitive import still runs theirtop-level code), and I found no top-level throw or
stopPropagationanywhere in the click path. None of that turned up a defect either.
Verdict: inconclusive. I could not reproduce or find the actual break
in this pass -- ruled out cross-origin/CORS with reasonably strong
evidence, and reviewed (without finding an issue) the allowlists, the
click-listener wiring, and the URL-building helpers shared by
account.js/digest.js. What I could not do from here: capture a livebrowser network trace on an actual LAN dev session (no headless browser
tooling available in this environment) to see whether the beacon request
is even attempted client-side, which would be the fastest way to
narrow this further.
Separately, while reading
core/metrics.py's Postgres store I noticed_boot_token()derives its "reset exactly once per deployment" guard fromthe container's own PID 1 start time -- correct for multiple uvicorn
workers sharing one container, but prod's Swarm
webservice can runmultiple replica containers per deployment (
WEB_MIN_REPLICAS/WEB_MAX_REPLICAS, autoscaled), each with its own PID 1 and therefore itsown token. A second replica of the same deploy would see the stored
token mismatch its own and re-
TRUNCATEevent_cume(and the othercumulative metrics tables) again. That's a real latent gap, but I'm not
shipping a fix for it here: it wouldn't explain dev/beta (single replica,
so unaffected), I have no live confirmation it's actually firing on prod,
and fixing it needs a stable per-deployment identifier plumbed through the
stack file (this repo's Postgres-backed metrics path is deliberately
excluded from the unit suite -- see
tests/accounts/test_db_engines.py'sown docstring -- so it's also not unit-testable as-is). Flagging it here
for whoever picks this up next rather than guessing at a fix blind.