Commit Graph
7 Commits
Author SHA1 Message Date
binwiederhier fefe5e0c19 Do not read the shared visitor's user in the auth and account limiters
The callers only use these for anonymous requests, and the user on an IP-keyed visitor can be
set by another request from the same IP (#1051).

Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
2026-10-29 01:29:22 +00:00
binwiederhier 1ba8fa2863 Keep spent tokens on a burst change; account shows the emails sendable now
A tier upgrade used to keep the old bucket's tokens, capped at the new\nburst, so a bigger tier did not add its difference. The bucket now shifts\nby the burst difference both ways, down to a debt of one burst.\n\nemails_remaining is now capped by what the email bucket holds, so it no\nlonger shows emails the visitor cannot send (e.g. after the daily reset).\n\nClaude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
2026-10-28 22:13:53 +00:00
binwiederhier 6463981f8f One field per line in multi-field struct literals
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
2026-10-28 21:35:06 +00:00
binwiederhier f16baadec4 Let a zero message, call or bandwidth limit admit nothing again
The limit service reads a zero cap as no cap, so a tier with no calls
(or no messages, or no attachment bandwidth on upload) became unlimited
where main's FixedLimiter(0) and bytes limiter admitted nothing. The
visitor now answers zero limits itself before asking the service, like
it already did for requests, emails and bandwidth downloads.
2026-10-25 21:11:36 +00:00
binwiederhier 4bbb4418a5 One limiter model in package limit, replacing the three limiter types in the visitor
Allow(key, rule, n), Refund, Tokens and Usage on a service that keeps a
token bucket and a daily count per key and kind, sharded by key, with the
rule passed in on every call. The server's visitor keeps its identity and
the resolved limits and asks the service; rate.Limiter, util.RateLimiter
and util.FixedLimiter are gone from it (subscriptions stay a per-node
gauge). util.RateLimiter, NewBytesLimiter and BurnTokens had no users
left and are removed.

Behavior changes: refunds work for every kind (a revert on the bandwidth
limiter hands the bytes back), a tier change applies on the next request
without handing back full buckets, a changed tier limit applies without
a tier ID change, and the daily email limit is a real daily cap on top
of the bucket. Step 2 of plans/261025-shared-rate-limits.md.
2026-10-25 15:50:03 +00:00
binwiederhier 72b7cbce39 Retry failed user stats writes, and keep anonymous usage off a new account
Since #1989 the stats queue holds increments, so a batch whose write
failed was lost for good: nothing writes those increments again. It is
now put back and retried with the next flush, unless the daily reset ran
in between.

When a user with a tier took over an IP-keyed visitor, the visitor
handed over its counts after switching to the new user, so anonymous
usage on that IP was added to the account. Counts are now handed over
under the old account, and an IP-keyed visitor's counts are not.
2026-10-12 18:33:06 +00:00
binwiederhier ba2b0a62ae Flush user stats as additive deltas instead of absolute values
UPDATE ... SET stats_messages = $1 lets two ntfy processes that share one
database overwrite each other's counts for the same user: whoever flushed
last won, and the other's publishes were never counted. The queue now
accumulates deltas and flushes SQL increments, and visitors track what they
already handed to the queue and enqueue only the difference. Nothing changes
on a single node; this is groundwork for running more than one.
2026-10-03 19:16:56 +00:00