[{"data":1,"prerenderedAt":3061},["ShallowReactive",2],{"docs:/docs/sdks/go":3},{"id":4,"title":5,"body":6,"description":3054,"extension":3055,"meta":3056,"navigation":75,"path":3057,"seo":3058,"stem":3059,"__hash__":3060},"docs/docs/sdks/go.md","Go SDK",{"type":7,"value":8,"toc":3032},"minimark",[9,13,17,22,52,56,345,394,398,419,494,498,519,666,670,683,785,801,805,823,974,984,988,1006,1137,1162,1166,1386,1394,1398,1405,1587,1614,1617,1620,1648,1658,1663,1752,1758,1860,1884,1888,2114,2117,2123,2177,2181,2184,2436,2440,2443,2621,2625,2632,2733,2747,2751,2972,2976,2990,2994,3028],[10,11,5],"h1",{"id":12},"go-sdk",[14,15,16],"p",{},"The official NoLag SDK for Go. Idiomatic Go client with goroutines\nand channels support.",[18,19,21],"h2",{"id":20},"installation","Installation",[23,24,25],"code-tabs",{},[26,27,33],"pre",{"className":28,"code":29,"filename":30,"language":31,"meta":32,"style":32},"language-bash shiki shiki-themes github-light github-dark","go get github.com/NoLagApp/go-sdk\n","Terminal","bash","",[34,35,36],"code",{"__ignoreMap":32},[37,38,41,45,49],"span",{"class":39,"line":40},"line",1,[37,42,44],{"class":43},"sScJk","go",[37,46,48],{"class":47},"sZZnC"," get",[37,50,51],{"class":47}," github.com/NoLagApp/go-sdk\n",[18,53,55],{"id":54},"quick-start","Quick Start",[23,57,58],{},[26,59,63],{"className":60,"code":61,"filename":62,"language":44,"meta":32,"style":32},"language-go shiki shiki-themes github-light github-dark","package main\n\nimport (\n    \"fmt\"\n    \"time\"\n\n    nolag \"github.com/NoLagApp/go-sdk\"\n)\n\n// The slug returned when you created the app (slugs carry a random suffix)\nconst appSlug = \"chat-a3f9\"\n\nfunc main() {\n    // Create client with your actor token\n    client := nolag.New(\"your-actor-token\")\n\n    // Broker errors (unknown room, no access) are asynchronous: register before Connect\n    client.OnError(func(err *nolag.ServerError) {\n        fmt.Printf(\"nolag: %v\\n\", err)\n    })\n\n    // Connect to NoLag\n    if err := client.Connect(); err != nil {\n        panic(err)\n    }\n    defer client.Close()\n\n    // Scope to an app and a room. The room must already exist.\n    room := client.SetApp(appSlug).SetRoom(\"general\")\n\n    // Subscribe to a topic in the room\n    err := room.Subscribe(\"messages\", func(data any, meta nolag.MessageMeta) {\n        fmt.Printf(\"Received: %v\\n\", data)\n    })\n    if err != nil {\n        panic(err)\n    }\n\n    // Publish a message\n    if err := room.Emit(\"messages\", map[string]any{\"hello\": \"world\"}); err != nil {\n        fmt.Printf(\"Emit failed: %v\\n\", err)\n    }\n\n    // Get actor ID assigned by server\n    fmt.Println(\"Actor ID:\", client.ActorID())\n\n    // Keep running\n    time.Sleep(60 * time.Second)\n}\n","Go",[34,64,65,70,77,83,89,95,100,106,112,117,123,129,134,140,146,152,157,163,169,175,181,186,192,198,204,210,216,221,227,233,238,244,250,256,261,267,272,277,282,288,294,300,305,310,316,322,327,333,339],{"__ignoreMap":32},[37,66,67],{"class":39,"line":40},[37,68,69],{},"package main\n",[37,71,73],{"class":39,"line":72},2,[37,74,76],{"emptyLinePlaceholder":75},true,"\n",[37,78,80],{"class":39,"line":79},3,[37,81,82],{},"import (\n",[37,84,86],{"class":39,"line":85},4,[37,87,88],{},"    \"fmt\"\n",[37,90,92],{"class":39,"line":91},5,[37,93,94],{},"    \"time\"\n",[37,96,98],{"class":39,"line":97},6,[37,99,76],{"emptyLinePlaceholder":75},[37,101,103],{"class":39,"line":102},7,[37,104,105],{},"    nolag \"github.com/NoLagApp/go-sdk\"\n",[37,107,109],{"class":39,"line":108},8,[37,110,111],{},")\n",[37,113,115],{"class":39,"line":114},9,[37,116,76],{"emptyLinePlaceholder":75},[37,118,120],{"class":39,"line":119},10,[37,121,122],{},"// The slug returned when you created the app (slugs carry a random suffix)\n",[37,124,126],{"class":39,"line":125},11,[37,127,128],{},"const appSlug = \"chat-a3f9\"\n",[37,130,132],{"class":39,"line":131},12,[37,133,76],{"emptyLinePlaceholder":75},[37,135,137],{"class":39,"line":136},13,[37,138,139],{},"func main() {\n",[37,141,143],{"class":39,"line":142},14,[37,144,145],{},"    // Create client with your actor token\n",[37,147,149],{"class":39,"line":148},15,[37,150,151],{},"    client := nolag.New(\"your-actor-token\")\n",[37,153,155],{"class":39,"line":154},16,[37,156,76],{"emptyLinePlaceholder":75},[37,158,160],{"class":39,"line":159},17,[37,161,162],{},"    // Broker errors (unknown room, no access) are asynchronous: register before Connect\n",[37,164,166],{"class":39,"line":165},18,[37,167,168],{},"    client.OnError(func(err *nolag.ServerError) {\n",[37,170,172],{"class":39,"line":171},19,[37,173,174],{},"        fmt.Printf(\"nolag: %v\\n\", err)\n",[37,176,178],{"class":39,"line":177},20,[37,179,180],{},"    })\n",[37,182,184],{"class":39,"line":183},21,[37,185,76],{"emptyLinePlaceholder":75},[37,187,189],{"class":39,"line":188},22,[37,190,191],{},"    // Connect to NoLag\n",[37,193,195],{"class":39,"line":194},23,[37,196,197],{},"    if err := client.Connect(); err != nil {\n",[37,199,201],{"class":39,"line":200},24,[37,202,203],{},"        panic(err)\n",[37,205,207],{"class":39,"line":206},25,[37,208,209],{},"    }\n",[37,211,213],{"class":39,"line":212},26,[37,214,215],{},"    defer client.Close()\n",[37,217,219],{"class":39,"line":218},27,[37,220,76],{"emptyLinePlaceholder":75},[37,222,224],{"class":39,"line":223},28,[37,225,226],{},"    // Scope to an app and a room. The room must already exist.\n",[37,228,230],{"class":39,"line":229},29,[37,231,232],{},"    room := client.SetApp(appSlug).SetRoom(\"general\")\n",[37,234,236],{"class":39,"line":235},30,[37,237,76],{"emptyLinePlaceholder":75},[37,239,241],{"class":39,"line":240},31,[37,242,243],{},"    // Subscribe to a topic in the room\n",[37,245,247],{"class":39,"line":246},32,[37,248,249],{},"    err := room.Subscribe(\"messages\", func(data any, meta nolag.MessageMeta) {\n",[37,251,253],{"class":39,"line":252},33,[37,254,255],{},"        fmt.Printf(\"Received: %v\\n\", data)\n",[37,257,259],{"class":39,"line":258},34,[37,260,180],{},[37,262,264],{"class":39,"line":263},35,[37,265,266],{},"    if err != nil {\n",[37,268,270],{"class":39,"line":269},36,[37,271,203],{},[37,273,275],{"class":39,"line":274},37,[37,276,209],{},[37,278,280],{"class":39,"line":279},38,[37,281,76],{"emptyLinePlaceholder":75},[37,283,285],{"class":39,"line":284},39,[37,286,287],{},"    // Publish a message\n",[37,289,291],{"class":39,"line":290},40,[37,292,293],{},"    if err := room.Emit(\"messages\", map[string]any{\"hello\": \"world\"}); err != nil {\n",[37,295,297],{"class":39,"line":296},41,[37,298,299],{},"        fmt.Printf(\"Emit failed: %v\\n\", err)\n",[37,301,303],{"class":39,"line":302},42,[37,304,209],{},[37,306,308],{"class":39,"line":307},43,[37,309,76],{"emptyLinePlaceholder":75},[37,311,313],{"class":39,"line":312},44,[37,314,315],{},"    // Get actor ID assigned by server\n",[37,317,319],{"class":39,"line":318},45,[37,320,321],{},"    fmt.Println(\"Actor ID:\", client.ActorID())\n",[37,323,325],{"class":39,"line":324},46,[37,326,76],{"emptyLinePlaceholder":75},[37,328,330],{"class":39,"line":329},47,[37,331,332],{},"    // Keep running\n",[37,334,336],{"class":39,"line":335},48,[37,337,338],{},"    time.Sleep(60 * time.Second)\n",[37,340,342],{"class":39,"line":341},49,[37,343,344],{},"}\n",[346,347,349],"callout",{"type":348},"info",[14,350,351,355,356,359,360,363,364,367,368,371,372,377,378,381,382,385,386,389,390,393],{},[352,353,354],"strong",{},"Topics live inside rooms."," The only hierarchy is ",[34,357,358],{},"app/room/topic",": the room context above addresses ",[34,361,362],{},"chat-a3f9/general/messages",". Topic names are single tokens (",[34,365,366],{},"[a-zA-Z0-9_:-]",", no ",[34,369,370],{},"/","). Rooms never exist implicitly: create them via the ",[373,374,376],"a",{"href":375},"#rest-api-client","REST API"," or the portal first, or subscribing reports ",[34,379,380],{},"unknown_topic"," (42940) through ",[34,383,384],{},"OnError",". App slugs always get a random 4-hex suffix, so read ",[34,387,388],{},"Slug"," from the create response and pass that to ",[34,391,392],{},"SetApp()",".",[18,395,397],{"id":396},"configuration","Configuration",[14,399,400,401,404,405,408,409,412,413,404,416,393],{},"Every field you omit keeps its default. ",[34,402,403],{},"Reconnect"," and ",[34,406,407],{},"QoS"," are pointers so\nthat an omitted field is distinguishable from ",[34,410,411],{},"false"," or QoS 0; set them with\n",[34,414,415],{},"nolag.Bool()",[34,417,418],{},"nolag.QoSLevel()",[23,420,421],{},[26,422,424],{"className":60,"code":423,"filename":62,"language":44,"meta":32,"style":32},"options := nolag.Options{\n    URL:                  \"wss://broker.nolag.app/ws\",          // Custom broker URL\n    Reconnect:            nolag.Bool(false),                    // Auto-reconnect; omit to keep the default (true)\n    ReconnectInterval:    5 * time.Second,                      // Reconnect interval (default: 5s)\n    MaxReconnectAttempts: 10,                                   // Max attempts (default: 10); 0 keeps the default, -1 is unlimited\n    HeartbeatInterval:    30 * time.Second,                     // Heartbeat interval (default: 30s); 0 keeps the default, -1 disables\n    QoS:                  nolag.QoSLevel(nolag.QoSAtLeastOnce), // Default QoS; omit to keep the default (QoSAtLeastOnce)\n    LoadBalance:          true,                                 // Enable load balancing (default: false)\n    LoadBalanceGroup:     \"workers\",                            // Load balance group name\n    ActorTokenID:         \"custom-id\",                          // Optional actor token identifier\n    Debug:                true,                                 // Enable debug logging (default: false)\n}\n\nclient := nolag.New(\"your-actor-token\", options)\n",[34,425,426,431,436,441,446,451,456,461,466,471,476,481,485,489],{"__ignoreMap":32},[37,427,428],{"class":39,"line":40},[37,429,430],{},"options := nolag.Options{\n",[37,432,433],{"class":39,"line":72},[37,434,435],{},"    URL:                  \"wss://broker.nolag.app/ws\",          // Custom broker URL\n",[37,437,438],{"class":39,"line":79},[37,439,440],{},"    Reconnect:            nolag.Bool(false),                    // Auto-reconnect; omit to keep the default (true)\n",[37,442,443],{"class":39,"line":85},[37,444,445],{},"    ReconnectInterval:    5 * time.Second,                      // Reconnect interval (default: 5s)\n",[37,447,448],{"class":39,"line":91},[37,449,450],{},"    MaxReconnectAttempts: 10,                                   // Max attempts (default: 10); 0 keeps the default, -1 is unlimited\n",[37,452,453],{"class":39,"line":97},[37,454,455],{},"    HeartbeatInterval:    30 * time.Second,                     // Heartbeat interval (default: 30s); 0 keeps the default, -1 disables\n",[37,457,458],{"class":39,"line":102},[37,459,460],{},"    QoS:                  nolag.QoSLevel(nolag.QoSAtLeastOnce), // Default QoS; omit to keep the default (QoSAtLeastOnce)\n",[37,462,463],{"class":39,"line":108},[37,464,465],{},"    LoadBalance:          true,                                 // Enable load balancing (default: false)\n",[37,467,468],{"class":39,"line":114},[37,469,470],{},"    LoadBalanceGroup:     \"workers\",                            // Load balance group name\n",[37,472,473],{"class":39,"line":119},[37,474,475],{},"    ActorTokenID:         \"custom-id\",                          // Optional actor token identifier\n",[37,477,478],{"class":39,"line":125},[37,479,480],{},"    Debug:                true,                                 // Enable debug logging (default: false)\n",[37,482,483],{"class":39,"line":131},[37,484,344],{},[37,486,487],{"class":39,"line":136},[37,488,76],{"emptyLinePlaceholder":75},[37,490,491],{"class":39,"line":142},[37,492,493],{},"client := nolag.New(\"your-actor-token\", options)\n",[18,495,497],{"id":496},"subscribing-to-topics","Subscribing to Topics",[14,499,500,503,504,507,508,511,512,514,515,393],{},[34,501,502],{},"Subscribe",", ",[34,505,506],{},"Unsubscribe",", and all filter methods return ",[34,509,510],{},"error"," for local\nfailures (not connected, encode failures). Broker-side refusals arrive on\n",[34,513,384],{}," instead; see ",[373,516,518],{"href":517},"#error-handling","Error Handling",[23,520,521],{},[26,522,524],{"className":60,"code":523,"filename":62,"language":44,"meta":32,"style":32},"client := nolag.New(\"your-actor-token\")\nroom := client.SetApp(appSlug).SetRoom(\"general\")\nhandler := func(data any, meta nolag.MessageMeta) {\n    fmt.Printf(\"Message from %s: %v\\n\", meta.Sender, data)\n}\n\n// Basic subscription. Subscribe returns an error\nerr := room.Subscribe(\"messages\", handler)\nif err != nil {\n    fmt.Printf(\"Subscribe failed: %v\\n\", err)\n}\n\n// With options (QoS, load balancing, filters)\nerr = room.Subscribe(\"tasks\", handler, nolag.SubscribeOptions{\n    QoS:              nolag.QoSLevel(nolag.QoSAtLeastOnce),\n    LoadBalance:      nolag.Bool(true),\n    LoadBalanceGroup: \"workers\",\n    Filters:          []any{\"priority:high\", \"region:us\"},\n})\nif err != nil {\n    fmt.Printf(\"Subscribe failed: %v\\n\", err)\n}\n\n// Client-level equivalent: the full app/room/topic path\nerr = client.Subscribe(appSlug+\"/general/messages\", handler)\n\n// Unsubscribe (also returns an error)\nif err := room.Unsubscribe(\"messages\"); err != nil {\n    fmt.Printf(\"Unsubscribe failed: %v\\n\", err)\n}\n",[34,525,526,531,536,541,546,550,554,559,564,569,574,578,582,587,592,597,602,607,612,617,621,625,629,633,638,643,647,652,657,662],{"__ignoreMap":32},[37,527,528],{"class":39,"line":40},[37,529,530],{},"client := nolag.New(\"your-actor-token\")\n",[37,532,533],{"class":39,"line":72},[37,534,535],{},"room := client.SetApp(appSlug).SetRoom(\"general\")\n",[37,537,538],{"class":39,"line":79},[37,539,540],{},"handler := func(data any, meta nolag.MessageMeta) {\n",[37,542,543],{"class":39,"line":85},[37,544,545],{},"    fmt.Printf(\"Message from %s: %v\\n\", meta.Sender, data)\n",[37,547,548],{"class":39,"line":91},[37,549,344],{},[37,551,552],{"class":39,"line":97},[37,553,76],{"emptyLinePlaceholder":75},[37,555,556],{"class":39,"line":102},[37,557,558],{},"// Basic subscription. Subscribe returns an error\n",[37,560,561],{"class":39,"line":108},[37,562,563],{},"err := room.Subscribe(\"messages\", handler)\n",[37,565,566],{"class":39,"line":114},[37,567,568],{},"if err != nil {\n",[37,570,571],{"class":39,"line":119},[37,572,573],{},"    fmt.Printf(\"Subscribe failed: %v\\n\", err)\n",[37,575,576],{"class":39,"line":125},[37,577,344],{},[37,579,580],{"class":39,"line":131},[37,581,76],{"emptyLinePlaceholder":75},[37,583,584],{"class":39,"line":136},[37,585,586],{},"// With options (QoS, load balancing, filters)\n",[37,588,589],{"class":39,"line":142},[37,590,591],{},"err = room.Subscribe(\"tasks\", handler, nolag.SubscribeOptions{\n",[37,593,594],{"class":39,"line":148},[37,595,596],{},"    QoS:              nolag.QoSLevel(nolag.QoSAtLeastOnce),\n",[37,598,599],{"class":39,"line":154},[37,600,601],{},"    LoadBalance:      nolag.Bool(true),\n",[37,603,604],{"class":39,"line":159},[37,605,606],{},"    LoadBalanceGroup: \"workers\",\n",[37,608,609],{"class":39,"line":165},[37,610,611],{},"    Filters:          []any{\"priority:high\", \"region:us\"},\n",[37,613,614],{"class":39,"line":171},[37,615,616],{},"})\n",[37,618,619],{"class":39,"line":177},[37,620,568],{},[37,622,623],{"class":39,"line":183},[37,624,573],{},[37,626,627],{"class":39,"line":188},[37,628,344],{},[37,630,631],{"class":39,"line":194},[37,632,76],{"emptyLinePlaceholder":75},[37,634,635],{"class":39,"line":200},[37,636,637],{},"// Client-level equivalent: the full app/room/topic path\n",[37,639,640],{"class":39,"line":206},[37,641,642],{},"err = client.Subscribe(appSlug+\"/general/messages\", handler)\n",[37,644,645],{"class":39,"line":212},[37,646,76],{"emptyLinePlaceholder":75},[37,648,649],{"class":39,"line":218},[37,650,651],{},"// Unsubscribe (also returns an error)\n",[37,653,654],{"class":39,"line":223},[37,655,656],{},"if err := room.Unsubscribe(\"messages\"); err != nil {\n",[37,658,659],{"class":39,"line":229},[37,660,661],{},"    fmt.Printf(\"Unsubscribe failed: %v\\n\", err)\n",[37,663,664],{"class":39,"line":235},[37,665,344],{},[18,667,669],{"id":668},"publishing-messages","Publishing Messages",[14,671,672,675,676,678,679,682],{},[34,673,674],{},"Emit"," returns an ",[34,677,510],{}," for local failures. Use ",[34,680,681],{},"EmitOptions"," to set QoS,\nretain, and filter targeting.",[23,684,685],{},[26,686,688],{"className":60,"code":687,"filename":62,"language":44,"meta":32,"style":32},"client := nolag.New(\"your-actor-token\")\nroom := client.SetApp(appSlug).SetRoom(\"general\")\n\n// Publish any data (maps, structs, strings, etc.). Emit returns an error\nif err := room.Emit(\"messages\", map[string]any{\"text\": \"Hello!\"}); err != nil {\n    fmt.Printf(\"Emit failed: %v\\n\", err)\n}\n\n// With options\nerr := room.Emit(\"status\", map[string]any{\"online\": true}, nolag.EmitOptions{\n    QoS:    nolag.QoSLevel(nolag.QoSAtLeastOnce), // Override the default QoS\n    Retain: true,                                 // Broker keeps the last message for new subscribers\n    Filter: \"priority:high\",                      // Target subscribers with this filter\n    Echo:   nolag.Bool(false),                    // Per-connection flag; see the note below\n})\nif err != nil {\n    fmt.Printf(\"Emit failed: %v\\n\", err)\n}\n\n// Client-level equivalent: the full path\nerr = client.Emit(appSlug+\"/general/messages\", map[string]any{\"text\": \"Hello!\"})\n",[34,689,690,694,698,702,707,712,717,721,725,730,735,740,745,750,755,759,763,767,771,775,780],{"__ignoreMap":32},[37,691,692],{"class":39,"line":40},[37,693,530],{},[37,695,696],{"class":39,"line":72},[37,697,535],{},[37,699,700],{"class":39,"line":79},[37,701,76],{"emptyLinePlaceholder":75},[37,703,704],{"class":39,"line":85},[37,705,706],{},"// Publish any data (maps, structs, strings, etc.). Emit returns an error\n",[37,708,709],{"class":39,"line":91},[37,710,711],{},"if err := room.Emit(\"messages\", map[string]any{\"text\": \"Hello!\"}); err != nil {\n",[37,713,714],{"class":39,"line":97},[37,715,716],{},"    fmt.Printf(\"Emit failed: %v\\n\", err)\n",[37,718,719],{"class":39,"line":102},[37,720,344],{},[37,722,723],{"class":39,"line":108},[37,724,76],{"emptyLinePlaceholder":75},[37,726,727],{"class":39,"line":114},[37,728,729],{},"// With options\n",[37,731,732],{"class":39,"line":119},[37,733,734],{},"err := room.Emit(\"status\", map[string]any{\"online\": true}, nolag.EmitOptions{\n",[37,736,737],{"class":39,"line":125},[37,738,739],{},"    QoS:    nolag.QoSLevel(nolag.QoSAtLeastOnce), // Override the default QoS\n",[37,741,742],{"class":39,"line":131},[37,743,744],{},"    Retain: true,                                 // Broker keeps the last message for new subscribers\n",[37,746,747],{"class":39,"line":136},[37,748,749],{},"    Filter: \"priority:high\",                      // Target subscribers with this filter\n",[37,751,752],{"class":39,"line":142},[37,753,754],{},"    Echo:   nolag.Bool(false),                    // Per-connection flag; see the note below\n",[37,756,757],{"class":39,"line":148},[37,758,616],{},[37,760,761],{"class":39,"line":154},[37,762,568],{},[37,764,765],{"class":39,"line":159},[37,766,716],{},[37,768,769],{"class":39,"line":165},[37,770,344],{},[37,772,773],{"class":39,"line":171},[37,774,76],{"emptyLinePlaceholder":75},[37,776,777],{"class":39,"line":177},[37,778,779],{},"// Client-level equivalent: the full path\n",[37,781,782],{"class":39,"line":183},[37,783,784],{},"err = client.Emit(appSlug+\"/general/messages\", map[string]any{\"text\": \"Hello!\"})\n",[346,786,787],{"type":348},[14,788,789,792,793,796,797,800],{},[352,790,791],{},"Publishers never receive their own messages."," The broker drops a message before delivering it to the actor that published it, whatever ",[34,794,795],{},"Echo"," is set to. Append your own message to the UI locally. ",[34,798,799],{},"Echo: nolag.Bool(false)"," only adds a per-connection drop for the rare case of two connections sharing one actor token.",[18,802,804],{"id":803},"fluent-api-setapp-setroom","Fluent API (SetApp / SetRoom)",[14,806,807,808,811,812,815,816,819,820,393],{},"The fluent API scopes all operations to an ",[352,809,810],{},"app/room"," pair.\nTopics are automatically prefixed, so ",[34,813,814],{},"room.Emit(\"messages\", ...)"," publishes\nto ",[34,817,818],{},"\"chat-a3f9/general/messages\""," when the app slug is ",[34,821,822],{},"chat-a3f9",[23,824,825],{},[26,826,828],{"className":60,"code":827,"filename":62,"language":44,"meta":32,"style":32},"client := nolag.New(\"your-actor-token\")\n\n// The fluent API scopes operations to an app/room.\n// Topics are automatically prefixed with \"app/room/\".\nroom := client.SetApp(appSlug).SetRoom(\"general\")\n\n// Subscribe: topic becomes \"chat-a3f9/general/messages\"\nerr := room.Subscribe(\"messages\", func(data any, meta nolag.MessageMeta) {\n    fmt.Printf(\"Message: %v\\n\", data)\n})\nif err != nil {\n    fmt.Printf(\"Subscribe failed: %v\\n\", err)\n}\n\n// Emit: same prefix\nif err := room.Emit(\"messages\", map[string]any{\"text\": \"Hello!\"}); err != nil {\n    fmt.Printf(\"Emit failed: %v\\n\", err)\n}\n\n// Unsubscribe\nroom.Unsubscribe(\"messages\")\n\n// Filter management on a room\nroom.SetFilters(\"messages\", []any{\"priority:high\"})\nroom.AddFilters(\"messages\", []string{\"priority:medium\"})\nroom.RemoveFilters(\"messages\", []string{\"priority:high\"})\n\n// Room-scoped presence\nroom.SetPresence(map[string]any{\"status\": \"online\"})\n\n// Get the full topic prefix\nfmt.Println(room.Prefix()) // \"chat-a3f9/general\"\n",[34,829,830,834,838,843,848,852,856,861,866,871,875,879,883,887,891,896,900,904,908,912,917,922,926,931,936,941,946,950,955,960,964,969],{"__ignoreMap":32},[37,831,832],{"class":39,"line":40},[37,833,530],{},[37,835,836],{"class":39,"line":72},[37,837,76],{"emptyLinePlaceholder":75},[37,839,840],{"class":39,"line":79},[37,841,842],{},"// The fluent API scopes operations to an app/room.\n",[37,844,845],{"class":39,"line":85},[37,846,847],{},"// Topics are automatically prefixed with \"app/room/\".\n",[37,849,850],{"class":39,"line":91},[37,851,535],{},[37,853,854],{"class":39,"line":97},[37,855,76],{"emptyLinePlaceholder":75},[37,857,858],{"class":39,"line":102},[37,859,860],{},"// Subscribe: topic becomes \"chat-a3f9/general/messages\"\n",[37,862,863],{"class":39,"line":108},[37,864,865],{},"err := room.Subscribe(\"messages\", func(data any, meta nolag.MessageMeta) {\n",[37,867,868],{"class":39,"line":114},[37,869,870],{},"    fmt.Printf(\"Message: %v\\n\", data)\n",[37,872,873],{"class":39,"line":119},[37,874,616],{},[37,876,877],{"class":39,"line":125},[37,878,568],{},[37,880,881],{"class":39,"line":131},[37,882,573],{},[37,884,885],{"class":39,"line":136},[37,886,344],{},[37,888,889],{"class":39,"line":142},[37,890,76],{"emptyLinePlaceholder":75},[37,892,893],{"class":39,"line":148},[37,894,895],{},"// Emit: same prefix\n",[37,897,898],{"class":39,"line":154},[37,899,711],{},[37,901,902],{"class":39,"line":159},[37,903,716],{},[37,905,906],{"class":39,"line":165},[37,907,344],{},[37,909,910],{"class":39,"line":171},[37,911,76],{"emptyLinePlaceholder":75},[37,913,914],{"class":39,"line":177},[37,915,916],{},"// Unsubscribe\n",[37,918,919],{"class":39,"line":183},[37,920,921],{},"room.Unsubscribe(\"messages\")\n",[37,923,924],{"class":39,"line":188},[37,925,76],{"emptyLinePlaceholder":75},[37,927,928],{"class":39,"line":194},[37,929,930],{},"// Filter management on a room\n",[37,932,933],{"class":39,"line":200},[37,934,935],{},"room.SetFilters(\"messages\", []any{\"priority:high\"})\n",[37,937,938],{"class":39,"line":206},[37,939,940],{},"room.AddFilters(\"messages\", []string{\"priority:medium\"})\n",[37,942,943],{"class":39,"line":212},[37,944,945],{},"room.RemoveFilters(\"messages\", []string{\"priority:high\"})\n",[37,947,948],{"class":39,"line":218},[37,949,76],{"emptyLinePlaceholder":75},[37,951,952],{"class":39,"line":223},[37,953,954],{},"// Room-scoped presence\n",[37,956,957],{"class":39,"line":229},[37,958,959],{},"room.SetPresence(map[string]any{\"status\": \"online\"})\n",[37,961,962],{"class":39,"line":235},[37,963,76],{"emptyLinePlaceholder":75},[37,965,966],{"class":39,"line":240},[37,967,968],{},"// Get the full topic prefix\n",[37,970,971],{"class":39,"line":246},[37,972,973],{},"fmt.Println(room.Prefix()) // \"chat-a3f9/general\"\n",[14,975,976,977,979,980,983],{},"Topic messages are delivered to the handler you pass to ",[34,978,502],{},". ",[34,981,982],{},"room.On","\nregisters a named client event handler and does not receive topic messages.",[18,985,987],{"id":986},"filter-management","Filter Management",[14,989,990,991,503,994,997,998,1001,1002,1005],{},"Filters narrow which messages a subscriber receives. You can set filters at subscribe time\nor manage them dynamically with ",[34,992,993],{},"SetFilters",[34,995,996],{},"AddFilters",", and\n",[34,999,1000],{},"RemoveFilters",". On the publish side, use ",[34,1003,1004],{},"EmitOptions.Filter"," to\ntarget specific subscribers.",[23,1007,1008],{},[26,1009,1011],{"className":60,"code":1010,"filename":62,"language":44,"meta":32,"style":32},"client := nolag.New(\"your-actor-token\")\nroom := client.SetApp(appSlug).SetRoom(\"ops\")\nhandler := func(data any, meta nolag.MessageMeta) {\n    fmt.Printf(\"Order (filter %s): %v\\n\", meta.Filter, data)\n}\n\n// Subscribe with initial filters\nerr := room.Subscribe(\"orders\", handler, nolag.SubscribeOptions{\n    Filters: []any{\"region:us\", \"status:pending\"},\n})\n\n// Replace all filters for a topic (empty slice = receive all messages)\nerr = room.SetFilters(\"orders\", []any{\"region:eu\", \"status:shipped\"})\n\n// Add filters to existing set (deduplicates automatically)\nerr = room.AddFilters(\"orders\", []string{\"status:delivered\"})\n\n// Remove specific filters\nerr = room.RemoveFilters(\"orders\", []string{\"status:shipped\"})\n\n// Emit with a filter value. Reaches subscribers with this filter, plus subscribers with no filters\nerr = room.Emit(\"orders\", orderData, nolag.EmitOptions{\n    Filter: \"region:us\",\n})\n\n// Client-level equivalents take the full path\nerr = client.SetFilters(appSlug+\"/ops/orders\", []any{\"region:eu\"})\n",[34,1012,1013,1017,1022,1026,1031,1035,1039,1044,1049,1054,1058,1062,1067,1072,1076,1081,1086,1090,1095,1100,1104,1109,1114,1119,1123,1127,1132],{"__ignoreMap":32},[37,1014,1015],{"class":39,"line":40},[37,1016,530],{},[37,1018,1019],{"class":39,"line":72},[37,1020,1021],{},"room := client.SetApp(appSlug).SetRoom(\"ops\")\n",[37,1023,1024],{"class":39,"line":79},[37,1025,540],{},[37,1027,1028],{"class":39,"line":85},[37,1029,1030],{},"    fmt.Printf(\"Order (filter %s): %v\\n\", meta.Filter, data)\n",[37,1032,1033],{"class":39,"line":91},[37,1034,344],{},[37,1036,1037],{"class":39,"line":97},[37,1038,76],{"emptyLinePlaceholder":75},[37,1040,1041],{"class":39,"line":102},[37,1042,1043],{},"// Subscribe with initial filters\n",[37,1045,1046],{"class":39,"line":108},[37,1047,1048],{},"err := room.Subscribe(\"orders\", handler, nolag.SubscribeOptions{\n",[37,1050,1051],{"class":39,"line":114},[37,1052,1053],{},"    Filters: []any{\"region:us\", \"status:pending\"},\n",[37,1055,1056],{"class":39,"line":119},[37,1057,616],{},[37,1059,1060],{"class":39,"line":125},[37,1061,76],{"emptyLinePlaceholder":75},[37,1063,1064],{"class":39,"line":131},[37,1065,1066],{},"// Replace all filters for a topic (empty slice = receive all messages)\n",[37,1068,1069],{"class":39,"line":136},[37,1070,1071],{},"err = room.SetFilters(\"orders\", []any{\"region:eu\", \"status:shipped\"})\n",[37,1073,1074],{"class":39,"line":142},[37,1075,76],{"emptyLinePlaceholder":75},[37,1077,1078],{"class":39,"line":148},[37,1079,1080],{},"// Add filters to existing set (deduplicates automatically)\n",[37,1082,1083],{"class":39,"line":154},[37,1084,1085],{},"err = room.AddFilters(\"orders\", []string{\"status:delivered\"})\n",[37,1087,1088],{"class":39,"line":159},[37,1089,76],{"emptyLinePlaceholder":75},[37,1091,1092],{"class":39,"line":165},[37,1093,1094],{},"// Remove specific filters\n",[37,1096,1097],{"class":39,"line":171},[37,1098,1099],{},"err = room.RemoveFilters(\"orders\", []string{\"status:shipped\"})\n",[37,1101,1102],{"class":39,"line":177},[37,1103,76],{"emptyLinePlaceholder":75},[37,1105,1106],{"class":39,"line":183},[37,1107,1108],{},"// Emit with a filter value. Reaches subscribers with this filter, plus subscribers with no filters\n",[37,1110,1111],{"class":39,"line":188},[37,1112,1113],{},"err = room.Emit(\"orders\", orderData, nolag.EmitOptions{\n",[37,1115,1116],{"class":39,"line":194},[37,1117,1118],{},"    Filter: \"region:us\",\n",[37,1120,1121],{"class":39,"line":200},[37,1122,616],{},[37,1124,1125],{"class":39,"line":206},[37,1126,76],{"emptyLinePlaceholder":75},[37,1128,1129],{"class":39,"line":212},[37,1130,1131],{},"// Client-level equivalents take the full path\n",[37,1133,1134],{"class":39,"line":218},[37,1135,1136],{},"err = client.SetFilters(appSlug+\"/ops/orders\", []any{\"region:eu\"})\n",[346,1138,1139],{"type":348},[14,1140,1141,1144,1145,503,1147,503,1150,1153,1154,1157,1158,393],{},[352,1142,1143],{},"No filters = wildcard:"," Subscribing without filters receives all messages on the topic, including filtered publishes. Subscribing with filters only receives messages published with a matching filter. Messages published without a filter are only delivered to wildcard (no-filter) subscribers. Each filter is its own routing key, not a privacy boundary. Max 100 filters per topic; values must not contain ",[34,1146,370],{},[34,1148,1149],{},"#",[34,1151,1152],{},"+"," or ",[34,1155,1156],{},"|",". See ",[373,1159,1161],{"href":1160},"/docs/concepts/filters","Filters",[18,1163,1165],{"id":1164},"connection-events","Connection Events",[23,1167,1168],{},[26,1169,1171],{"className":60,"code":1170,"filename":62,"language":44,"meta":32,"style":32},"client := nolag.New(\"your-actor-token\")\n\n// Listen for connection events\nclient.On(\"connected\", func(args ...any) {\n    fmt.Println(\"Connected!\")\n})\n\nclient.On(\"disconnected\", func(args ...any) {\n    fmt.Println(\"Disconnected\")\n})\n\nclient.On(\"reconnecting\", func(args ...any) {\n    attempt := args[0].(int)\n    fmt.Printf(\"Reconnecting... attempt %d\\n\", attempt)\n})\n\n// Prefer OnError for broker errors: it delivers a typed *nolag.ServerError\n// with the error code, topic, and remediation hint. See Error Handling below.\nclient.OnError(func(err *nolag.ServerError) {\n    fmt.Printf(\"Error: %v\\n\", err)\n})\n\n// Presence events: args[0] is a nolag.ActorPresence (ActorTokenID, Presence)\nclient.On(\"presence:join\", func(args ...any) {\n    actor := args[0].(nolag.ActorPresence)\n    fmt.Printf(\"%s joined: %v\\n\", actor.ActorTokenID, actor.Presence)\n})\n\n// Hydration: with a hydration webhook configured on the app, the broker sends\n// the webhook's response body once per subscribe. topic is the bare topic name\nclient.On(\"hydration\", func(args ...any) {\n    topic := args[0].(string)\n    data := args[1]\n    fmt.Printf(\"Initial state for %s: %v\\n\", topic, data)\n})\n\n// Remove all handlers for an event\nclient.Off(\"error\")\n\n// Check connection status\nif client.Status() == nolag.StatusConnected {\n    fmt.Println(\"We're connected!\")\n}\n\n// Get the actor ID assigned by the server after authentication\nfmt.Println(\"Actor ID:\", client.ActorID())\n",[34,1172,1173,1177,1181,1186,1191,1196,1200,1204,1209,1214,1218,1222,1227,1232,1237,1241,1245,1250,1255,1260,1265,1269,1273,1278,1283,1288,1293,1297,1301,1306,1311,1316,1321,1326,1331,1335,1339,1344,1349,1353,1358,1363,1368,1372,1376,1381],{"__ignoreMap":32},[37,1174,1175],{"class":39,"line":40},[37,1176,530],{},[37,1178,1179],{"class":39,"line":72},[37,1180,76],{"emptyLinePlaceholder":75},[37,1182,1183],{"class":39,"line":79},[37,1184,1185],{},"// Listen for connection events\n",[37,1187,1188],{"class":39,"line":85},[37,1189,1190],{},"client.On(\"connected\", func(args ...any) {\n",[37,1192,1193],{"class":39,"line":91},[37,1194,1195],{},"    fmt.Println(\"Connected!\")\n",[37,1197,1198],{"class":39,"line":97},[37,1199,616],{},[37,1201,1202],{"class":39,"line":102},[37,1203,76],{"emptyLinePlaceholder":75},[37,1205,1206],{"class":39,"line":108},[37,1207,1208],{},"client.On(\"disconnected\", func(args ...any) {\n",[37,1210,1211],{"class":39,"line":114},[37,1212,1213],{},"    fmt.Println(\"Disconnected\")\n",[37,1215,1216],{"class":39,"line":119},[37,1217,616],{},[37,1219,1220],{"class":39,"line":125},[37,1221,76],{"emptyLinePlaceholder":75},[37,1223,1224],{"class":39,"line":131},[37,1225,1226],{},"client.On(\"reconnecting\", func(args ...any) {\n",[37,1228,1229],{"class":39,"line":136},[37,1230,1231],{},"    attempt := args[0].(int)\n",[37,1233,1234],{"class":39,"line":142},[37,1235,1236],{},"    fmt.Printf(\"Reconnecting... attempt %d\\n\", attempt)\n",[37,1238,1239],{"class":39,"line":148},[37,1240,616],{},[37,1242,1243],{"class":39,"line":154},[37,1244,76],{"emptyLinePlaceholder":75},[37,1246,1247],{"class":39,"line":159},[37,1248,1249],{},"// Prefer OnError for broker errors: it delivers a typed *nolag.ServerError\n",[37,1251,1252],{"class":39,"line":165},[37,1253,1254],{},"// with the error code, topic, and remediation hint. See Error Handling below.\n",[37,1256,1257],{"class":39,"line":171},[37,1258,1259],{},"client.OnError(func(err *nolag.ServerError) {\n",[37,1261,1262],{"class":39,"line":177},[37,1263,1264],{},"    fmt.Printf(\"Error: %v\\n\", err)\n",[37,1266,1267],{"class":39,"line":183},[37,1268,616],{},[37,1270,1271],{"class":39,"line":188},[37,1272,76],{"emptyLinePlaceholder":75},[37,1274,1275],{"class":39,"line":194},[37,1276,1277],{},"// Presence events: args[0] is a nolag.ActorPresence (ActorTokenID, Presence)\n",[37,1279,1280],{"class":39,"line":200},[37,1281,1282],{},"client.On(\"presence:join\", func(args ...any) {\n",[37,1284,1285],{"class":39,"line":206},[37,1286,1287],{},"    actor := args[0].(nolag.ActorPresence)\n",[37,1289,1290],{"class":39,"line":212},[37,1291,1292],{},"    fmt.Printf(\"%s joined: %v\\n\", actor.ActorTokenID, actor.Presence)\n",[37,1294,1295],{"class":39,"line":218},[37,1296,616],{},[37,1298,1299],{"class":39,"line":223},[37,1300,76],{"emptyLinePlaceholder":75},[37,1302,1303],{"class":39,"line":229},[37,1304,1305],{},"// Hydration: with a hydration webhook configured on the app, the broker sends\n",[37,1307,1308],{"class":39,"line":235},[37,1309,1310],{},"// the webhook's response body once per subscribe. topic is the bare topic name\n",[37,1312,1313],{"class":39,"line":240},[37,1314,1315],{},"client.On(\"hydration\", func(args ...any) {\n",[37,1317,1318],{"class":39,"line":246},[37,1319,1320],{},"    topic := args[0].(string)\n",[37,1322,1323],{"class":39,"line":252},[37,1324,1325],{},"    data := args[1]\n",[37,1327,1328],{"class":39,"line":258},[37,1329,1330],{},"    fmt.Printf(\"Initial state for %s: %v\\n\", topic, data)\n",[37,1332,1333],{"class":39,"line":263},[37,1334,616],{},[37,1336,1337],{"class":39,"line":269},[37,1338,76],{"emptyLinePlaceholder":75},[37,1340,1341],{"class":39,"line":274},[37,1342,1343],{},"// Remove all handlers for an event\n",[37,1345,1346],{"class":39,"line":279},[37,1347,1348],{},"client.Off(\"error\")\n",[37,1350,1351],{"class":39,"line":284},[37,1352,76],{"emptyLinePlaceholder":75},[37,1354,1355],{"class":39,"line":290},[37,1356,1357],{},"// Check connection status\n",[37,1359,1360],{"class":39,"line":296},[37,1361,1362],{},"if client.Status() == nolag.StatusConnected {\n",[37,1364,1365],{"class":39,"line":302},[37,1366,1367],{},"    fmt.Println(\"We're connected!\")\n",[37,1369,1370],{"class":39,"line":307},[37,1371,344],{},[37,1373,1374],{"class":39,"line":312},[37,1375,76],{"emptyLinePlaceholder":75},[37,1377,1378],{"class":39,"line":318},[37,1379,1380],{},"// Get the actor ID assigned by the server after authentication\n",[37,1382,1383],{"class":39,"line":324},[37,1384,1385],{},"fmt.Println(\"Actor ID:\", client.ActorID())\n",[14,1387,1388,1389,1393],{},"See ",[373,1390,1392],{"href":1391},"/docs/concepts/webhooks","Webhooks"," for configuring hydration.",[18,1395,1397],{"id":1396},"presence","Presence",[14,1399,1400,1401,1404],{},"Presence is room-scoped. Set it through the room context or pass the room slug\nto ",[34,1402,1403],{},"SetPresence","; presence events arrive on the client.",[23,1406,1407],{},[26,1408,1410],{"className":60,"code":1409,"filename":62,"language":44,"meta":32,"style":32},"client := nolag.New(\"your-actor-token\")\nroom := client.SetApp(appSlug).SetRoom(\"general\")\n\n// Set your presence data in the room\nif err := room.SetPresence(map[string]any{\n    \"status\": \"online\",\n    \"typing\": false,\n}); err != nil {\n    fmt.Printf(\"SetPresence failed: %v\\n\", err)\n}\n\n// Or with an explicit room slug\nif err := client.SetPresence(map[string]any{\"status\": \"online\"}, \"general\"); err != nil {\n    fmt.Printf(\"SetPresence failed: %v\\n\", err)\n}\n\n// Fetch the presence list for a room. Entries carry ActorTokenID, Presence and Status\npresenceList, err := client.GetPresence(\"general\")\nif err == nil {\n    for _, actor := range presenceList {\n        fmt.Printf(\"%s: %v %s\\n\", actor.ActorTokenID, actor.Presence, actor.Status)\n    }\n}\n\n// Presence events are client-level: presence:join, presence:leave, presence:update\nclient.On(\"presence:join\", func(args ...any) {\n    actor := args[0].(nolag.ActorPresence)\n    fmt.Printf(\"%s joined with %v\\n\", actor.ActorTokenID, actor.Presence)\n})\n\nclient.On(\"presence:leave\", func(args ...any) {\n    actor := args[0].(nolag.ActorPresence)\n    fmt.Printf(\"%s left\\n\", actor.ActorTokenID)\n})\n\nclient.On(\"presence:update\", func(args ...any) {\n    actor := args[0].(nolag.ActorPresence)\n    fmt.Printf(\"%s updated: %v\\n\", actor.ActorTokenID, actor.Presence)\n})\n",[34,1411,1412,1416,1420,1424,1429,1434,1439,1444,1449,1454,1458,1462,1467,1472,1476,1480,1484,1489,1494,1499,1504,1509,1513,1517,1521,1526,1530,1534,1539,1543,1547,1552,1556,1561,1565,1569,1574,1578,1583],{"__ignoreMap":32},[37,1413,1414],{"class":39,"line":40},[37,1415,530],{},[37,1417,1418],{"class":39,"line":72},[37,1419,535],{},[37,1421,1422],{"class":39,"line":79},[37,1423,76],{"emptyLinePlaceholder":75},[37,1425,1426],{"class":39,"line":85},[37,1427,1428],{},"// Set your presence data in the room\n",[37,1430,1431],{"class":39,"line":91},[37,1432,1433],{},"if err := room.SetPresence(map[string]any{\n",[37,1435,1436],{"class":39,"line":97},[37,1437,1438],{},"    \"status\": \"online\",\n",[37,1440,1441],{"class":39,"line":102},[37,1442,1443],{},"    \"typing\": false,\n",[37,1445,1446],{"class":39,"line":108},[37,1447,1448],{},"}); err != nil {\n",[37,1450,1451],{"class":39,"line":114},[37,1452,1453],{},"    fmt.Printf(\"SetPresence failed: %v\\n\", err)\n",[37,1455,1456],{"class":39,"line":119},[37,1457,344],{},[37,1459,1460],{"class":39,"line":125},[37,1461,76],{"emptyLinePlaceholder":75},[37,1463,1464],{"class":39,"line":131},[37,1465,1466],{},"// Or with an explicit room slug\n",[37,1468,1469],{"class":39,"line":136},[37,1470,1471],{},"if err := client.SetPresence(map[string]any{\"status\": \"online\"}, \"general\"); err != nil {\n",[37,1473,1474],{"class":39,"line":142},[37,1475,1453],{},[37,1477,1478],{"class":39,"line":148},[37,1479,344],{},[37,1481,1482],{"class":39,"line":154},[37,1483,76],{"emptyLinePlaceholder":75},[37,1485,1486],{"class":39,"line":159},[37,1487,1488],{},"// Fetch the presence list for a room. Entries carry ActorTokenID, Presence and Status\n",[37,1490,1491],{"class":39,"line":165},[37,1492,1493],{},"presenceList, err := client.GetPresence(\"general\")\n",[37,1495,1496],{"class":39,"line":171},[37,1497,1498],{},"if err == nil {\n",[37,1500,1501],{"class":39,"line":177},[37,1502,1503],{},"    for _, actor := range presenceList {\n",[37,1505,1506],{"class":39,"line":183},[37,1507,1508],{},"        fmt.Printf(\"%s: %v %s\\n\", actor.ActorTokenID, actor.Presence, actor.Status)\n",[37,1510,1511],{"class":39,"line":188},[37,1512,209],{},[37,1514,1515],{"class":39,"line":194},[37,1516,344],{},[37,1518,1519],{"class":39,"line":200},[37,1520,76],{"emptyLinePlaceholder":75},[37,1522,1523],{"class":39,"line":206},[37,1524,1525],{},"// Presence events are client-level: presence:join, presence:leave, presence:update\n",[37,1527,1528],{"class":39,"line":212},[37,1529,1282],{},[37,1531,1532],{"class":39,"line":218},[37,1533,1287],{},[37,1535,1536],{"class":39,"line":223},[37,1537,1538],{},"    fmt.Printf(\"%s joined with %v\\n\", actor.ActorTokenID, actor.Presence)\n",[37,1540,1541],{"class":39,"line":229},[37,1542,616],{},[37,1544,1545],{"class":39,"line":235},[37,1546,76],{"emptyLinePlaceholder":75},[37,1548,1549],{"class":39,"line":240},[37,1550,1551],{},"client.On(\"presence:leave\", func(args ...any) {\n",[37,1553,1554],{"class":39,"line":246},[37,1555,1287],{},[37,1557,1558],{"class":39,"line":252},[37,1559,1560],{},"    fmt.Printf(\"%s left\\n\", actor.ActorTokenID)\n",[37,1562,1563],{"class":39,"line":258},[37,1564,616],{},[37,1566,1567],{"class":39,"line":263},[37,1568,76],{"emptyLinePlaceholder":75},[37,1570,1571],{"class":39,"line":269},[37,1572,1573],{},"client.On(\"presence:update\", func(args ...any) {\n",[37,1575,1576],{"class":39,"line":274},[37,1577,1287],{},[37,1579,1580],{"class":39,"line":279},[37,1581,1582],{},"    fmt.Printf(\"%s updated: %v\\n\", actor.ActorTokenID, actor.Presence)\n",[37,1584,1585],{"class":39,"line":284},[37,1586,616],{},[14,1588,1589,1592,1593,1596,1597,503,1600,1602,1603,1606,1607,404,1610,1613],{},[34,1590,1591],{},"client.SetPresence(data)"," without a room is deprecated and is not broadcast.\n",[34,1594,1595],{},"GetPresence"," takes a room slug and fills only ",[34,1598,1599],{},"ActorTokenID",[34,1601,1397],{}," and\n",[34,1604,1605],{},"Status"," on each entry; ",[34,1608,1609],{},"ActorType",[34,1611,1612],{},"JoinedAt"," are not sent by the broker.",[18,1615,518],{"id":1616},"error-handling",[14,1618,1619],{},"Errors reach you through two separate channels, and confusing them is the most\ncommon reason a Go client appears to do nothing:",[1621,1622,1623,1630],"ul",{},[1624,1625,1626,1629],"li",{},[352,1627,1628],{},"Returned errors"," come from local, synchronous problems: not connected, encode\nfailures, timeouts. Every operation returns one.",[1624,1631,1632,1635,1636,404,1638,1640,1641,1643,1644,1647],{},[352,1633,1634],{},"Broker errors"," are asynchronous. ",[34,1637,502],{},[34,1639,674],{}," are fire-and-forget,\nso a rejected subscription or an unwritable topic is reported later on the error\nevent, not as a return value. ",[34,1642,502],{}," returning ",[34,1645,1646],{},"nil"," means the frame was\nsent, not that the broker accepted it.",[14,1649,1650,1651,1653,1654,1657],{},"Register ",[34,1652,384],{}," before calling ",[34,1655,1656],{},"Connect"," so nothing is missed during the\nhandshake.",[1659,1660,1662],"h3",{"id":1661},"broker-errors","Broker Errors",[23,1664,1665],{},[26,1666,1668],{"className":60,"code":1667,"filename":62,"language":44,"meta":32,"style":32},"client := nolag.New(\"your-actor-token\")\n\nclient.OnError(func(err *nolag.ServerError) {\n    // err also satisfies the error interface\n    log.Printf(\"nolag: %v\", err)\n\n    switch err.Name {\n    case \"unknown_topic\":\n        // The room has not been created. Provision it via the rooms API.\n        log.Printf(\"missing room for topic %s: %s\", err.Topic, err.Hint)\n    case \"not_authorized\":\n        log.Printf(\"actor lacks access to %s\", err.Topic)\n    }\n})\n\nif err := client.Connect(); err != nil {\n    log.Fatal(err)\n}\n",[34,1669,1670,1674,1678,1682,1687,1692,1696,1701,1706,1711,1716,1721,1726,1730,1734,1738,1743,1748],{"__ignoreMap":32},[37,1671,1672],{"class":39,"line":40},[37,1673,530],{},[37,1675,1676],{"class":39,"line":72},[37,1677,76],{"emptyLinePlaceholder":75},[37,1679,1680],{"class":39,"line":79},[37,1681,1259],{},[37,1683,1684],{"class":39,"line":85},[37,1685,1686],{},"    // err also satisfies the error interface\n",[37,1688,1689],{"class":39,"line":91},[37,1690,1691],{},"    log.Printf(\"nolag: %v\", err)\n",[37,1693,1694],{"class":39,"line":97},[37,1695,76],{"emptyLinePlaceholder":75},[37,1697,1698],{"class":39,"line":102},[37,1699,1700],{},"    switch err.Name {\n",[37,1702,1703],{"class":39,"line":108},[37,1704,1705],{},"    case \"unknown_topic\":\n",[37,1707,1708],{"class":39,"line":114},[37,1709,1710],{},"        // The room has not been created. Provision it via the rooms API.\n",[37,1712,1713],{"class":39,"line":119},[37,1714,1715],{},"        log.Printf(\"missing room for topic %s: %s\", err.Topic, err.Hint)\n",[37,1717,1718],{"class":39,"line":125},[37,1719,1720],{},"    case \"not_authorized\":\n",[37,1722,1723],{"class":39,"line":131},[37,1724,1725],{},"        log.Printf(\"actor lacks access to %s\", err.Topic)\n",[37,1727,1728],{"class":39,"line":136},[37,1729,209],{},[37,1731,1732],{"class":39,"line":142},[37,1733,616],{},[37,1735,1736],{"class":39,"line":148},[37,1737,76],{"emptyLinePlaceholder":75},[37,1739,1740],{"class":39,"line":154},[37,1741,1742],{},"if err := client.Connect(); err != nil {\n",[37,1744,1745],{"class":39,"line":159},[37,1746,1747],{},"    log.Fatal(err)\n",[37,1749,1750],{"class":39,"line":165},[37,1751,344],{},[14,1753,1754,1757],{},[34,1755,1756],{},"ServerError"," carries the full frame:",[1759,1760,1761,1777],"table",{},[1762,1763,1764],"thead",{},[1765,1766,1767,1771,1774],"tr",{},[1768,1769,1770],"th",{},"Field",[1768,1772,1773],{},"Type",[1768,1775,1776],{},"Description",[1778,1779,1780,1800,1818,1832,1846],"tbody",{},[1765,1781,1782,1788,1793],{},[1783,1784,1785],"td",{},[34,1786,1787],{},"Code",[1783,1789,1790],{},[34,1791,1792],{},"int",[1783,1794,1795,1796,1799],{},"Numeric error code, for example ",[34,1797,1798],{},"42940",". Zero if the broker sent none",[1765,1801,1802,1807,1812],{},[1783,1803,1804],{},[34,1805,1806],{},"Name",[1783,1808,1809],{},[34,1810,1811],{},"string",[1783,1813,1814,1815,1817],{},"Machine-readable name, for example ",[34,1816,380],{},". Always set",[1765,1819,1820,1825,1829],{},[1783,1821,1822],{},[34,1823,1824],{},"Topic",[1783,1826,1827],{},[34,1828,1811],{},[1783,1830,1831],{},"The topic the error refers to, when topic-scoped",[1765,1833,1834,1839,1843],{},[1783,1835,1836],{},[34,1837,1838],{},"Hint",[1783,1840,1841],{},[34,1842,1811],{},[1783,1844,1845],{},"Remediation hint from the broker, when provided",[1765,1847,1848,1853,1857],{},[1783,1849,1850],{},[34,1851,1852],{},"MsgRef",[1783,1854,1855],{},[34,1856,1811],{},[1783,1858,1859],{},"The publish this error responds to, when applicable",[346,1861,1862],{"type":348},[14,1863,1864,1867,1868,404,1870,1872,1873,1876,1877,1880,1881,1883],{},[352,1865,1866],{},"Protocol version 2."," ",[34,1869,1787],{},[34,1871,1838],{}," require protocol version 2. The SDK requests v2 automatically and\nthe negotiated result is available from ",[34,1874,1875],{},"client.ProtocolVersion()",". Against an older\nbroker this returns ",[34,1878,1879],{},"1"," and only ",[34,1882,1806],{}," is populated.",[1659,1885,1887],{"id":1886},"returned-errors","Returned Errors",[23,1889,1890],{},[26,1891,1893],{"className":60,"code":1892,"filename":62,"language":44,"meta":32,"style":32},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n\n    nolag \"github.com/NoLagApp/go-sdk\"\n)\n\nfunc main() {\n    client := nolag.New(\"your-actor-token\")\n\n    // Connect with error handling\n    if err := client.Connect(); err != nil {\n        fmt.Printf(\"Connection failed: %v\\n\", err)\n        return\n    }\n    defer client.Close()\n\n    // \"chat-a3f9\" is the suffixed slug returned when you created the app\n    room := client.SetApp(\"chat-a3f9\").SetRoom(\"general\")\n    handler := func(data any, meta nolag.MessageMeta) {}\n\n    // All operations return errors\n    if err := room.Subscribe(\"messages\", handler); err != nil {\n        // Check for specific error types\n        if errors.Is(err, nolag.ErrNotConnected) {\n            fmt.Println(\"Not connected!\")\n        }\n        fmt.Printf(\"Subscribe failed: %v\\n\", err)\n    }\n\n    if err := room.Emit(\"messages\", \"data\"); err != nil {\n        fmt.Printf(\"Emit failed: %v\\n\", err)\n    }\n\n    if err := room.Unsubscribe(\"messages\"); err != nil {\n        fmt.Printf(\"Unsubscribe failed: %v\\n\", err)\n    }\n\n    if err := room.SetFilters(\"messages\", []any{\"filter1\"}); err != nil {\n        fmt.Printf(\"SetFilters failed: %v\\n\", err)\n    }\n}\n\n// Sentinel errors available:\n// nolag.ErrNotConnected - operation attempted while disconnected\n// nolag.ErrAuthFailed   - authentication failed\n// nolag.ErrTimeout      - operation timed out\n",[34,1894,1895,1899,1903,1907,1912,1916,1920,1924,1928,1932,1936,1940,1944,1949,1953,1958,1963,1967,1971,1975,1980,1985,1990,1994,1999,2004,2009,2014,2019,2024,2029,2033,2037,2042,2046,2050,2054,2059,2064,2068,2072,2077,2082,2086,2090,2094,2099,2104,2109],{"__ignoreMap":32},[37,1896,1897],{"class":39,"line":40},[37,1898,69],{},[37,1900,1901],{"class":39,"line":72},[37,1902,76],{"emptyLinePlaceholder":75},[37,1904,1905],{"class":39,"line":79},[37,1906,82],{},[37,1908,1909],{"class":39,"line":85},[37,1910,1911],{},"    \"errors\"\n",[37,1913,1914],{"class":39,"line":91},[37,1915,88],{},[37,1917,1918],{"class":39,"line":97},[37,1919,76],{"emptyLinePlaceholder":75},[37,1921,1922],{"class":39,"line":102},[37,1923,105],{},[37,1925,1926],{"class":39,"line":108},[37,1927,111],{},[37,1929,1930],{"class":39,"line":114},[37,1931,76],{"emptyLinePlaceholder":75},[37,1933,1934],{"class":39,"line":119},[37,1935,139],{},[37,1937,1938],{"class":39,"line":125},[37,1939,151],{},[37,1941,1942],{"class":39,"line":131},[37,1943,76],{"emptyLinePlaceholder":75},[37,1945,1946],{"class":39,"line":136},[37,1947,1948],{},"    // Connect with error handling\n",[37,1950,1951],{"class":39,"line":142},[37,1952,197],{},[37,1954,1955],{"class":39,"line":148},[37,1956,1957],{},"        fmt.Printf(\"Connection failed: %v\\n\", err)\n",[37,1959,1960],{"class":39,"line":154},[37,1961,1962],{},"        return\n",[37,1964,1965],{"class":39,"line":159},[37,1966,209],{},[37,1968,1969],{"class":39,"line":165},[37,1970,215],{},[37,1972,1973],{"class":39,"line":171},[37,1974,76],{"emptyLinePlaceholder":75},[37,1976,1977],{"class":39,"line":177},[37,1978,1979],{},"    // \"chat-a3f9\" is the suffixed slug returned when you created the app\n",[37,1981,1982],{"class":39,"line":183},[37,1983,1984],{},"    room := client.SetApp(\"chat-a3f9\").SetRoom(\"general\")\n",[37,1986,1987],{"class":39,"line":188},[37,1988,1989],{},"    handler := func(data any, meta nolag.MessageMeta) {}\n",[37,1991,1992],{"class":39,"line":194},[37,1993,76],{"emptyLinePlaceholder":75},[37,1995,1996],{"class":39,"line":200},[37,1997,1998],{},"    // All operations return errors\n",[37,2000,2001],{"class":39,"line":206},[37,2002,2003],{},"    if err := room.Subscribe(\"messages\", handler); err != nil {\n",[37,2005,2006],{"class":39,"line":212},[37,2007,2008],{},"        // Check for specific error types\n",[37,2010,2011],{"class":39,"line":218},[37,2012,2013],{},"        if errors.Is(err, nolag.ErrNotConnected) {\n",[37,2015,2016],{"class":39,"line":223},[37,2017,2018],{},"            fmt.Println(\"Not connected!\")\n",[37,2020,2021],{"class":39,"line":229},[37,2022,2023],{},"        }\n",[37,2025,2026],{"class":39,"line":235},[37,2027,2028],{},"        fmt.Printf(\"Subscribe failed: %v\\n\", err)\n",[37,2030,2031],{"class":39,"line":240},[37,2032,209],{},[37,2034,2035],{"class":39,"line":246},[37,2036,76],{"emptyLinePlaceholder":75},[37,2038,2039],{"class":39,"line":252},[37,2040,2041],{},"    if err := room.Emit(\"messages\", \"data\"); err != nil {\n",[37,2043,2044],{"class":39,"line":258},[37,2045,299],{},[37,2047,2048],{"class":39,"line":263},[37,2049,209],{},[37,2051,2052],{"class":39,"line":269},[37,2053,76],{"emptyLinePlaceholder":75},[37,2055,2056],{"class":39,"line":274},[37,2057,2058],{},"    if err := room.Unsubscribe(\"messages\"); err != nil {\n",[37,2060,2061],{"class":39,"line":279},[37,2062,2063],{},"        fmt.Printf(\"Unsubscribe failed: %v\\n\", err)\n",[37,2065,2066],{"class":39,"line":284},[37,2067,209],{},[37,2069,2070],{"class":39,"line":290},[37,2071,76],{"emptyLinePlaceholder":75},[37,2073,2074],{"class":39,"line":296},[37,2075,2076],{},"    if err := room.SetFilters(\"messages\", []any{\"filter1\"}); err != nil {\n",[37,2078,2079],{"class":39,"line":302},[37,2080,2081],{},"        fmt.Printf(\"SetFilters failed: %v\\n\", err)\n",[37,2083,2084],{"class":39,"line":307},[37,2085,209],{},[37,2087,2088],{"class":39,"line":312},[37,2089,344],{},[37,2091,2092],{"class":39,"line":318},[37,2093,76],{"emptyLinePlaceholder":75},[37,2095,2096],{"class":39,"line":324},[37,2097,2098],{},"// Sentinel errors available:\n",[37,2100,2101],{"class":39,"line":329},[37,2102,2103],{},"// nolag.ErrNotConnected - operation attempted while disconnected\n",[37,2105,2106],{"class":39,"line":335},[37,2107,2108],{},"// nolag.ErrAuthFailed   - authentication failed\n",[37,2110,2111],{"class":39,"line":341},[37,2112,2113],{},"// nolag.ErrTimeout      - operation timed out\n",[18,2115,407],{"id":2116},"qos",[14,2118,2119,2120,393],{},"QoS is a broker-hop setting, not an end-to-end guarantee. The level (0, 1 or 2)\nis validated and passed to the broker's internal MQTT hop. The WebSocket leg has\nan optional publish ack and no resend, so no level is an end-to-end delivery\nguarantee; deduplicate with an idempotency key if a message must not be applied\ntwice. See ",[373,2121,407],{"href":2122},"/docs/concepts/qos",[1759,2124,2125,2137],{},[1762,2126,2127],{},[1765,2128,2129,2132,2135],{},[1768,2130,2131],{},"Constant",[1768,2133,2134],{},"Level",[1768,2136,1776],{},[1778,2138,2139,2152,2164],{},[1765,2140,2141,2146,2149],{},[1783,2142,2143],{},[34,2144,2145],{},"QoSAtMostOnce",[1783,2147,2148],{},"0",[1783,2150,2151],{},"Fire and forget on the broker hop",[1765,2153,2154,2159,2161],{},[1783,2155,2156],{},[34,2157,2158],{},"QoSAtLeastOnce",[1783,2160,1879],{},[1783,2162,2163],{},"Acknowledged on the broker hop (default)",[1765,2165,2166,2171,2174],{},[1783,2167,2168],{},[34,2169,2170],{},"QoSExactlyOnce",[1783,2172,2173],{},"2",[1783,2175,2176],{},"Deduplicated on the broker hop",[18,2178,2180],{"id":2179},"rest-api-client","REST API Client",[14,2182,2183],{},"The SDK also includes a REST API client for managing apps, rooms, actors, and scopes:",[23,2185,2186],{},[26,2187,2189],{"className":60,"code":2188,"filename":62,"language":44,"meta":32,"style":32},"package main\n\nimport (\n    \"context\"\n    \"fmt\"\n\n    nolag \"github.com/NoLagApp/go-sdk\"\n)\n\nfunc main() {\n    ctx := context.Background()\n\n    // Create API client with project-scoped API key\n    api := nolag.NewAPI(\"nlg_live_xxx.secret\")\n\n    // List apps: Data plus Pagination{Total, Page, PageCount}\n    apps, err := api.Apps.List(ctx, nil)\n    if err != nil {\n        panic(err)\n    }\n    fmt.Printf(\"%d of %d apps (page %d of %d)\\n\",\n        len(apps.Data), apps.Pagination.Total, apps.Pagination.Page, apps.Pagination.PageCount)\n\n    // Create a new app. Without Topics every subscribe is unknown_topic\n    app, err := api.Apps.Create(ctx, nolag.AppCreate{\n        Name:        \"my-chat-app\",\n        Description: \"A real-time chat application\",\n        Topics:      []string{\"messages\"},\n    })\n    if err != nil {\n        panic(err)\n    }\n    fmt.Printf(\"Created app: %s (slug %s)\\n\", app.AppID, app.Slug) // pass app.Slug to SetApp()\n\n    // Create a room in the app (rooms must exist before clients subscribe)\n    room, err := api.Rooms.Create(ctx, app.AppID, nolag.RoomCreate{\n        Name: \"general\",\n        Slug: \"general\",\n    })\n    if err != nil {\n        panic(err)\n    }\n    fmt.Printf(\"Created room: %s\\n\", room.RoomID)\n\n    // Create an actor (save the access token!)\n    actor, err := api.Actors.Create(ctx, nolag.ActorCreate{\n        Name:      \"web-client\",\n        ActorType: nolag.ActorDevice, // ActorDevice, ActorUser, ActorService, ActorSession, ActorAgent, ActorOrchestrator, ActorObserver\n    })\n    if err != nil {\n        panic(err)\n    }\n    fmt.Printf(\"Actor token: %s\\n\", actor.AccessToken)\n}\n",[34,2190,2191,2195,2199,2203,2208,2212,2216,2220,2224,2228,2232,2237,2241,2246,2251,2255,2260,2265,2269,2273,2277,2282,2287,2291,2296,2301,2306,2311,2316,2320,2324,2328,2332,2337,2341,2346,2351,2356,2361,2365,2369,2373,2377,2382,2386,2391,2396,2401,2406,2410,2415,2420,2425,2431],{"__ignoreMap":32},[37,2192,2193],{"class":39,"line":40},[37,2194,69],{},[37,2196,2197],{"class":39,"line":72},[37,2198,76],{"emptyLinePlaceholder":75},[37,2200,2201],{"class":39,"line":79},[37,2202,82],{},[37,2204,2205],{"class":39,"line":85},[37,2206,2207],{},"    \"context\"\n",[37,2209,2210],{"class":39,"line":91},[37,2211,88],{},[37,2213,2214],{"class":39,"line":97},[37,2215,76],{"emptyLinePlaceholder":75},[37,2217,2218],{"class":39,"line":102},[37,2219,105],{},[37,2221,2222],{"class":39,"line":108},[37,2223,111],{},[37,2225,2226],{"class":39,"line":114},[37,2227,76],{"emptyLinePlaceholder":75},[37,2229,2230],{"class":39,"line":119},[37,2231,139],{},[37,2233,2234],{"class":39,"line":125},[37,2235,2236],{},"    ctx := context.Background()\n",[37,2238,2239],{"class":39,"line":131},[37,2240,76],{"emptyLinePlaceholder":75},[37,2242,2243],{"class":39,"line":136},[37,2244,2245],{},"    // Create API client with project-scoped API key\n",[37,2247,2248],{"class":39,"line":142},[37,2249,2250],{},"    api := nolag.NewAPI(\"nlg_live_xxx.secret\")\n",[37,2252,2253],{"class":39,"line":148},[37,2254,76],{"emptyLinePlaceholder":75},[37,2256,2257],{"class":39,"line":154},[37,2258,2259],{},"    // List apps: Data plus Pagination{Total, Page, PageCount}\n",[37,2261,2262],{"class":39,"line":159},[37,2263,2264],{},"    apps, err := api.Apps.List(ctx, nil)\n",[37,2266,2267],{"class":39,"line":165},[37,2268,266],{},[37,2270,2271],{"class":39,"line":171},[37,2272,203],{},[37,2274,2275],{"class":39,"line":177},[37,2276,209],{},[37,2278,2279],{"class":39,"line":183},[37,2280,2281],{},"    fmt.Printf(\"%d of %d apps (page %d of %d)\\n\",\n",[37,2283,2284],{"class":39,"line":188},[37,2285,2286],{},"        len(apps.Data), apps.Pagination.Total, apps.Pagination.Page, apps.Pagination.PageCount)\n",[37,2288,2289],{"class":39,"line":194},[37,2290,76],{"emptyLinePlaceholder":75},[37,2292,2293],{"class":39,"line":200},[37,2294,2295],{},"    // Create a new app. Without Topics every subscribe is unknown_topic\n",[37,2297,2298],{"class":39,"line":206},[37,2299,2300],{},"    app, err := api.Apps.Create(ctx, nolag.AppCreate{\n",[37,2302,2303],{"class":39,"line":212},[37,2304,2305],{},"        Name:        \"my-chat-app\",\n",[37,2307,2308],{"class":39,"line":218},[37,2309,2310],{},"        Description: \"A real-time chat application\",\n",[37,2312,2313],{"class":39,"line":223},[37,2314,2315],{},"        Topics:      []string{\"messages\"},\n",[37,2317,2318],{"class":39,"line":229},[37,2319,180],{},[37,2321,2322],{"class":39,"line":235},[37,2323,266],{},[37,2325,2326],{"class":39,"line":240},[37,2327,203],{},[37,2329,2330],{"class":39,"line":246},[37,2331,209],{},[37,2333,2334],{"class":39,"line":252},[37,2335,2336],{},"    fmt.Printf(\"Created app: %s (slug %s)\\n\", app.AppID, app.Slug) // pass app.Slug to SetApp()\n",[37,2338,2339],{"class":39,"line":258},[37,2340,76],{"emptyLinePlaceholder":75},[37,2342,2343],{"class":39,"line":263},[37,2344,2345],{},"    // Create a room in the app (rooms must exist before clients subscribe)\n",[37,2347,2348],{"class":39,"line":269},[37,2349,2350],{},"    room, err := api.Rooms.Create(ctx, app.AppID, nolag.RoomCreate{\n",[37,2352,2353],{"class":39,"line":274},[37,2354,2355],{},"        Name: \"general\",\n",[37,2357,2358],{"class":39,"line":279},[37,2359,2360],{},"        Slug: \"general\",\n",[37,2362,2363],{"class":39,"line":284},[37,2364,180],{},[37,2366,2367],{"class":39,"line":290},[37,2368,266],{},[37,2370,2371],{"class":39,"line":296},[37,2372,203],{},[37,2374,2375],{"class":39,"line":302},[37,2376,209],{},[37,2378,2379],{"class":39,"line":307},[37,2380,2381],{},"    fmt.Printf(\"Created room: %s\\n\", room.RoomID)\n",[37,2383,2384],{"class":39,"line":312},[37,2385,76],{"emptyLinePlaceholder":75},[37,2387,2388],{"class":39,"line":318},[37,2389,2390],{},"    // Create an actor (save the access token!)\n",[37,2392,2393],{"class":39,"line":324},[37,2394,2395],{},"    actor, err := api.Actors.Create(ctx, nolag.ActorCreate{\n",[37,2397,2398],{"class":39,"line":329},[37,2399,2400],{},"        Name:      \"web-client\",\n",[37,2402,2403],{"class":39,"line":335},[37,2404,2405],{},"        ActorType: nolag.ActorDevice, // ActorDevice, ActorUser, ActorService, ActorSession, ActorAgent, ActorOrchestrator, ActorObserver\n",[37,2407,2408],{"class":39,"line":341},[37,2409,180],{},[37,2411,2413],{"class":39,"line":2412},50,[37,2414,266],{},[37,2416,2418],{"class":39,"line":2417},51,[37,2419,203],{},[37,2421,2423],{"class":39,"line":2422},52,[37,2424,209],{},[37,2426,2428],{"class":39,"line":2427},53,[37,2429,2430],{},"    fmt.Printf(\"Actor token: %s\\n\", actor.AccessToken)\n",[37,2432,2434],{"class":39,"line":2433},54,[37,2435,344],{},[18,2437,2439],{"id":2438},"access-scopes","Access Scopes",[14,2441,2442],{},"Manage access scopes for tenant isolation:",[23,2444,2445],{},[26,2446,2448],{"className":60,"code":2447,"filename":62,"language":44,"meta":32,"style":32},"api := nolag.NewAPI(\"nlg_live_xxx.secret\")\nctx := context.Background()\n\n// List scopes: paginated like apps\nscopes, err := api.Scopes.List(ctx)\nif err != nil {\n    panic(err)\n}\nfmt.Printf(\"Found %d of %d scopes\\n\", len(scopes.Data), scopes.Pagination.Total)\n\n// Create a scope\nscope, err := api.Scopes.Create(ctx, nolag.ScopeCreate{\n    Slug: \"tenant-acme\",\n    Name: \"Acme Corp\",\n})\nif err != nil {\n    panic(err)\n}\nfmt.Printf(\"Created scope: %s\\n\", scope.AccessScopeID)\n\n// Assign an actor to the scope\nscopeID := scope.AccessScopeID\n_, err = api.Actors.Update(ctx, actorID, nolag.ActorUpdate{\n    AccessScopeID: &scopeID,\n})\n\n// List actors in a scope (plain slice)\nactors, err := api.Scopes.ListActors(ctx, scope.AccessScopeID)\n\n// Update a scope\nnewName := \"Acme Corporation\"\n_, err = api.Scopes.Update(ctx, scope.AccessScopeID, nolag.ScopeUpdate{\n    Name: &newName,\n})\n\n// Delete a scope (409 while actors are still assigned)\nerr = api.Scopes.Delete(ctx, scope.AccessScopeID)\n",[34,2449,2450,2455,2460,2464,2469,2474,2478,2483,2487,2492,2496,2501,2506,2511,2516,2520,2524,2528,2532,2537,2541,2546,2551,2556,2561,2565,2569,2574,2579,2583,2588,2593,2598,2603,2607,2611,2616],{"__ignoreMap":32},[37,2451,2452],{"class":39,"line":40},[37,2453,2454],{},"api := nolag.NewAPI(\"nlg_live_xxx.secret\")\n",[37,2456,2457],{"class":39,"line":72},[37,2458,2459],{},"ctx := context.Background()\n",[37,2461,2462],{"class":39,"line":79},[37,2463,76],{"emptyLinePlaceholder":75},[37,2465,2466],{"class":39,"line":85},[37,2467,2468],{},"// List scopes: paginated like apps\n",[37,2470,2471],{"class":39,"line":91},[37,2472,2473],{},"scopes, err := api.Scopes.List(ctx)\n",[37,2475,2476],{"class":39,"line":97},[37,2477,568],{},[37,2479,2480],{"class":39,"line":102},[37,2481,2482],{},"    panic(err)\n",[37,2484,2485],{"class":39,"line":108},[37,2486,344],{},[37,2488,2489],{"class":39,"line":114},[37,2490,2491],{},"fmt.Printf(\"Found %d of %d scopes\\n\", len(scopes.Data), scopes.Pagination.Total)\n",[37,2493,2494],{"class":39,"line":119},[37,2495,76],{"emptyLinePlaceholder":75},[37,2497,2498],{"class":39,"line":125},[37,2499,2500],{},"// Create a scope\n",[37,2502,2503],{"class":39,"line":131},[37,2504,2505],{},"scope, err := api.Scopes.Create(ctx, nolag.ScopeCreate{\n",[37,2507,2508],{"class":39,"line":136},[37,2509,2510],{},"    Slug: \"tenant-acme\",\n",[37,2512,2513],{"class":39,"line":142},[37,2514,2515],{},"    Name: \"Acme Corp\",\n",[37,2517,2518],{"class":39,"line":148},[37,2519,616],{},[37,2521,2522],{"class":39,"line":154},[37,2523,568],{},[37,2525,2526],{"class":39,"line":159},[37,2527,2482],{},[37,2529,2530],{"class":39,"line":165},[37,2531,344],{},[37,2533,2534],{"class":39,"line":171},[37,2535,2536],{},"fmt.Printf(\"Created scope: %s\\n\", scope.AccessScopeID)\n",[37,2538,2539],{"class":39,"line":177},[37,2540,76],{"emptyLinePlaceholder":75},[37,2542,2543],{"class":39,"line":183},[37,2544,2545],{},"// Assign an actor to the scope\n",[37,2547,2548],{"class":39,"line":188},[37,2549,2550],{},"scopeID := scope.AccessScopeID\n",[37,2552,2553],{"class":39,"line":194},[37,2554,2555],{},"_, err = api.Actors.Update(ctx, actorID, nolag.ActorUpdate{\n",[37,2557,2558],{"class":39,"line":200},[37,2559,2560],{},"    AccessScopeID: &scopeID,\n",[37,2562,2563],{"class":39,"line":206},[37,2564,616],{},[37,2566,2567],{"class":39,"line":212},[37,2568,76],{"emptyLinePlaceholder":75},[37,2570,2571],{"class":39,"line":218},[37,2572,2573],{},"// List actors in a scope (plain slice)\n",[37,2575,2576],{"class":39,"line":223},[37,2577,2578],{},"actors, err := api.Scopes.ListActors(ctx, scope.AccessScopeID)\n",[37,2580,2581],{"class":39,"line":229},[37,2582,76],{"emptyLinePlaceholder":75},[37,2584,2585],{"class":39,"line":235},[37,2586,2587],{},"// Update a scope\n",[37,2589,2590],{"class":39,"line":240},[37,2591,2592],{},"newName := \"Acme Corporation\"\n",[37,2594,2595],{"class":39,"line":246},[37,2596,2597],{},"_, err = api.Scopes.Update(ctx, scope.AccessScopeID, nolag.ScopeUpdate{\n",[37,2599,2600],{"class":39,"line":252},[37,2601,2602],{},"    Name: &newName,\n",[37,2604,2605],{"class":39,"line":258},[37,2606,616],{},[37,2608,2609],{"class":39,"line":263},[37,2610,76],{"emptyLinePlaceholder":75},[37,2612,2613],{"class":39,"line":269},[37,2614,2615],{},"// Delete a scope (409 while actors are still assigned)\n",[37,2617,2618],{"class":39,"line":274},[37,2619,2620],{},"err = api.Scopes.Delete(ctx, scope.AccessScopeID)\n",[18,2622,2624],{"id":2623},"load-balancing","Load Balancing",[14,2626,2627,2628,2631],{},"Load balancing is opt-in. Without it every subscriber receives every message.\nWith it, subscribers that share a ",[34,2629,2630],{},"LoadBalanceGroup"," form a group and each\nmessage is delivered to one member of the group:",[23,2633,2634],{},[26,2635,2637],{"className":60,"code":2636,"filename":62,"language":44,"meta":32,"style":32},"processTask := func(data any, meta nolag.MessageMeta) {\n    fmt.Printf(\"Processing: %v\\n\", data)\n}\n\n// Enable load balancing per-subscription\nclient := nolag.New(\"your-actor-token\")\nroom := client.SetApp(appSlug).SetRoom(\"image-processing\")\nerr := room.Subscribe(\"tasks\", processTask, nolag.SubscribeOptions{\n    LoadBalance:      nolag.Bool(true),\n    LoadBalanceGroup: \"task-workers\",\n})\nif err != nil {\n    fmt.Printf(\"Subscribe failed: %v\\n\", err)\n}\n\n// Or enable load balancing globally via connection options.\n// Fields you omit (Reconnect, QoS, intervals) keep their defaults\nworker := nolag.New(\"your-actor-token\", nolag.Options{\n    LoadBalance:      true,\n    LoadBalanceGroup: \"task-workers\",\n})\n",[34,2638,2639,2644,2649,2653,2657,2662,2666,2671,2676,2680,2685,2689,2693,2697,2701,2705,2710,2715,2720,2725,2729],{"__ignoreMap":32},[37,2640,2641],{"class":39,"line":40},[37,2642,2643],{},"processTask := func(data any, meta nolag.MessageMeta) {\n",[37,2645,2646],{"class":39,"line":72},[37,2647,2648],{},"    fmt.Printf(\"Processing: %v\\n\", data)\n",[37,2650,2651],{"class":39,"line":79},[37,2652,344],{},[37,2654,2655],{"class":39,"line":85},[37,2656,76],{"emptyLinePlaceholder":75},[37,2658,2659],{"class":39,"line":91},[37,2660,2661],{},"// Enable load balancing per-subscription\n",[37,2663,2664],{"class":39,"line":97},[37,2665,530],{},[37,2667,2668],{"class":39,"line":102},[37,2669,2670],{},"room := client.SetApp(appSlug).SetRoom(\"image-processing\")\n",[37,2672,2673],{"class":39,"line":108},[37,2674,2675],{},"err := room.Subscribe(\"tasks\", processTask, nolag.SubscribeOptions{\n",[37,2677,2678],{"class":39,"line":114},[37,2679,601],{},[37,2681,2682],{"class":39,"line":119},[37,2683,2684],{},"    LoadBalanceGroup: \"task-workers\",\n",[37,2686,2687],{"class":39,"line":125},[37,2688,616],{},[37,2690,2691],{"class":39,"line":131},[37,2692,568],{},[37,2694,2695],{"class":39,"line":136},[37,2696,573],{},[37,2698,2699],{"class":39,"line":142},[37,2700,344],{},[37,2702,2703],{"class":39,"line":148},[37,2704,76],{"emptyLinePlaceholder":75},[37,2706,2707],{"class":39,"line":154},[37,2708,2709],{},"// Or enable load balancing globally via connection options.\n",[37,2711,2712],{"class":39,"line":159},[37,2713,2714],{},"// Fields you omit (Reconnect, QoS, intervals) keep their defaults\n",[37,2716,2717],{"class":39,"line":165},[37,2718,2719],{},"worker := nolag.New(\"your-actor-token\", nolag.Options{\n",[37,2721,2722],{"class":39,"line":171},[37,2723,2724],{},"    LoadBalance:      true,\n",[37,2726,2727],{"class":39,"line":177},[37,2728,2684],{},[37,2730,2731],{"class":39,"line":183},[37,2732,616],{},[14,2734,2735,2736,503,2739,2742,2743,393],{},"Workers whose actor type holds a persistent session (",[34,2737,2738],{},"agent",[34,2740,2741],{},"orchestrator",")\nalso have messages queued while they are away; see ",[373,2744,2746],{"href":2745},"/docs/concepts/replay","Replay",[18,2748,2750],{"id":2749},"type-definitions","Type Definitions",[23,2752,2753],{},[26,2754,2756],{"className":60,"code":2755,"filename":62,"language":44,"meta":32,"style":32},"import nolag \"github.com/NoLagApp/go-sdk\"\n\n// WebSocket Client\nnolag.Client           // The real-time messaging client\nnolag.Options          // Connection options (URL, Reconnect, QoS, LoadBalance, etc.)\nnolag.SubscribeOptions // Subscription options (QoS, LoadBalance, Filters, etc.)\nnolag.EmitOptions      // Publish options (QoS, Retain, Echo, Filter)\nnolag.App              // Intermediate context from SetApp()\nnolag.Room             // Scoped pub/sub context from SetApp().SetRoom()\nnolag.Bool             // func(bool) *bool, for pointer-typed option fields\nnolag.QoSLevel         // func(QoS) *QoS, for pointer-typed QoS fields\nnolag.ProtocolVersion  // Wire protocol version this SDK requests (2)\n\n// Enums / Constants\nnolag.ConnectionStatus // StatusDisconnected, StatusConnecting, StatusConnected, StatusReconnecting\nnolag.ActorType        // ActorDevice, ActorUser, ActorService, ActorSession,\n                       // ActorAgent, ActorOrchestrator, ActorObserver\nnolag.QoS              // QoSAtMostOnce, QoSAtLeastOnce, QoSExactlyOnce (broker-hop levels)\n\n// Errors\nnolag.ServerError      // Structured broker error (Code, Name, Topic, Hint, MsgRef)\nnolag.ErrorHandler     // func(err *ServerError)\nnolag.ErrNotConnected  // Not connected to broker\nnolag.ErrAuthFailed    // Authentication failed\nnolag.ErrTimeout       // Operation timed out\n\n// Data types\nnolag.MessageMeta      // Message metadata (Sender, Timestamp, IsReplay, MsgID, Filter)\nnolag.ActorPresence    // Presence info (ActorTokenID, Presence, Status populated)\nnolag.MessageHandler   // func(data any, meta MessageMeta)\nnolag.EventHandler     // func(args ...any)\n\n// REST API Client\nnolag.API              // REST API client\nnolag.APIOptions       // API client options\nnolag.NoLagAPIError    // API error type\nnolag.APIError         // Raw API error details\n\n// Resources\nnolag.AppResource, nolag.AppCreate, nolag.AppUpdate\nnolag.RoomResource, nolag.RoomCreate, nolag.RoomUpdate\nnolag.ActorResource, nolag.ActorWithToken, nolag.ActorCreate, nolag.ActorUpdate\nnolag.ScopeResource, nolag.ScopeCreate, nolag.ScopeUpdate\nnolag.Pagination, nolag.PaginatedApps, nolag.PaginatedScopes, nolag.ListOptions\n",[34,2757,2758,2763,2767,2772,2777,2782,2787,2792,2797,2802,2807,2812,2817,2821,2826,2831,2836,2841,2846,2850,2855,2860,2865,2870,2875,2880,2884,2889,2894,2899,2904,2909,2913,2918,2923,2928,2933,2938,2942,2947,2952,2957,2962,2967],{"__ignoreMap":32},[37,2759,2760],{"class":39,"line":40},[37,2761,2762],{},"import nolag \"github.com/NoLagApp/go-sdk\"\n",[37,2764,2765],{"class":39,"line":72},[37,2766,76],{"emptyLinePlaceholder":75},[37,2768,2769],{"class":39,"line":79},[37,2770,2771],{},"// WebSocket Client\n",[37,2773,2774],{"class":39,"line":85},[37,2775,2776],{},"nolag.Client           // The real-time messaging client\n",[37,2778,2779],{"class":39,"line":91},[37,2780,2781],{},"nolag.Options          // Connection options (URL, Reconnect, QoS, LoadBalance, etc.)\n",[37,2783,2784],{"class":39,"line":97},[37,2785,2786],{},"nolag.SubscribeOptions // Subscription options (QoS, LoadBalance, Filters, etc.)\n",[37,2788,2789],{"class":39,"line":102},[37,2790,2791],{},"nolag.EmitOptions      // Publish options (QoS, Retain, Echo, Filter)\n",[37,2793,2794],{"class":39,"line":108},[37,2795,2796],{},"nolag.App              // Intermediate context from SetApp()\n",[37,2798,2799],{"class":39,"line":114},[37,2800,2801],{},"nolag.Room             // Scoped pub/sub context from SetApp().SetRoom()\n",[37,2803,2804],{"class":39,"line":119},[37,2805,2806],{},"nolag.Bool             // func(bool) *bool, for pointer-typed option fields\n",[37,2808,2809],{"class":39,"line":125},[37,2810,2811],{},"nolag.QoSLevel         // func(QoS) *QoS, for pointer-typed QoS fields\n",[37,2813,2814],{"class":39,"line":131},[37,2815,2816],{},"nolag.ProtocolVersion  // Wire protocol version this SDK requests (2)\n",[37,2818,2819],{"class":39,"line":136},[37,2820,76],{"emptyLinePlaceholder":75},[37,2822,2823],{"class":39,"line":142},[37,2824,2825],{},"// Enums / Constants\n",[37,2827,2828],{"class":39,"line":148},[37,2829,2830],{},"nolag.ConnectionStatus // StatusDisconnected, StatusConnecting, StatusConnected, StatusReconnecting\n",[37,2832,2833],{"class":39,"line":154},[37,2834,2835],{},"nolag.ActorType        // ActorDevice, ActorUser, ActorService, ActorSession,\n",[37,2837,2838],{"class":39,"line":159},[37,2839,2840],{},"                       // ActorAgent, ActorOrchestrator, ActorObserver\n",[37,2842,2843],{"class":39,"line":165},[37,2844,2845],{},"nolag.QoS              // QoSAtMostOnce, QoSAtLeastOnce, QoSExactlyOnce (broker-hop levels)\n",[37,2847,2848],{"class":39,"line":171},[37,2849,76],{"emptyLinePlaceholder":75},[37,2851,2852],{"class":39,"line":177},[37,2853,2854],{},"// Errors\n",[37,2856,2857],{"class":39,"line":183},[37,2858,2859],{},"nolag.ServerError      // Structured broker error (Code, Name, Topic, Hint, MsgRef)\n",[37,2861,2862],{"class":39,"line":188},[37,2863,2864],{},"nolag.ErrorHandler     // func(err *ServerError)\n",[37,2866,2867],{"class":39,"line":194},[37,2868,2869],{},"nolag.ErrNotConnected  // Not connected to broker\n",[37,2871,2872],{"class":39,"line":200},[37,2873,2874],{},"nolag.ErrAuthFailed    // Authentication failed\n",[37,2876,2877],{"class":39,"line":206},[37,2878,2879],{},"nolag.ErrTimeout       // Operation timed out\n",[37,2881,2882],{"class":39,"line":212},[37,2883,76],{"emptyLinePlaceholder":75},[37,2885,2886],{"class":39,"line":218},[37,2887,2888],{},"// Data types\n",[37,2890,2891],{"class":39,"line":223},[37,2892,2893],{},"nolag.MessageMeta      // Message metadata (Sender, Timestamp, IsReplay, MsgID, Filter)\n",[37,2895,2896],{"class":39,"line":229},[37,2897,2898],{},"nolag.ActorPresence    // Presence info (ActorTokenID, Presence, Status populated)\n",[37,2900,2901],{"class":39,"line":235},[37,2902,2903],{},"nolag.MessageHandler   // func(data any, meta MessageMeta)\n",[37,2905,2906],{"class":39,"line":240},[37,2907,2908],{},"nolag.EventHandler     // func(args ...any)\n",[37,2910,2911],{"class":39,"line":246},[37,2912,76],{"emptyLinePlaceholder":75},[37,2914,2915],{"class":39,"line":252},[37,2916,2917],{},"// REST API Client\n",[37,2919,2920],{"class":39,"line":258},[37,2921,2922],{},"nolag.API              // REST API client\n",[37,2924,2925],{"class":39,"line":263},[37,2926,2927],{},"nolag.APIOptions       // API client options\n",[37,2929,2930],{"class":39,"line":269},[37,2931,2932],{},"nolag.NoLagAPIError    // API error type\n",[37,2934,2935],{"class":39,"line":274},[37,2936,2937],{},"nolag.APIError         // Raw API error details\n",[37,2939,2940],{"class":39,"line":279},[37,2941,76],{"emptyLinePlaceholder":75},[37,2943,2944],{"class":39,"line":284},[37,2945,2946],{},"// Resources\n",[37,2948,2949],{"class":39,"line":290},[37,2950,2951],{},"nolag.AppResource, nolag.AppCreate, nolag.AppUpdate\n",[37,2953,2954],{"class":39,"line":296},[37,2955,2956],{},"nolag.RoomResource, nolag.RoomCreate, nolag.RoomUpdate\n",[37,2958,2959],{"class":39,"line":302},[37,2960,2961],{},"nolag.ActorResource, nolag.ActorWithToken, nolag.ActorCreate, nolag.ActorUpdate\n",[37,2963,2964],{"class":39,"line":307},[37,2965,2966],{},"nolag.ScopeResource, nolag.ScopeCreate, nolag.ScopeUpdate\n",[37,2968,2969],{"class":39,"line":312},[37,2970,2971],{},"nolag.Pagination, nolag.PaginatedApps, nolag.PaginatedScopes, nolag.ListOptions\n",[18,2973,2975],{"id":2974},"requirements","Requirements",[1621,2977,2978,2981,2984,2987],{},[1624,2979,2980],{},"Go 1.21+",[1624,2982,2983],{},"github.com/gorilla/websocket v1.5.1",[1624,2985,2986],{},"github.com/vmihailenco/msgpack/v5 v5.4.1",[1624,2988,2989],{},"github.com/pion/webrtc/v3 v3.2.50",[18,2991,2993],{"id":2992},"next-steps","Next Steps",[1621,2995,2996,3002,3008,3014,3018,3022],{},[1624,2997,2998],{},[373,2999,3001],{"href":3000},"/docs/concepts/topics","Learn about Topics",[1624,3003,3004],{},[373,3005,3007],{"href":3006},"/docs/concepts/rooms","Rooms",[1624,3009,3010],{},[373,3011,3013],{"href":3012},"/docs/concepts/presence","Presence Tracking",[1624,3015,3016],{},[373,3017,1161],{"href":1160},[1624,3019,3020],{},[373,3021,2746],{"href":2745},[1624,3023,3024],{},[373,3025,3027],{"href":3026},"/docs/api-reference","REST API Reference",[3029,3030,3031],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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);}",{"title":32,"searchDepth":72,"depth":72,"links":3033},[3034,3035,3036,3037,3038,3039,3040,3041,3042,3043,3047,3048,3049,3050,3051,3052,3053],{"id":20,"depth":72,"text":21},{"id":54,"depth":72,"text":55},{"id":396,"depth":72,"text":397},{"id":496,"depth":72,"text":497},{"id":668,"depth":72,"text":669},{"id":803,"depth":72,"text":804},{"id":986,"depth":72,"text":987},{"id":1164,"depth":72,"text":1165},{"id":1396,"depth":72,"text":1397},{"id":1616,"depth":72,"text":518,"children":3044},[3045,3046],{"id":1661,"depth":79,"text":1662},{"id":1886,"depth":79,"text":1887},{"id":2116,"depth":72,"text":407},{"id":2179,"depth":72,"text":2180},{"id":2438,"depth":72,"text":2439},{"id":2623,"depth":72,"text":2624},{"id":2749,"depth":72,"text":2750},{"id":2974,"depth":72,"text":2975},{"id":2992,"depth":72,"text":2993},"Complete reference for the NoLag Go SDK. Connect, subscribe, publish, and manage real-time messaging with idiomatic Go patterns.","md",{},"/docs/sdks/go",{"title":5,"description":3054},"docs/sdks/go","NfrfUMRZuZ_PFjYobE8g68rFuMow3-f4LhNtnZr3yIE",1789869421299]