Skip to main content
← Back to list
01Issue
FeatureOpenSwamp ClubPublic
AssigneesNone

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 with buildInboxView's unreadCount over 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.
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED

Open

9/4/2026, 7:40:54 PM

No activity in this phase yet.

03Sludge Pulse
keeb linked parent of #20089/4/2026, 7:41:18 PM

Sign in to post a ripple.