Bell: lock-expiry notification + notification retention setting (lean at scale) #402

Closed
opened 2026-09-02 23:53:24 +00:00 by Cordy · 0 comments
Owner

Ratified design (follows the #394 "no notification in v1" deferral and the bell architecture review).

Principles

The bell stays a derived view (no event store, no sweeper) — that is what keeps it lean where Nextcloud fights multi-GB activity tables and Novu TTLs its feed at 1 month. New event kinds must be derivable from live state.

Lock-expiry notification

  • #394 keeps cap-lapsed manual locks in locks.json (policy lens, revivable). The feed derives from them: for the requesting user, every manual lock that is live by its own record but lapsed under the cap becomes an item — kind lock-expired, file = basename, At = lapse moment (rec.At + cap).
  • UI: bell row with an open-shackle padlock in the lock (amber) tint; bold line = file name; meta = "Your lock expired (enforced by admin setting)" + relative age. i18n ×4.
  • View semantics: cap off → lock revives → item disappears; lock removed → item disappears; past retention → gone for good.

Retention setting

notifyRetentionDays (int; 0/absent = default 30; max 365; deliberately no "forever") in Basic settings, whose first card is retitled "Name & Notifications" (→ "General" if a third loose setting ever lands there). It governs:

  1. Feed horizon — nothing older than H appears (all kinds), bounding derivation output.
  2. Lapsed-lock prune — manual locks lapsed for longer than H are removed from locks.json on save (lazy, like dav reaping).
  3. Watermark prunenotify-seen.json entries older than H are dropped on save; lossless (a watermark older than the horizon is behaviourally identical to none), bounding the file by users active within H instead of users-ever.

Scale guard

Per-user in-memory feed cache (TTL ~45s, invalidated by read-all/clear): backend cost ≤1 derivation per user per TTL regardless of tabs/devices — today every tab polls every 60s and each derivation is 1+P backend LISTs. Nothing persisted; memory bounded by active users.

Out of scope, flagged separately: peer-transfers.json grows per send forever and also feeds the Transfers view — retention for it is its own decision.

Refs: #394 (lapse-lazily decision), #147 (bell), mockup lock-notification-mockup.html (approved).

Ratified design (follows the #394 "no notification in v1" deferral and the bell architecture review). ## Principles The bell stays a **derived view** (no event store, no sweeper) — that is what keeps it lean where Nextcloud fights multi-GB activity tables and Novu TTLs its feed at 1 month. New event kinds must be derivable from live state. ## Lock-expiry notification - #394 keeps cap-lapsed manual locks in `locks.json` (policy lens, revivable). The feed derives from them: for the requesting user, every manual lock that is live by its own record but lapsed under the cap becomes an item — kind `lock-expired`, `file` = basename, `At` = lapse moment (`rec.At + cap`). - UI: bell row with an **open-shackle padlock** in the lock (amber) tint; bold line = file name; meta = "Your lock expired (enforced by admin setting)" + relative age. i18n ×4. - View semantics: cap off → lock revives → item disappears; lock removed → item disappears; past retention → gone for good. ## Retention setting `notifyRetentionDays` (int; 0/absent = default 30; max 365; deliberately no "forever") in Basic settings, whose first card is retitled **"Name & Notifications"** (→ "General" if a third loose setting ever lands there). It governs: 1. **Feed horizon** — nothing older than H appears (all kinds), bounding derivation output. 2. **Lapsed-lock prune** — manual locks lapsed for longer than H are removed from `locks.json` on save (lazy, like dav reaping). 3. **Watermark prune** — `notify-seen.json` entries older than H are dropped on save; lossless (a watermark older than the horizon is behaviourally identical to none), bounding the file by users active within H instead of users-ever. ## Scale guard Per-user **in-memory feed cache** (TTL ~45s, invalidated by read-all/clear): backend cost ≤1 derivation per user per TTL regardless of tabs/devices — today every tab polls every 60s and each derivation is 1+P backend LISTs. Nothing persisted; memory bounded by active users. Out of scope, flagged separately: `peer-transfers.json` grows per send forever and also feeds the Transfers view — retention for it is its own decision. Refs: #394 (lapse-lazily decision), #147 (bell), mockup `lock-notification-mockup.html` (approved).
Cordy closed this issue 2026-09-03 00:00:09 +00:00
Sign in to join this conversation.
No labels
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/Cairn#402
No description provided.