Relationships
↑ child of #2008#2022 feat(api): a cheap unread-notification count for API-key clients
Opened by keeb · 9/4/2026
Parent: the product-nurture epic. Prerequisite for the CLI hint.
Problem
The CLI needs to answer one question cheaply, on every invocation, without adding latency: do I have anything waiting? The endpoint that exists cannot serve that.
GET /api/v1/notifications builds the full inbox view: notifications, lab
issues, broadcasts, and tier colouring — which is a score read. That is the
right shape for a page and completely wrong for something a CLI polls.
What ships
GET /api/v1/notifications/count returning a small, stable body:
{ "unread": 3, "asOf": "2026-09-04T…Z" }API-key capable. resolveRequestAuth already prefers an explicit API key
over a session cookie on /api/* and mints a synthetic session for a personal
key, so this works from the CLI with no new auth machinery. A collective
token sets no session by design and must get 401 here — an inbox belongs to a
person, not a credential.
The count must match what the inbox shows. notificationRepo.countUnread
alone is not the answer: buildInboxView merges unread broadcasts into its
badge. A count that omits them says "3 waiting", and the operative opens the
inbox and finds 5. Derive both numbers from one shared function so they cannot
drift, and pin it with a test that builds a fixture containing both kinds and
asserts the two agree.
Cheap and cacheable-per-viewer. Two keyed Mongo counts, no lab reads, no
score reads, no tier colouring. applyResponsePolicy will stamp
private, no-cache + Vary: Cookie because the response is credential-derived
— that is correct and needs nothing added.
Rate limited. A new rule matching this sub-path must be ordered before
the /api/ rule in lib/app/repos.ts, since RateLimiter.findRule returns
the first match. The CLI caches locally (E2), so the limit can be tight.
Verification
tests/routes/notifications_count_test.ts— 401 anonymous, 401 for a collective token, 200 with a personal key; the count agrees withbuildInboxView'sunreadCountover a fixture containing notifications and an unread broadcast.- A call-counting assertion that the handler issues no lab and no ClickHouse reads.
- Measured against prod once deployed: quote the latency next to
/api/health, per the standing rule that latency is round trips.
Open
No activity in this phase yet.
Sign in to post a ripple.