// ── Core's own push-notification streams ─────────────────────────────────── // // What is left of config/notificationStreams.js once the shard-derived catalog // moved to config/shardStreams.js (MODULE_SYSTEM.md §1.8: push INFRASTRUCTURE is // core, the CATALOG is content). `news.post` is produced by the website's own // posts path, not by any game feed, and the four `team.*` streams by core's own // Team sync and forum. // // Registered through modules/registries.js like any module's, and read back // through it — nothing imports this file to get "the catalog", because the // catalog is core's plus every module's. // // Phase 6 added the four Team streams below. They are core's for the same reason // the Team tables are: a module supplies who is in a Team, but who may be told // about it is the access resolver's answer, and that is core's (TEAMS.md Part 6). // // The payload that ever leaves the server is a CONTENT-FREE tickle // ({ stream, ref }); the app wakes and PULLS the real, ownership-checked content // over the authenticated API (docs/android/PLAN.md §11). const STREAMS = [ { id: 'news.post', label: 'News posts', description: 'New news / Five-on-Friday / newsletter posts.', personal: false, requiresLinkedAccount: false, }, // ── Teams (TEAMS.md §6.2, phase 6) ─────────────────────────────────────── // // FOUR streams, and not one per Team. The catalog is a static registration // validated at boot; it has no way to express an unbounded runtime-created set, // and a stream id per Team would leave rows in notification_subscriptions to // collect every time a Team archived. Which Team an event came from lives in // the RECIPIENT SET (utils/teamNotify.js) and in the `ref`, never in the id. // // `requiresLinkedAccount: false` on all four is deliberate and reads oddly. // These are game-sourced events, so the instinct is to demand a linked game // account — but a forum-granted user with no game identity at all is exactly // the population §2.5 path 3 exists for, and they are a legitimate recipient of // `team.forum.post`. The flag would refuse them a toggle they have every right // to. What enforces who gets what is the recipient computation, which asks the // access resolver; the stream flag is not a second, weaker copy of that rule. // // `personal: false` for the same reason it is false on news.post: these are not // owner-keyed events about one account's own property. `publishToUsers` is a // third fan-out shape alongside "everyone subscribed" and "this one owner", and // the catalog has no flag for it because the flag would say nothing a caller // does not already know by choosing the function. { id: 'team.member.joined', label: 'Team — new member', description: 'Someone joined a Team you belong to.', personal: false, requiresLinkedAccount: false, }, { id: 'team.leadership.changed', label: 'Team — leadership change', description: 'Leadership changed in a Team you belong to.', personal: false, requiresLinkedAccount: false, }, { id: 'team.forum.post', label: 'Team — new forum post', description: 'A new thread or reply in a Team forum you can read.', personal: false, requiresLinkedAccount: false, }, { id: 'team.announcement', label: 'Team — announcements', description: 'A leader posted an announcement in a Team you can read.', personal: false, requiresLinkedAccount: false, }, ] module.exports = { STREAMS }