Move the base-url check out of the else-if chain instead of into a
helper, and test both cases with an invalid auth-default-access, which
fails right after the checks, instead of starting a server.
A message the poll stored before its live delivery has no unread marker
yet, so the WebSocket delivery that follows marks it unread and notifies
again, while a replayed message that came in live keeps its state.
The e2e tests publish within one second and reload, so the reconnect
and the startup poll replay the earlier messages: deleted ones stay
gone, and read ones stay read without a new browser notification.
Like the Android app, a deleted notification stays in the notifications
table with a deleted flag, so the existing duplicate checks skip it when
the server replays it, and a deleted newer version keeps an older one
out. The lists leave deleted notifications out, and they are pruned with
the others. The service worker does not store a deleted message again.
The WebSocket handler deleted every version of a message's sequence
before adding it, so a replayed message that was already stored came
back as unread and notified again, and a replayed older version
replaced a newer one. addNotification now replaces older versions of
the sequence itself, and skips a message if a newer version is stored.
Fixes#2090
The server builds the usage store on the user database (the shared pool on
Postgres, the auth-file on SQLite) and starts the limiter before serving. A
visitor without a user writes its message count from a tenth of the cap on,
everyone else from the first message; anonymous emails get the daily cap the
replenish interval implies. The account API reports reset_at. The user stats
columns are no longer read or written.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
Every 5 s a node writes the unflushed counts of its promoted keys and reads back
the rows changed since the last round; at startup it loads the current rows
(waiting up to 30 s, then serving from memory while it keeps loading). A key is
promoted once it reaches its rule's threshold, or when another node's row for it
shows up. Rows not written for 25 h are pruned hourly.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
One row per key: same period adds, a newer period replaces, and a leftover of an
older period is added to the stored one rather than taking the row back. Writes
are sorted by key and chunked; reads stream rows changed since a database time.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
Daily counts (messages, emails, calls) now live in per-key counters that start
over lazily when the key's period ends; buckets keep their tokens. ResetAt
reports the end of the key's period. Seed, ResetUsage and Config.ResetTime are
gone, and an idle key is forgotten only once its buckets are full again.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
Liveness no longer comes from keepalives: a topic keeps its rate visitor
while it has an open stream, and for a day after the last one closed or
the last subscribe. This removes the keepalive matching, taking a stale
topic back and its write re-check.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
A stream's keepalives refreshed only its own visitor, so a topic that held
the visitor of a newer stream or a poll from the same key lost it after a
day although a stream was still open. The topic now keeps when a
subscriber with its rate visitor's key was last seen, and a stream takes a
stale topic back.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
Tier to tier changes keep spend (no tier to a tier never did), only a tier's daily email limit is a cap, only the read over the size limit is refunded, and the #2119 bullet describes the fix.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
Without a tier, the daily email limit is only derived from the refill, for
display. Enforcing it as a cap cut what the email bucket allowed: with a burst
of 5 and a refill every 288 minutes, 5 emails a day instead of about 10. Only
a tier's daily email limit is a cap now, as before.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe