[{"data":1,"prerenderedAt":1289},["ShallowReactive",2],{"navigation_docs_en":3,"-en-notify-broadcast":441,"-en-notify-broadcast-surround":1284},[4,45,60,81,112,129,146,173,197,213,242,279,356,372,384,403,429],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":44},"Getting Started","i-lucide-rocket","\u002Fen\u002Fgetting-started","en\u002F1.getting-started",[10,15,20,24,29,34,39],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fen\u002Fgetting-started\u002Fintroduction","en\u002F1.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Coming from Laravel","\u002Fen\u002Fgetting-started\u002Fcoming-from-laravel","en\u002F1.getting-started\u002F2.coming-from-laravel","i-lucide-arrow-right-left",{"title":21,"path":22,"stem":23,"icon":19},"Coming from Symfony","\u002Fen\u002Fgetting-started\u002Fcoming-from-symfony","en\u002F1.getting-started\u002F3.coming-from-symfony",{"title":25,"path":26,"stem":27,"icon":28},"Installation","\u002Fen\u002Fgetting-started\u002Finstallation","en\u002F1.getting-started\u002F4.installation","i-lucide-download",{"title":30,"path":31,"stem":32,"icon":33},"Your first module","\u002Fen\u002Fgetting-started\u002Ffirst-module","en\u002F1.getting-started\u002F5.first-module","i-lucide-package-plus",{"title":35,"path":36,"stem":37,"icon":38},"Project structure","\u002Fen\u002Fgetting-started\u002Fproject-structure","en\u002F1.getting-started\u002F6.project-structure","i-lucide-folder-tree",{"title":40,"path":41,"stem":42,"icon":43},"The shop sample application","\u002Fen\u002Fgetting-started\u002Fsample-app","en\u002F1.getting-started\u002F7.sample-app","i-lucide-shopping-cart",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"queue","i-lucide-layers","\u002Fen\u002Fqueue","en\u002F10.queue",[51,56],{"title":52,"path":53,"stem":54,"icon":55},"Overview","\u002Fen\u002Fqueue\u002Foverview","en\u002F10.queue\u002F1.overview","i-lucide-list-ordered",{"title":57,"path":58,"stem":59,"icon":47},"Tasks","\u002Fen\u002Fqueue\u002Ftasks","en\u002F10.queue\u002F2.tasks",{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":44},"events","i-lucide-workflow","\u002Fen\u002Fevents","en\u002F11.events",[66,71,76],{"title":67,"path":68,"stem":69,"icon":70},"Events","\u002Fen\u002Fevents\u002Foverview","en\u002F11.events\u002F1.overview","i-lucide-radio",{"title":72,"path":73,"stem":74,"icon":75},"Outbox","\u002Fen\u002Fevents\u002Foutbox","en\u002F11.events\u002F2.outbox","i-lucide-inbox",{"title":77,"path":78,"stem":79,"icon":80},"Scheduling","\u002Fen\u002Fevents\u002Fscheduler","en\u002F11.events\u002F3.scheduler","i-lucide-calendar-clock",{"title":82,"icon":83,"path":84,"stem":85,"children":86,"page":44},"auth","i-lucide-shield-check","\u002Fen\u002Fauth","en\u002F12.auth",[87,92,97,102,107],{"title":88,"path":89,"stem":90,"icon":91},"Authentication","\u002Fen\u002Fauth\u002Foverview","en\u002F12.auth\u002F1.overview","i-lucide-key-round",{"title":93,"path":94,"stem":95,"icon":96},"RBAC","\u002Fen\u002Fauth\u002Frbac","en\u002F12.auth\u002F2.rbac","i-lucide-users",{"title":98,"path":99,"stem":100,"icon":101},"Policies","\u002Fen\u002Fauth\u002Fpolicy","en\u002F12.auth\u002F3.policy","i-lucide-gavel",{"title":103,"path":104,"stem":105,"icon":106},"Using auth standalone","\u002Fen\u002Fauth\u002Fstandalone","en\u002F12.auth\u002F4.standalone","i-lucide-plug",{"title":108,"path":109,"stem":110,"icon":111},"Social login with goth, standalone","\u002Fen\u002Fauth\u002Fsocial-standalone","en\u002F12.auth\u002F5.social-standalone","i-lucide-log-in",{"title":113,"icon":114,"path":115,"stem":116,"children":117,"page":44},"mail","i-lucide-mail","\u002Fen\u002Fmail","en\u002F13.mail",[118,121,125],{"title":52,"path":119,"stem":120,"icon":114},"\u002Fen\u002Fmail\u002Foverview","en\u002F13.mail\u002F1.overview",{"title":122,"path":123,"stem":124,"icon":114},"SMTP","\u002Fen\u002Fmail\u002Fsmtp","en\u002F13.mail\u002F2.smtp",{"title":126,"path":127,"stem":128,"icon":114},"MJML","\u002Fen\u002Fmail\u002Fmjml","en\u002F13.mail\u002F3.mjml",{"title":130,"icon":131,"path":132,"stem":133,"children":134,"page":44},"notify","i-lucide-bell","\u002Fen\u002Fnotify","en\u002F14.notify",[135,138,142],{"title":52,"path":136,"stem":137,"icon":131},"\u002Fen\u002Fnotify\u002Foverview","en\u002F14.notify\u002F1.overview",{"title":139,"path":140,"stem":141,"icon":131},"Database channel","\u002Fen\u002Fnotify\u002Fdatabase","en\u002F14.notify\u002F2.database",{"title":143,"path":144,"stem":145,"icon":131},"Broadcast channel","\u002Fen\u002Fnotify\u002Fbroadcast","en\u002F14.notify\u002F3.broadcast",{"title":147,"icon":148,"path":149,"stem":150,"children":151,"page":44},"storage","i-lucide-hard-drive","\u002Fen\u002Fstorage","en\u002F15.storage",[152,155,159,163,168],{"title":52,"path":153,"stem":154,"icon":148},"\u002Fen\u002Fstorage\u002Foverview","en\u002F15.storage\u002F1.overview",{"title":156,"path":157,"stem":158},"Disk API","\u002Fen\u002Fstorage\u002Fdisk-api","en\u002F15.storage\u002F2.disk-api",{"title":160,"path":161,"stem":162,"icon":148},"Memory and local disk","\u002Fen\u002Fstorage\u002Flocal","en\u002F15.storage\u002F3.local",{"title":164,"path":165,"stem":166,"icon":167},"S3","\u002Fen\u002Fstorage\u002Fs3","en\u002F15.storage\u002F4.s3","i-lucide-cloud",{"title":169,"path":170,"stem":171,"icon":172},"Testing","\u002Fen\u002Fstorage\u002Ftesting","en\u002F15.storage\u002F5.testing","i-lucide-flask-conical",{"title":174,"icon":175,"path":176,"stem":177,"children":178,"page":44},"telemetry","i-lucide-activity","\u002Fen\u002Ftelemetry","en\u002F16.telemetry",[179,182,187,192],{"title":52,"path":180,"stem":181,"icon":175},"\u002Fen\u002Ftelemetry\u002Foverview","en\u002F16.telemetry\u002F1.overview",{"title":183,"path":184,"stem":185,"icon":186},"Logging","\u002Fen\u002Ftelemetry\u002Flogging","en\u002F16.telemetry\u002F2.logging","i-lucide-scroll-text",{"title":188,"path":189,"stem":190,"icon":191},"OpenTelemetry","\u002Fen\u002Ftelemetry\u002Fopentelemetry","en\u002F16.telemetry\u002F3.opentelemetry","i-lucide-radar",{"title":193,"path":194,"stem":195,"icon":196},"Sentry","\u002Fen\u002Ftelemetry\u002Fsentry","en\u002F16.telemetry\u002F4.sentry","i-lucide-bug",{"title":198,"icon":199,"path":200,"stem":201,"children":202,"page":44},"Reference","i-lucide-book-open","\u002Fen\u002Freference","en\u002F17.reference",[203,208],{"title":204,"path":205,"stem":206,"icon":207},"Configuration","\u002Fen\u002Freference\u002Fconfiguration","en\u002F17.reference\u002F1.configuration","i-lucide-settings",{"title":209,"path":210,"stem":211,"icon":212},"External dependencies","\u002Fen\u002Freference\u002Fdependencies","en\u002F17.reference\u002F2.dependencies","i-lucide-package",{"title":214,"icon":215,"path":216,"stem":217,"children":218,"page":44},"Concepts","i-lucide-lightbulb","\u002Fen\u002Fconcepts","en\u002F2.concepts",[219,223,228,232,237],{"title":220,"path":221,"stem":222,"icon":47},"Architecture","\u002Fen\u002Fconcepts\u002Farchitecture","en\u002F2.concepts\u002F1.architecture",{"title":224,"path":225,"stem":226,"icon":227},"Error model","\u002Fen\u002Fconcepts\u002Ferror-model","en\u002F2.concepts\u002F2.error-model","i-lucide-shield-alert",{"title":204,"path":229,"stem":230,"icon":231},"\u002Fen\u002Fconcepts\u002Fconfiguration","en\u002F2.concepts\u002F3.configuration","i-lucide-settings-2",{"title":233,"path":234,"stem":235,"icon":236},"Codegen pipeline","\u002Fen\u002Fconcepts\u002Fcodegen-pipeline","en\u002F2.concepts\u002F4.codegen-pipeline","i-lucide-file-json",{"title":238,"path":239,"stem":240,"icon":241},"Design patterns","\u002Fen\u002Fconcepts\u002Fdesign-patterns","en\u002F2.concepts\u002F5.design-patterns","i-lucide-puzzle",{"title":243,"icon":244,"path":245,"stem":246,"children":247,"page":44},"Kit","i-lucide-box","\u002Fen\u002Fkit","en\u002F3.kit",[248,251,256,261,266,270,275],{"title":52,"path":249,"stem":250,"icon":244},"\u002Fen\u002Fkit\u002Foverview","en\u002F3.kit\u002F1.overview",{"title":252,"path":253,"stem":254,"icon":255},"Application lifecycle","\u002Fen\u002Fkit\u002Fapp","en\u002F3.kit\u002F2.app","i-lucide-power",{"title":257,"path":258,"stem":259,"icon":260},"Server","\u002Fen\u002Fkit\u002Fserver","en\u002F3.kit\u002F3.server","i-lucide-server",{"title":262,"path":263,"stem":264,"icon":265},"Worker","\u002Fen\u002Fkit\u002Fworker","en\u002F3.kit\u002F4.worker","i-lucide-cog",{"title":267,"path":268,"stem":269,"icon":70},"Realtime","\u002Fen\u002Fkit\u002Frealtime","en\u002F3.kit\u002F5.realtime",{"title":271,"path":272,"stem":273,"icon":274},"Migrations","\u002Fen\u002Fkit\u002Fmigrations","en\u002F3.kit\u002F6.migrations","i-lucide-file-stack",{"title":276,"path":277,"stem":278,"icon":111},"Social login","\u002Fen\u002Fkit\u002Fsocial-login","en\u002F3.kit\u002F7.social-login",{"title":280,"icon":281,"path":282,"stem":283,"children":284,"page":44},"CLI Reference","i-lucide-terminal","\u002Fen\u002Fcli","en\u002F4.cli",[285,288,293,298,303,308,313,318,323,328,333,337,342,347,351],{"title":52,"path":286,"stem":287,"icon":281},"\u002Fen\u002Fcli\u002Foverview","en\u002F4.cli\u002F1.overview",{"title":289,"path":290,"stem":291,"icon":292},"add db","\u002Fen\u002Fcli\u002Fadd-db","en\u002F4.cli\u002F10.add-db","i-lucide-database-zap",{"title":294,"path":295,"stem":296,"icon":297},"add compose \u002F add docker","\u002Fen\u002Fcli\u002Fadd-compose","en\u002F4.cli\u002F11.add-compose","i-lucide-container",{"title":299,"path":300,"stem":301,"icon":302},"add mail","\u002Fen\u002Fcli\u002Fadd-mail","en\u002F4.cli\u002F12.add-mail","i-lucide-mail-plus",{"title":304,"path":305,"stem":306,"icon":307},"add notification","\u002Fen\u002Fcli\u002Fadd-notification","en\u002F4.cli\u002F13.add-notification","i-lucide-bell-plus",{"title":309,"path":310,"stem":311,"icon":312},"add realtime","\u002Fen\u002Fcli\u002Fadd-realtime","en\u002F4.cli\u002F14.add-realtime","i-lucide-radio-tower",{"title":314,"path":315,"stem":316,"icon":317},"add seeder","\u002Fen\u002Fcli\u002Fadd-seeder","en\u002F4.cli\u002F15.add-seeder","i-lucide-sprout",{"title":319,"path":320,"stem":321,"icon":322},"new project","\u002Fen\u002Fcli\u002Fnew-project","en\u002F4.cli\u002F2.new-project","i-lucide-folder-plus",{"title":324,"path":325,"stem":326,"icon":327},"new module","\u002Fen\u002Fcli\u002Fnew-module","en\u002F4.cli\u002F3.new-module","i-lucide-blocks",{"title":329,"path":330,"stem":331,"icon":332},"add surface","\u002Fen\u002Fcli\u002Fadd-surface","en\u002F4.cli\u002F4.add-surface","i-lucide-layers-2",{"title":334,"path":335,"stem":336,"icon":244},"add core","\u002Fen\u002Fcli\u002Fadd-core","en\u002F4.cli\u002F5.add-core",{"title":338,"path":339,"stem":340,"icon":341},"add handler","\u002Fen\u002Fcli\u002Fadd-handler","en\u002F4.cli\u002F6.add-handler","i-lucide-webhook",{"title":343,"path":344,"stem":345,"icon":346},"new migration","\u002Fen\u002Fcli\u002Fnew-migration","en\u002F4.cli\u002F7.new-migration","i-lucide-file-plus",{"title":348,"path":349,"stem":350,"icon":265},"worker generators","\u002Fen\u002Fcli\u002Fworker-generators","en\u002F4.cli\u002F8.worker-generators",{"title":352,"path":353,"stem":354,"icon":355},"upgrade templates","\u002Fen\u002Fcli\u002Fupgrade-templates","en\u002F4.cli\u002F9.upgrade-templates","i-lucide-refresh-cw",{"title":357,"icon":227,"path":358,"stem":359,"children":360,"page":44},"errs","\u002Fen\u002Ferrs","en\u002F5.errs",[361,364,368],{"title":52,"path":362,"stem":363,"icon":227},"\u002Fen\u002Ferrs\u002Foverview","en\u002F5.errs\u002F1.overview",{"title":365,"path":366,"stem":367},"API","\u002Fen\u002Ferrs\u002Fapi","en\u002F5.errs\u002F2.api",{"title":369,"path":370,"stem":371,"icon":227},"Integration","\u002Fen\u002Ferrs\u002Fintegration","en\u002F5.errs\u002F3.integration",{"title":373,"icon":231,"path":374,"stem":375,"children":376,"page":44},"envconf","\u002Fen\u002Fenvconf","en\u002F6.envconf",[377,380],{"title":52,"path":378,"stem":379,"icon":231},"\u002Fen\u002Fenvconf\u002Foverview","en\u002F6.envconf\u002F1.overview",{"title":381,"path":382,"stem":383},"Recipes","\u002Fen\u002Fenvconf\u002Frecipes","en\u002F6.envconf\u002F2.recipes",{"title":385,"icon":386,"path":387,"stem":388,"children":389,"page":44},"httperr","i-lucide-globe","\u002Fen\u002Fhttperr","en\u002F7.httperr",[390,393,398],{"title":52,"path":391,"stem":392,"icon":386},"\u002Fen\u002Fhttperr\u002Foverview","en\u002F7.httperr\u002F1.overview",{"title":394,"path":395,"stem":396,"icon":397},"Error responses","\u002Fen\u002Fhttperr\u002Ferror-responses","en\u002F7.httperr\u002F2.error-responses","i-lucide-octagon-alert",{"title":399,"path":400,"stem":401,"icon":402},"Validation","\u002Fen\u002Fhttperr\u002Fvalidation","en\u002F7.httperr\u002F3.validation","i-lucide-badge-check",{"title":404,"icon":405,"path":406,"stem":407,"children":408,"page":44},"dbx","i-lucide-database","\u002Fen\u002Fdbx","en\u002F8.dbx",[409,412,416,420,425],{"title":52,"path":410,"stem":411,"icon":405},"\u002Fen\u002Fdbx\u002Foverview","en\u002F8.dbx\u002F1.overview",{"title":413,"path":414,"stem":415,"icon":106},"pgx","\u002Fen\u002Fdbx\u002Fpg","en\u002F8.dbx\u002F2.pg",{"title":417,"path":418,"stem":419,"icon":47},"bun","\u002Fen\u002Fdbx\u002Fbunx","en\u002F8.dbx\u002F3.bunx",{"title":421,"path":422,"stem":423,"icon":424},"Transactions","\u002Fen\u002Fdbx\u002Ftransactions","en\u002F8.dbx\u002F4.transactions","i-lucide-git-merge",{"title":426,"path":427,"stem":428,"icon":317},"Seeders","\u002Fen\u002Fdbx\u002Fseed","en\u002F8.dbx\u002F5.seed",{"title":430,"icon":55,"path":431,"stem":432,"children":433,"page":44},"paginate","\u002Fen\u002Fpaginate","en\u002F9.paginate",[434,437],{"title":52,"path":435,"stem":436,"icon":55},"\u002Fen\u002Fpaginate\u002Foverview","en\u002F9.paginate\u002F1.overview",{"title":438,"path":439,"stem":440,"icon":19},"Cursor pagination","\u002Fen\u002Fpaginate\u002Fcursor","en\u002F9.paginate\u002F2.cursor",{"id":442,"title":143,"body":443,"description":1277,"extension":1278,"links":1279,"meta":1280,"navigation":1281,"path":144,"seo":1282,"stem":145,"__hash__":1283},"docs_en\u002Fen\u002F14.notify\u002F3.broadcast.md",{"type":444,"value":445,"toc":1266},"minimark",[446,454,483,488,518,529,608,612,659,688,692,730,767,771,899,915,943,962,966,1024,1068,1072,1122,1133,1137,1178,1204,1208,1232,1236,1262],[447,448,449,450,453],"p",{},"This subpackage gives ",[451,452,130],"code",{}," its live-push channel: import it when a notification needs to reach connected clients immediately, on top of or instead of the database channel's durability.",[455,456,461],"pre",{"className":457,"code":458,"language":459,"meta":460,"style":460},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import \"github.com\u002Fgp-system\u002Fnotify\u002Fbroadcast\"\n","go","",[451,462,463],{"__ignoreMap":460},[464,465,468,472,476,480],"span",{"class":466,"line":467},"line",1,[464,469,471],{"class":470},"s7zQu","import",[464,473,475],{"class":474},"sMK4o"," \"",[464,477,479],{"class":478},"sBMFI","github.com\u002Fgp-system\u002Fnotify\u002Fbroadcast",[464,481,482],{"class":474},"\"\n",[484,485,487],"h2",{"id":486},"broadcast","Broadcast",[447,489,490,493,494,503,504,507,508,513,514,517],{},[451,491,492],{},"notify\u002Fbroadcast"," is the live-push side of a notification: a thin, Valkey-backed publisher built on ",[495,496,500],"a",{"href":497,"rel":498},"https:\u002F\u002Fgithub.com\u002Fcentrifugal\u002Fcentrifuge",[499],"nofollow",[451,501,502],{},"centrifugal\u002Fcentrifuge",", the library behind Centrifugo and Grafana Live. It is deliberately small: publishing is fire-and-forget, and durability lives entirely in the ",[495,505,506],{"href":140},"database channel",", not here. The package is shared between two roles: any process (typically the worker) imports it just to publish, while the dedicated ",[495,509,510],{"href":268},[451,511,512],{},"realtime"," gateway also uses it to hold connections and relay what gets published; see that page for the gateway side (",[451,515,516],{},"cmd\u002Frealtime",", scaling, the client).",[447,519,520,521,524,525,528],{},"A notification opts into this channel through a third content interface, alongside ",[451,522,523],{},"Mailable"," and ",[451,526,527],{},"Databasable",":",[455,530,532],{"className":457,"code":531,"language":459,"meta":460,"style":460},"type Broadcastable interface {\n    ToBroadcast(ctx context.Context, r Recipient) (broadcast.Message, error)\n}\n",[451,533,534,548,602],{"__ignoreMap":460},[464,535,536,539,542,545],{"class":466,"line":467},[464,537,538],{"class":474},"type",[464,540,541],{"class":478}," Broadcastable",[464,543,544],{"class":474}," interface",[464,546,547],{"class":474}," {\n",[464,549,551,555,558,562,565,568,571,574,577,580,583,586,588,590,593,595,599],{"class":466,"line":550},2,[464,552,554],{"class":553},"s2Zo4","    ToBroadcast",[464,556,557],{"class":474},"(",[464,559,561],{"class":560},"sHdIc","ctx",[464,563,564],{"class":478}," context",[464,566,567],{"class":474},".",[464,569,570],{"class":478},"Context",[464,572,573],{"class":474},",",[464,575,576],{"class":560}," r",[464,578,579],{"class":478}," Recipient",[464,581,582],{"class":474},")",[464,584,585],{"class":474}," (",[464,587,486],{"class":478},[464,589,567],{"class":474},[464,591,592],{"class":478},"Message",[464,594,573],{"class":474},[464,596,598],{"class":597},"spNyl"," error",[464,600,601],{"class":474},")\n",[464,603,605],{"class":466,"line":604},3,[464,606,607],{"class":474},"}\n",[484,609,611],{"id":610},"valkeyconfig","ValkeyConfig",[455,613,615],{"className":457,"code":614,"language":459,"meta":460,"style":460},"type ValkeyConfig struct {\n    Addr     string\n    Password string\n    DB       int\n}\n",[451,616,617,629,638,645,654],{"__ignoreMap":460},[464,618,619,621,624,627],{"class":466,"line":467},[464,620,538],{"class":474},[464,622,623],{"class":478}," ValkeyConfig",[464,625,626],{"class":474}," struct",[464,628,547],{"class":474},[464,630,631,635],{"class":466,"line":550},[464,632,634],{"class":633},"sTEyZ","    Addr     ",[464,636,637],{"class":597},"string\n",[464,639,640,643],{"class":466,"line":604},[464,641,642],{"class":633},"    Password ",[464,644,637],{"class":597},[464,646,648,651],{"class":466,"line":647},4,[464,649,650],{"class":633},"    DB       ",[464,652,653],{"class":597},"int\n",[464,655,657],{"class":466,"line":656},5,[464,658,607],{"class":474},[447,660,661,663,664,668,669,671,672,676,677,680,681,684,685,687],{},[451,662,611],{}," deliberately carries ",[665,666,667],"strong",{},"no env struct tags of its own",": ",[451,670,492],{}," is a library, not a place to own an env namespace. The ",[495,673,674],{"href":268},[451,675,512],{}," chassis builds one from its own ",[451,678,679],{},"VALKEY_*","-tagged configuration and passes it in (",[451,682,683],{},"realtime.Config.BroadcastValkey()","); a non-chassis publisher does the same from whatever config it already has. See ",[495,686,267],{"href":268}," for the exact mapping.",[484,689,691],{"id":690},"config","Config",[455,693,695],{"className":457,"code":694,"language":459,"meta":460,"style":460},"type Config struct {\n    ChannelPrefix string `env:\"CHANNEL_PREFIX\" envDefault:\"rt\"`\n}\n",[451,696,697,708,726],{"__ignoreMap":460},[464,698,699,701,704,706],{"class":466,"line":467},[464,700,538],{"class":474},[464,702,703],{"class":478}," Config",[464,705,626],{"class":474},[464,707,547],{"class":474},[464,709,710,713,716,719,723],{"class":466,"line":550},[464,711,712],{"class":633},"    ChannelPrefix ",[464,714,715],{"class":597},"string",[464,717,718],{"class":474}," `",[464,720,722],{"class":721},"sfazB","env:\"CHANNEL_PREFIX\" envDefault:\"rt\"",[464,724,725],{"class":474},"`\n",[464,727,728],{"class":466,"line":604},[464,729,607],{"class":474},[447,731,732,733,735,736,738,739,742,743,746,747,750,751,754,755,758,759,762,763,766],{},"Unlike ",[451,734,611],{},", ",[451,737,691],{}," ",[665,740,741],{},"does"," carry its own env tag (",[451,744,745],{},"CHANNEL_PREFIX","). The kit embeds it under its own ",[451,748,749],{},"REALTIME_"," prefix, so in a generated project it surfaces as ",[451,752,753],{},"REALTIME_CHANNEL_PREFIX","; see ",[495,756,267],{"href":757},"\u002Fen\u002Fkit\u002Frealtime#config"," and the ",[495,760,761],{"href":205},"configuration reference"," for the full variable list. ",[451,764,765],{},"ChannelPrefix"," namespaces every Valkey channel this package touches, so multiple projects (or multiple environments) can share one Valkey instance without colliding.",[484,768,770],{"id":769},"publishing","Publishing",[455,772,774],{"className":457,"code":773,"language":459,"meta":460,"style":460},"pub, err := broadcast.NewPublisher(ctx, valkeyCfg, cfg)\npub := broadcast.MustNewPublisher(ctx, valkeyCfg, cfg) \u002F\u002F panics on error; the usual main() constructor\n\nerr := pub.Publish(ctx, channel, msg)\nerr = pub.Close()\n",[451,775,776,813,845,851,882],{"__ignoreMap":460},[464,777,778,781,783,786,789,792,794,797,799,801,803,806,808,811],{"class":466,"line":467},[464,779,780],{"class":633},"pub",[464,782,573],{"class":474},[464,784,785],{"class":633}," err ",[464,787,788],{"class":474},":=",[464,790,791],{"class":633}," broadcast",[464,793,567],{"class":474},[464,795,796],{"class":553},"NewPublisher",[464,798,557],{"class":474},[464,800,561],{"class":633},[464,802,573],{"class":474},[464,804,805],{"class":633}," valkeyCfg",[464,807,573],{"class":474},[464,809,810],{"class":633}," cfg",[464,812,601],{"class":474},[464,814,815,818,820,822,824,827,829,831,833,835,837,839,841],{"class":466,"line":550},[464,816,817],{"class":633},"pub ",[464,819,788],{"class":474},[464,821,791],{"class":633},[464,823,567],{"class":474},[464,825,826],{"class":553},"MustNewPublisher",[464,828,557],{"class":474},[464,830,561],{"class":633},[464,832,573],{"class":474},[464,834,805],{"class":633},[464,836,573],{"class":474},[464,838,810],{"class":633},[464,840,582],{"class":474},[464,842,844],{"class":843},"sHwdD"," \u002F\u002F panics on error; the usual main() constructor\n",[464,846,847],{"class":466,"line":604},[464,848,850],{"emptyLinePlaceholder":849},true,"\n",[464,852,853,856,858,861,863,866,868,870,872,875,877,880],{"class":466,"line":647},[464,854,855],{"class":633},"err ",[464,857,788],{"class":474},[464,859,860],{"class":633}," pub",[464,862,567],{"class":474},[464,864,865],{"class":553},"Publish",[464,867,557],{"class":474},[464,869,561],{"class":633},[464,871,573],{"class":474},[464,873,874],{"class":633}," channel",[464,876,573],{"class":474},[464,878,879],{"class":633}," msg",[464,881,601],{"class":474},[464,883,884,886,889,891,893,896],{"class":466,"line":656},[464,885,855],{"class":633},[464,887,888],{"class":474},"=",[464,890,860],{"class":633},[464,892,567],{"class":474},[464,894,895],{"class":553},"Close",[464,897,898],{"class":474},"()\n",[447,900,901,904,905,908,909,911,912,914],{},[451,902,903],{},"Publisher"," is a publish-only ",[451,906,907],{},"centrifuge.Node"," sharing the project's Valkey broker; it never accepts a connection, so a process that only publishes (a worker, a one-shot script) depends on nothing but a network client to Valkey. ",[451,910,865],{}," is fire-and-forget: a failed publish is returned to the caller, not retried and not queued, which is why the ",[495,913,506],{"href":140}," is the durability story, not this one.",[455,916,918],{"className":457,"code":917,"language":459,"meta":460,"style":460},"notify.NewBroadcastChannel(pub, cfg.ChannelPrefix)\n",[451,919,920],{"__ignoreMap":460},[464,921,922,924,926,929,931,933,935,937,939,941],{"class":466,"line":467},[464,923,130],{"class":633},[464,925,567],{"class":474},[464,927,928],{"class":553},"NewBroadcastChannel",[464,930,557],{"class":474},[464,932,780],{"class":633},[464,934,573],{"class":474},[464,936,810],{"class":633},[464,938,567],{"class":474},[464,940,765],{"class":633},[464,942,601],{"class":474},[447,944,945,946,948,949,952,953,957,958,961],{},"wraps a ",[451,947,903],{}," into the ",[451,950,951],{},"notify.Channel"," the ",[495,954,956],{"href":955},"\u002Fen\u002Fnotify\u002Foverview#the-hub","Hub"," dispatches ",[451,959,960],{},"Broadcastable"," content to.",[484,963,965],{"id":964},"channel-naming-userchannel-and-topicchannel","Channel naming: UserChannel and TopicChannel",[455,967,969],{"className":457,"code":968,"language":459,"meta":460,"style":460},"func UserChannel(prefix, subject string) string  \u002F\u002F a recipient's own channel\nfunc TopicChannel(prefix, topic string) string    \u002F\u002F a shared, opt-in topic channel\n",[451,970,971,999],{"__ignoreMap":460},[464,972,973,976,979,981,984,986,989,992,994,996],{"class":466,"line":467},[464,974,975],{"class":474},"func",[464,977,978],{"class":553}," UserChannel",[464,980,557],{"class":474},[464,982,983],{"class":560},"prefix",[464,985,573],{"class":474},[464,987,988],{"class":560}," subject",[464,990,991],{"class":597}," string",[464,993,582],{"class":474},[464,995,991],{"class":597},[464,997,998],{"class":843},"  \u002F\u002F a recipient's own channel\n",[464,1000,1001,1003,1006,1008,1010,1012,1015,1017,1019,1021],{"class":466,"line":550},[464,1002,975],{"class":474},[464,1004,1005],{"class":553}," TopicChannel",[464,1007,557],{"class":474},[464,1009,983],{"class":560},[464,1011,573],{"class":474},[464,1013,1014],{"class":560}," topic",[464,1016,991],{"class":597},[464,1018,582],{"class":474},[464,1020,991],{"class":597},[464,1022,1023],{"class":843},"    \u002F\u002F a shared, opt-in topic channel\n",[447,1025,1026,1027,1030,1031,1034,1035,1038,1039,1042,1043,1047,1048,1051,1052,1051,1055,1058,1059,1064,1065,567],{},"Every connection the ",[495,1028,1029],{"href":268},"realtime gateway"," accepts is server-side subscribed to its own ",[451,1032,1033],{},"UserChannel",", which is how a notification addressed to a ",[451,1036,1037],{},"Recipient.ID"," reaches exactly that user's live connection with no client-side subscribe call. ",[451,1040,1041],{},"TopicChannel"," names the shared channels used for broadcast beyond single-recipient notifications (announcements, admin alerts); see ",[495,1044,1046],{"href":1045},"\u002Fen\u002Fkit\u002Frealtime#the-gateway","Realtime: shared topic channels"," for the ",[451,1049,1050],{},"TopicAuth","\u002F",[451,1053,1054],{},"Allow",[451,1056,1057],{},"AllowPrefix"," authorization these require, which relies on ",[495,1060,1061],{"href":94},[451,1062,1063],{},"auth\u002Frbac","'s ",[451,1066,1067],{},"Identity",[484,1069,1071],{"id":1070},"newnode","NewNode",[455,1073,1075],{"className":457,"code":1074,"language":459,"meta":460,"style":460},"func NewNode(cfg ValkeyConfig, centrifugeCfg centrifuge.Config) (*centrifuge.Node, error)\n",[451,1076,1077],{"__ignoreMap":460},[464,1078,1079,1081,1084,1086,1089,1091,1093,1096,1099,1101,1103,1105,1108,1111,1113,1116,1118,1120],{"class":466,"line":467},[464,1080,975],{"class":474},[464,1082,1083],{"class":553}," NewNode",[464,1085,557],{"class":474},[464,1087,1088],{"class":560},"cfg",[464,1090,623],{"class":478},[464,1092,573],{"class":474},[464,1094,1095],{"class":560}," centrifugeCfg",[464,1097,1098],{"class":478}," centrifuge",[464,1100,567],{"class":474},[464,1102,691],{"class":478},[464,1104,582],{"class":474},[464,1106,1107],{"class":474}," (*",[464,1109,1110],{"class":478},"centrifuge",[464,1112,567],{"class":474},[464,1114,1115],{"class":478},"Node",[464,1117,573],{"class":474},[464,1119,598],{"class":597},[464,1121,601],{"class":474},[447,1123,1124,1126,1127,1129,1130,1132],{},[451,1125,1071],{}," is the shared constructor behind both roles: a publish-only node (what ",[451,1128,796],{}," wraps) and the full connection-accepting node the ",[495,1131,1029],{"href":268}," runs. It wires the Valkey broker centrifuge needs to fan a publish out across every gateway instance, regardless of which process published it.",[484,1134,1136],{"id":1135},"memory","Memory",[455,1138,1140],{"className":457,"code":1139,"language":459,"meta":460,"style":460},"mem := broadcast.NewMemory()\npub := mem.All() \u002F\u002F []broadcast.Published, everything captured so far\n",[451,1141,1142,1158],{"__ignoreMap":460},[464,1143,1144,1147,1149,1151,1153,1156],{"class":466,"line":467},[464,1145,1146],{"class":633},"mem ",[464,1148,788],{"class":474},[464,1150,791],{"class":633},[464,1152,567],{"class":474},[464,1154,1155],{"class":553},"NewMemory",[464,1157,898],{"class":474},[464,1159,1160,1162,1164,1167,1169,1172,1175],{"class":466,"line":550},[464,1161,817],{"class":633},[464,1163,788],{"class":474},[464,1165,1166],{"class":633}," mem",[464,1168,567],{"class":474},[464,1170,1171],{"class":553},"All",[464,1173,1174],{"class":474},"()",[464,1176,1177],{"class":843}," \u002F\u002F []broadcast.Published, everything captured so far\n",[447,1179,1180,1182,1183,1186,1187,1190,1191,524,1197,1203],{},[451,1181,1136],{}," is a test double: it captures every publish instead of touching Valkey, so a notification's ",[451,1184,1185],{},"ToBroadcast"," (or a channel wired with ",[451,1188,1189],{},"notify.NewBroadcastChannel(mem, prefix)",") can be asserted on directly, the same way ",[495,1192,1194],{"href":1193},"\u002Fen\u002Fmail\u002Foverview#dev-mailers-memory-log-and-discard",[451,1195,1196],{},"mail.NewMemory()",[495,1198,1200],{"href":1199},"\u002Fen\u002Fnotify\u002Fdatabase#memorystore",[451,1201,1202],{},"notifydatabase.NewMemoryStore()"," stand in for their real drivers.",[484,1205,1207],{"id":1206},"with-the-kit","With the kit",[447,1209,1210,1211,1213,1214,1216,1217,1064,1221,1223,1224,1227,1228,567],{},"The gateway that turns a ",[451,1212,865],{}," call into a live client push, its scaling model, and the client-side subscription story are all on the ",[495,1215,267],{"href":268}," page. The authorization model for shared topic channels builds on ",[495,1218,1219],{"href":94},[451,1220,1063],{},[451,1222,1067],{},". Wiring the broadcast channel into a notification's ",[451,1225,1226],{},"Via"," is covered by ",[495,1229,1230],{"href":305},[451,1231,304],{},[484,1233,1235],{"id":1234},"related-pages","Related pages",[1237,1238,1239,1251,1257],"ul",{},[1240,1241,1242,1244,1245,735,1247,1250],"li",{},[495,1243,52],{"href":136},": the ",[451,1246,956],{},[451,1248,1249],{},"Notification",", and the mail channel.",[1240,1252,1253,1256],{},[495,1254,1255],{"href":140},"Database",": the durable channel this one complements, never replaces.",[1240,1258,1259,1261],{},[495,1260,267],{"href":268},": the gateway, its scaling model, and the client.",[1263,1264,1265],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":460,"searchDepth":550,"depth":550,"links":1267},[1268,1269,1270,1271,1272,1273,1274,1275,1276],{"id":486,"depth":550,"text":487},{"id":610,"depth":550,"text":611},{"id":690,"depth":550,"text":691},{"id":769,"depth":550,"text":770},{"id":964,"depth":550,"text":965},{"id":1070,"depth":550,"text":1071},{"id":1135,"depth":550,"text":1136},{"id":1206,"depth":550,"text":1207},{"id":1234,"depth":550,"text":1235},"The realtime broadcast notification channel.","md",null,{},{"icon":131},{"title":143,"description":1277},"0f7jyzbHxa0hVXFL839YrJxHhlgSXpmK6-jZSbaFKgM",[1285,1287],{"title":139,"path":140,"stem":141,"description":1286,"icon":131,"children":-1},"The durable database notification channel.",{"title":52,"path":153,"stem":154,"description":1288,"icon":148,"children":-1},"Object storage behind a Driver\u002FDisk\u002FManager model, with in-memory, local-disk and S3-compatible drivers.",1785445902612]