[{"data":1,"prerenderedAt":1395},["ShallowReactive",2],{"docs:/docs/concepts/acl":3},{"id":4,"title":5,"body":6,"description":1388,"extension":1389,"meta":1390,"navigation":261,"path":1391,"seo":1392,"stem":1393,"__hash__":1394},"docs/docs/concepts/acl.md","Access Control",{"type":7,"value":8,"toc":1376},"minimark",[9,13,17,22,25,29,32,83,87,96,107,121,143,147,150,156,162,374,399,405,828,842,1259,1263,1268,1271,1291,1295,1298,1305,1309,1312,1325,1354,1358,1372],[10,11,5],"h1",{"id":12},"access-control",[14,15,16],"p",{},"Control who can publish and subscribe to topics with fine-grained access control lists (ACLs).",[18,19,21],"h2",{"id":20},"acl-basics","ACL Basics",[14,23,24],{},"Each topic can have separate permissions for publishing and subscribing. This allows you to create read-only channels, write-only endpoints, or fully open topics.",[18,26,28],{"id":27},"permission-levels","Permission Levels",[14,30,31],{},"Each actor's access to a topic is defined by one of these permission levels:",[33,34,35,48],"table",{},[36,37,38],"thead",{},[39,40,41,45],"tr",{},[42,43,44],"th",{},"Permission",[42,46,47],{},"Description",[49,50,51,63,73],"tbody",{},[39,52,53,60],{},[54,55,56],"td",{},[57,58,59],"code",{},"subscribe",[54,61,62],{},"Can only subscribe to the topic (read-only)",[39,64,65,70],{},[54,66,67],{},[57,68,69],{},"publish",[54,71,72],{},"Can only publish to the topic (write-only)",[39,74,75,80],{},[54,76,77],{},[57,78,79],{},"pubSub",[54,81,82],{},"Can both publish and subscribe (full access)",[18,84,86],{"id":85},"the-default-is-open","The Default Is Open",[14,88,89,90,95],{},"Access resolves in two layers, and both start permissive. This is why the\n",[91,92,94],"a",{"href":93},"/docs/getting-started","quickstart"," connects successfully without any\naccess-control step.",[14,97,98,102,103,106],{},[99,100,101],"strong",{},"App level."," A new app is created in ",[57,104,105],{},"open"," mode, which gives every actor in the\nproject publish and subscribe rights on all of that app's topics.",[14,108,109,112,113,116,117,120],{},[99,110,111],{},"Room level."," A room with no grants inherits the app-level permission. Creating the\nfirst grant on a room flips it to private: from then on the broker admits only\nactors holding an explicit, unexpired grant. A grant naming a specific\n",[57,114,115],{},"actorTokenId"," takes precedence over a broader ",[57,118,119],{},"actorType"," grant.",[122,123,125,128],"callout",{"type":124},"warning",[14,126,127],{},"Two consequences worth planning for:",[129,130,131,140],"ul",{},[132,133,134,135,139],"li",{},"Until you add grants, any actor token in the project can read and write every\ntopic in the app. The project is your outermost boundary, so keep unrelated\nworkloads in separate projects and see ",[91,136,138],{"href":137},"/docs/scopes","Access Scopes"," for\ntenant isolation within one.",[132,141,142],{},"Because the first grant is what closes a room, granting access to one actor\nremoves it from every other actor. Add grants for everything that needs the room,\nincluding your own backend services, in the same change.",[18,144,146],{"id":145},"configuring-acls","Configuring ACLs",[14,148,149],{},"There are three ways to manage grants, all writing the same room grant records.",[14,151,152,155],{},[99,153,154],{},"In the dashboard."," Open your app, select a room, and manage its actor grants there.",[14,157,158,161],{},[99,159,160],{},"Over the REST API."," Room grants are a first-class resource, so a setup script or\nan agent can provision access without opening the dashboard:",[163,164,170],"pre",{"className":165,"code":166,"filename":167,"language":168,"meta":169,"style":169},"language-bash shiki shiki-themes github-light github-dark","# Grant one actor pub/sub on two topics in a room\ncurl -X POST https://api.nolag.app/v1/apps/{appId}/rooms/{roomId}/actors \\\n  -H \"Authorization: Bearer nlg_live_xxx.secret\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n        \"actorTokenId\": \"01939f83-8b57-7c3e-a456-426614174000\",\n        \"permission\": \"pubSub\",\n        \"topics\": [\"messages\", \"typing\"]\n      }'\n\n# Or grant by actor type, covering every actor of that type\ncurl -X POST https://api.nolag.app/v1/apps/{appId}/rooms/{roomId}/actors \\\n  -H \"Authorization: Bearer nlg_live_xxx.secret\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"actorType\":\"agent\",\"permission\":\"subscribe\"}'\n\n# List the room's grants\ncurl https://api.nolag.app/v1/apps/{appId}/rooms/{roomId}/actors \\\n  -H \"Authorization: Bearer nlg_live_xxx.secret\"\n\n# Revoke a grant\ncurl -X DELETE \\\n  https://api.nolag.app/v1/apps/{appId}/rooms/{roomId}/actors/{roomActorAccessId} \\\n  -H \"Authorization: Bearer nlg_live_xxx.secret\"\n","Terminal","bash","",[57,171,172,181,202,213,223,232,238,244,250,256,263,269,282,291,300,308,313,319,328,336,341,347,359,367],{"__ignoreMap":169},[173,174,177],"span",{"class":175,"line":176},"line",1,[173,178,180],{"class":179},"sJ8bj","# Grant one actor pub/sub on two topics in a room\n",[173,182,184,188,192,196,199],{"class":175,"line":183},2,[173,185,187],{"class":186},"sScJk","curl",[173,189,191],{"class":190},"sj4cs"," -X",[173,193,195],{"class":194},"sZZnC"," POST",[173,197,198],{"class":194}," https://api.nolag.app/v1/apps/{appId}/rooms/{roomId}/actors",[173,200,201],{"class":190}," \\\n",[173,203,205,208,211],{"class":175,"line":204},3,[173,206,207],{"class":190},"  -H",[173,209,210],{"class":194}," \"Authorization: Bearer nlg_live_xxx.secret\"",[173,212,201],{"class":190},[173,214,216,218,221],{"class":175,"line":215},4,[173,217,207],{"class":190},[173,219,220],{"class":194}," \"Content-Type: application/json\"",[173,222,201],{"class":190},[173,224,226,229],{"class":175,"line":225},5,[173,227,228],{"class":190},"  -d",[173,230,231],{"class":194}," '{\n",[173,233,235],{"class":175,"line":234},6,[173,236,237],{"class":194},"        \"actorTokenId\": \"01939f83-8b57-7c3e-a456-426614174000\",\n",[173,239,241],{"class":175,"line":240},7,[173,242,243],{"class":194},"        \"permission\": \"pubSub\",\n",[173,245,247],{"class":175,"line":246},8,[173,248,249],{"class":194},"        \"topics\": [\"messages\", \"typing\"]\n",[173,251,253],{"class":175,"line":252},9,[173,254,255],{"class":194},"      }'\n",[173,257,259],{"class":175,"line":258},10,[173,260,262],{"emptyLinePlaceholder":261},true,"\n",[173,264,266],{"class":175,"line":265},11,[173,267,268],{"class":179},"# Or grant by actor type, covering every actor of that type\n",[173,270,272,274,276,278,280],{"class":175,"line":271},12,[173,273,187],{"class":186},[173,275,191],{"class":190},[173,277,195],{"class":194},[173,279,198],{"class":194},[173,281,201],{"class":190},[173,283,285,287,289],{"class":175,"line":284},13,[173,286,207],{"class":190},[173,288,210],{"class":194},[173,290,201],{"class":190},[173,292,294,296,298],{"class":175,"line":293},14,[173,295,207],{"class":190},[173,297,220],{"class":194},[173,299,201],{"class":190},[173,301,303,305],{"class":175,"line":302},15,[173,304,228],{"class":190},[173,306,307],{"class":194}," '{\"actorType\":\"agent\",\"permission\":\"subscribe\"}'\n",[173,309,311],{"class":175,"line":310},16,[173,312,262],{"emptyLinePlaceholder":261},[173,314,316],{"class":175,"line":315},17,[173,317,318],{"class":179},"# List the room's grants\n",[173,320,322,324,326],{"class":175,"line":321},18,[173,323,187],{"class":186},[173,325,198],{"class":194},[173,327,201],{"class":190},[173,329,331,333],{"class":175,"line":330},19,[173,332,207],{"class":190},[173,334,335],{"class":194}," \"Authorization: Bearer nlg_live_xxx.secret\"\n",[173,337,339],{"class":175,"line":338},20,[173,340,262],{"emptyLinePlaceholder":261},[173,342,344],{"class":175,"line":343},21,[173,345,346],{"class":179},"# Revoke a grant\n",[173,348,350,352,354,357],{"class":175,"line":349},22,[173,351,187],{"class":186},[173,353,191],{"class":190},[173,355,356],{"class":194}," DELETE",[173,358,201],{"class":190},[173,360,362,365],{"class":175,"line":361},23,[173,363,364],{"class":194},"  https://api.nolag.app/v1/apps/{appId}/rooms/{roomId}/actors/{roomActorAccessId}",[173,366,201],{"class":190},[173,368,370,372],{"class":175,"line":369},24,[173,371,207],{"class":190},[173,373,335],{"class":194},[14,375,376,377,379,380,382,383,386,387,390,391,394,395,398],{},"Supply either ",[57,378,115],{}," or ",[57,381,119],{},", plus a ",[57,384,385],{},"permission",". Optionally scope\nthe grant with ",[57,388,389],{},"topics",", time-limit it with ",[57,392,393],{},"expiresAt",", or disable it with\n",[57,396,397],{},"isActive: false",".",[14,400,401,404],{},[99,402,403],{},"From the SDK REST clients."," Each SDK ships a REST client that wraps the same\nendpoints, so a backend can create the room and its grants in one place:",[406,407,408,611,718],"code-tabs",{},[163,409,414],{"className":410,"code":411,"filename":412,"language":413,"meta":169,"style":169},"language-typescript shiki shiki-themes github-light github-dark","import { NoLagApi } from '@nolag/js-sdk'\n\nconst api = new NoLagApi(process.env.NOLAG_API_KEY) // nlg_live_...\n\n// Grant one actor pub/sub on two topics in a room\nconst grant = await api.rooms.grantActor(appId, roomId, {\n  actorTokenId: '01939f83-8b57-7c3e-a456-426614174000',\n  permission: 'pubSub',\n  topics: ['messages', 'typing']\n})\n\n// Grant by actor type, covering every actor of that type\nawait api.rooms.grantActor(appId, roomId, { actorType: 'agent', permission: 'subscribe' })\n\n// List and revoke\nconst grants = await api.rooms.listActors(appId, roomId)\nawait api.rooms.revokeActor(appId, roomId, grant.roomActorAccessId)\n","TypeScript","typescript",[57,415,416,432,436,465,469,474,495,506,516,533,538,542,547,571,575,580,599],{"__ignoreMap":169},[173,417,418,422,426,429],{"class":175,"line":176},[173,419,421],{"class":420},"szBVR","import",[173,423,425],{"class":424},"sVt8B"," { NoLagApi } ",[173,427,428],{"class":420},"from",[173,430,431],{"class":194}," '@nolag/js-sdk'\n",[173,433,434],{"class":175,"line":183},[173,435,262],{"emptyLinePlaceholder":261},[173,437,438,441,444,447,450,453,456,459,462],{"class":175,"line":204},[173,439,440],{"class":420},"const",[173,442,443],{"class":190}," api",[173,445,446],{"class":420}," =",[173,448,449],{"class":420}," new",[173,451,452],{"class":186}," NoLagApi",[173,454,455],{"class":424},"(process.env.",[173,457,458],{"class":190},"NOLAG_API_KEY",[173,460,461],{"class":424},") ",[173,463,464],{"class":179},"// nlg_live_...\n",[173,466,467],{"class":175,"line":215},[173,468,262],{"emptyLinePlaceholder":261},[173,470,471],{"class":175,"line":225},[173,472,473],{"class":179},"// Grant one actor pub/sub on two topics in a room\n",[173,475,476,478,481,483,486,489,492],{"class":175,"line":234},[173,477,440],{"class":420},[173,479,480],{"class":190}," grant",[173,482,446],{"class":420},[173,484,485],{"class":420}," await",[173,487,488],{"class":424}," api.rooms.",[173,490,491],{"class":186},"grantActor",[173,493,494],{"class":424},"(appId, roomId, {\n",[173,496,497,500,503],{"class":175,"line":240},[173,498,499],{"class":424},"  actorTokenId: ",[173,501,502],{"class":194},"'01939f83-8b57-7c3e-a456-426614174000'",[173,504,505],{"class":424},",\n",[173,507,508,511,514],{"class":175,"line":246},[173,509,510],{"class":424},"  permission: ",[173,512,513],{"class":194},"'pubSub'",[173,515,505],{"class":424},[173,517,518,521,524,527,530],{"class":175,"line":252},[173,519,520],{"class":424},"  topics: [",[173,522,523],{"class":194},"'messages'",[173,525,526],{"class":424},", ",[173,528,529],{"class":194},"'typing'",[173,531,532],{"class":424},"]\n",[173,534,535],{"class":175,"line":258},[173,536,537],{"class":424},"})\n",[173,539,540],{"class":175,"line":265},[173,541,262],{"emptyLinePlaceholder":261},[173,543,544],{"class":175,"line":271},[173,545,546],{"class":179},"// Grant by actor type, covering every actor of that type\n",[173,548,549,552,554,556,559,562,565,568],{"class":175,"line":284},[173,550,551],{"class":420},"await",[173,553,488],{"class":424},[173,555,491],{"class":186},[173,557,558],{"class":424},"(appId, roomId, { actorType: ",[173,560,561],{"class":194},"'agent'",[173,563,564],{"class":424},", permission: ",[173,566,567],{"class":194},"'subscribe'",[173,569,570],{"class":424}," })\n",[173,572,573],{"class":175,"line":293},[173,574,262],{"emptyLinePlaceholder":261},[173,576,577],{"class":175,"line":302},[173,578,579],{"class":179},"// List and revoke\n",[173,581,582,584,587,589,591,593,596],{"class":175,"line":310},[173,583,440],{"class":420},[173,585,586],{"class":190}," grants",[173,588,446],{"class":420},[173,590,485],{"class":420},[173,592,488],{"class":424},[173,594,595],{"class":186},"listActors",[173,597,598],{"class":424},"(appId, roomId)\n",[173,600,601,603,605,608],{"class":175,"line":315},[173,602,551],{"class":420},[173,604,488],{"class":424},[173,606,607],{"class":186},"revokeActor",[173,609,610],{"class":424},"(appId, roomId, grant.roomActorAccessId)\n",[163,612,617],{"className":613,"code":614,"filename":615,"language":616,"meta":169,"style":169},"language-python shiki shiki-themes github-light github-dark","import os\nfrom nolag import NoLagApi, RoomActorAccessCreate\n\napi = NoLagApi(os.environ['NOLAG_API_KEY'])  # nlg_live_...\n\n# Grant one actor pub/sub on two topics in a room\ngrant = await api.rooms.grant_actor(app_id, room_id, RoomActorAccessCreate(\n    permission='pubSub',\n    actor_token_id='01939f83-8b57-7c3e-a456-426614174000',\n    topics=['messages', 'typing'],\n))\n\n# Grant by actor type, covering every actor of that type\nawait api.rooms.grant_actor(app_id, room_id, RoomActorAccessCreate(\n    permission='subscribe',\n    actor_type='agent',\n))\n\n# List and revoke\ngrants = await api.rooms.list_actors(app_id, room_id)\nawait api.rooms.revoke_actor(app_id, room_id, grant.room_actor_access_id)\n","Python","python",[57,618,619,624,629,633,638,642,646,651,656,661,666,671,675,680,685,690,695,699,703,708,713],{"__ignoreMap":169},[173,620,621],{"class":175,"line":176},[173,622,623],{},"import os\n",[173,625,626],{"class":175,"line":183},[173,627,628],{},"from nolag import NoLagApi, RoomActorAccessCreate\n",[173,630,631],{"class":175,"line":204},[173,632,262],{"emptyLinePlaceholder":261},[173,634,635],{"class":175,"line":215},[173,636,637],{},"api = NoLagApi(os.environ['NOLAG_API_KEY'])  # nlg_live_...\n",[173,639,640],{"class":175,"line":225},[173,641,262],{"emptyLinePlaceholder":261},[173,643,644],{"class":175,"line":234},[173,645,180],{},[173,647,648],{"class":175,"line":240},[173,649,650],{},"grant = await api.rooms.grant_actor(app_id, room_id, RoomActorAccessCreate(\n",[173,652,653],{"class":175,"line":246},[173,654,655],{},"    permission='pubSub',\n",[173,657,658],{"class":175,"line":252},[173,659,660],{},"    actor_token_id='01939f83-8b57-7c3e-a456-426614174000',\n",[173,662,663],{"class":175,"line":258},[173,664,665],{},"    topics=['messages', 'typing'],\n",[173,667,668],{"class":175,"line":265},[173,669,670],{},"))\n",[173,672,673],{"class":175,"line":271},[173,674,262],{"emptyLinePlaceholder":261},[173,676,677],{"class":175,"line":284},[173,678,679],{},"# Grant by actor type, covering every actor of that type\n",[173,681,682],{"class":175,"line":293},[173,683,684],{},"await api.rooms.grant_actor(app_id, room_id, RoomActorAccessCreate(\n",[173,686,687],{"class":175,"line":302},[173,688,689],{},"    permission='subscribe',\n",[173,691,692],{"class":175,"line":310},[173,693,694],{},"    actor_type='agent',\n",[173,696,697],{"class":175,"line":315},[173,698,670],{},[173,700,701],{"class":175,"line":321},[173,702,262],{"emptyLinePlaceholder":261},[173,704,705],{"class":175,"line":330},[173,706,707],{},"# List and revoke\n",[173,709,710],{"class":175,"line":338},[173,711,712],{},"grants = await api.rooms.list_actors(app_id, room_id)\n",[173,714,715],{"class":175,"line":343},[173,716,717],{},"await api.rooms.revoke_actor(app_id, room_id, grant.room_actor_access_id)\n",[163,719,724],{"className":720,"code":721,"filename":722,"language":723,"meta":169,"style":169},"language-go shiki shiki-themes github-light github-dark","api := nolag.NewAPI(os.Getenv(\"NOLAG_API_KEY\")) // nlg_live_...\n\n// Grant one actor pub/sub on two topics in a room\ngrant, err := api.Rooms.GrantActor(ctx, appID, roomID, nolag.RoomActorAccessCreate{\n    ActorTokenID: \"01939f83-8b57-7c3e-a456-426614174000\",\n    Permission:   nolag.PermissionPubSub,\n    Topics:       []string{\"messages\", \"typing\"},\n})\nif err != nil {\n    log.Fatal(err)\n}\n\n// Grant by actor type, covering every actor of that type\n_, _ = api.Rooms.GrantActor(ctx, appID, roomID, nolag.RoomActorAccessCreate{\n    ActorType:  \"agent\",\n    Permission: nolag.PermissionSubscribe,\n})\n\n// List and revoke\ngrants, _ := api.Rooms.ListActors(ctx, appID, roomID)\nfmt.Println(len(grants), \"grants\")\n_ = api.Rooms.RevokeActor(ctx, appID, roomID, grant.RoomActorAccessID)\n","Go","go",[57,725,726,731,735,739,744,749,754,759,763,768,773,778,782,786,791,796,801,805,809,813,818,823],{"__ignoreMap":169},[173,727,728],{"class":175,"line":176},[173,729,730],{},"api := nolag.NewAPI(os.Getenv(\"NOLAG_API_KEY\")) // nlg_live_...\n",[173,732,733],{"class":175,"line":183},[173,734,262],{"emptyLinePlaceholder":261},[173,736,737],{"class":175,"line":204},[173,738,473],{},[173,740,741],{"class":175,"line":215},[173,742,743],{},"grant, err := api.Rooms.GrantActor(ctx, appID, roomID, nolag.RoomActorAccessCreate{\n",[173,745,746],{"class":175,"line":225},[173,747,748],{},"    ActorTokenID: \"01939f83-8b57-7c3e-a456-426614174000\",\n",[173,750,751],{"class":175,"line":234},[173,752,753],{},"    Permission:   nolag.PermissionPubSub,\n",[173,755,756],{"class":175,"line":240},[173,757,758],{},"    Topics:       []string{\"messages\", \"typing\"},\n",[173,760,761],{"class":175,"line":246},[173,762,537],{},[173,764,765],{"class":175,"line":252},[173,766,767],{},"if err != nil {\n",[173,769,770],{"class":175,"line":258},[173,771,772],{},"    log.Fatal(err)\n",[173,774,775],{"class":175,"line":265},[173,776,777],{},"}\n",[173,779,780],{"class":175,"line":271},[173,781,262],{"emptyLinePlaceholder":261},[173,783,784],{"class":175,"line":284},[173,785,546],{},[173,787,788],{"class":175,"line":293},[173,789,790],{},"_, _ = api.Rooms.GrantActor(ctx, appID, roomID, nolag.RoomActorAccessCreate{\n",[173,792,793],{"class":175,"line":302},[173,794,795],{},"    ActorType:  \"agent\",\n",[173,797,798],{"class":175,"line":310},[173,799,800],{},"    Permission: nolag.PermissionSubscribe,\n",[173,802,803],{"class":175,"line":315},[173,804,537],{},[173,806,807],{"class":175,"line":321},[173,808,262],{"emptyLinePlaceholder":261},[173,810,811],{"class":175,"line":330},[173,812,579],{},[173,814,815],{"class":175,"line":338},[173,816,817],{},"grants, _ := api.Rooms.ListActors(ctx, appID, roomID)\n",[173,819,820],{"class":175,"line":343},[173,821,822],{},"fmt.Println(len(grants), \"grants\")\n",[173,824,825],{"class":175,"line":349},[173,826,827],{},"_ = api.Rooms.RevokeActor(ctx, appID, roomID, grant.RoomActorAccessID)\n",[14,829,830,833,834,837,838,841],{},[99,831,832],{},"Not over the WebSocket."," The realtime client (",[57,835,836],{},"NoLag(token)",") is a data-plane\nconnection and cannot read or change grants. Permission is enforced server-side\nwhen it subscribes or publishes, and a refusal arrives on the ",[57,839,840],{},"error"," event\nrather than as a thrown exception:",[406,843,844,1069,1168],{},[163,845,847],{"className":410,"code":846,"filename":412,"language":413,"meta":169,"style":169},"// Grants are managed over REST (above). On the realtime connection,\n// permission is enforced automatically:\nimport { NoLag, NoLagServerError } from '@nolag/js-sdk'\n\nconst client = NoLag('your_access_token')\nawait client.connect()\n\nclient.on('error', (err) => {\n  if (err instanceof NoLagServerError) {\n    // e.g. not_authorized or unknown_topic, with the topic it refers to\n    console.error(err.code, err.error, err.topic, err.hint)\n  }\n})\n\nconst room = client.setApp(APP_SLUG).setRoom('general')\n\n// Delivered only if the actor has 'subscribe' or 'pubSub' permission\nroom.subscribe('announcements')\n\n// Accepted only if the actor has 'publish' or 'pubSub' permission\nroom.emit('chat', { text: 'Hello!' })\n",[57,848,849,854,859,870,874,895,908,912,940,957,962,972,977,981,985,1017,1021,1026,1040,1044,1049],{"__ignoreMap":169},[173,850,851],{"class":175,"line":176},[173,852,853],{"class":179},"// Grants are managed over REST (above). On the realtime connection,\n",[173,855,856],{"class":175,"line":183},[173,857,858],{"class":179},"// permission is enforced automatically:\n",[173,860,861,863,866,868],{"class":175,"line":204},[173,862,421],{"class":420},[173,864,865],{"class":424}," { NoLag, NoLagServerError } ",[173,867,428],{"class":420},[173,869,431],{"class":194},[173,871,872],{"class":175,"line":215},[173,873,262],{"emptyLinePlaceholder":261},[173,875,876,878,881,883,886,889,892],{"class":175,"line":225},[173,877,440],{"class":420},[173,879,880],{"class":190}," client",[173,882,446],{"class":420},[173,884,885],{"class":186}," NoLag",[173,887,888],{"class":424},"(",[173,890,891],{"class":194},"'your_access_token'",[173,893,894],{"class":424},")\n",[173,896,897,899,902,905],{"class":175,"line":234},[173,898,551],{"class":420},[173,900,901],{"class":424}," client.",[173,903,904],{"class":186},"connect",[173,906,907],{"class":424},"()\n",[173,909,910],{"class":175,"line":240},[173,911,262],{"emptyLinePlaceholder":261},[173,913,914,917,920,922,925,928,932,934,937],{"class":175,"line":246},[173,915,916],{"class":424},"client.",[173,918,919],{"class":186},"on",[173,921,888],{"class":424},[173,923,924],{"class":194},"'error'",[173,926,927],{"class":424},", (",[173,929,931],{"class":930},"s4XuR","err",[173,933,461],{"class":424},[173,935,936],{"class":420},"=>",[173,938,939],{"class":424}," {\n",[173,941,942,945,948,951,954],{"class":175,"line":252},[173,943,944],{"class":420},"  if",[173,946,947],{"class":424}," (err ",[173,949,950],{"class":420},"instanceof",[173,952,953],{"class":186}," NoLagServerError",[173,955,956],{"class":424},") {\n",[173,958,959],{"class":175,"line":258},[173,960,961],{"class":179},"    // e.g. not_authorized or unknown_topic, with the topic it refers to\n",[173,963,964,967,969],{"class":175,"line":265},[173,965,966],{"class":424},"    console.",[173,968,840],{"class":186},[173,970,971],{"class":424},"(err.code, err.error, err.topic, err.hint)\n",[173,973,974],{"class":175,"line":271},[173,975,976],{"class":424},"  }\n",[173,978,979],{"class":175,"line":284},[173,980,537],{"class":424},[173,982,983],{"class":175,"line":293},[173,984,262],{"emptyLinePlaceholder":261},[173,986,987,989,992,994,996,999,1001,1004,1007,1010,1012,1015],{"class":175,"line":302},[173,988,440],{"class":420},[173,990,991],{"class":190}," room",[173,993,446],{"class":420},[173,995,901],{"class":424},[173,997,998],{"class":186},"setApp",[173,1000,888],{"class":424},[173,1002,1003],{"class":190},"APP_SLUG",[173,1005,1006],{"class":424},").",[173,1008,1009],{"class":186},"setRoom",[173,1011,888],{"class":424},[173,1013,1014],{"class":194},"'general'",[173,1016,894],{"class":424},[173,1018,1019],{"class":175,"line":310},[173,1020,262],{"emptyLinePlaceholder":261},[173,1022,1023],{"class":175,"line":315},[173,1024,1025],{"class":179},"// Delivered only if the actor has 'subscribe' or 'pubSub' permission\n",[173,1027,1028,1031,1033,1035,1038],{"class":175,"line":321},[173,1029,1030],{"class":424},"room.",[173,1032,59],{"class":186},[173,1034,888],{"class":424},[173,1036,1037],{"class":194},"'announcements'",[173,1039,894],{"class":424},[173,1041,1042],{"class":175,"line":330},[173,1043,262],{"emptyLinePlaceholder":261},[173,1045,1046],{"class":175,"line":338},[173,1047,1048],{"class":179},"// Accepted only if the actor has 'publish' or 'pubSub' permission\n",[173,1050,1051,1053,1056,1058,1061,1064,1067],{"class":175,"line":343},[173,1052,1030],{"class":424},[173,1054,1055],{"class":186},"emit",[173,1057,888],{"class":424},[173,1059,1060],{"class":194},"'chat'",[173,1062,1063],{"class":424},", { text: ",[173,1065,1066],{"class":194},"'Hello!'",[173,1068,570],{"class":424},[163,1070,1072],{"className":613,"code":1071,"filename":615,"language":616,"meta":169,"style":169},"# Grants are managed over REST (above). On the realtime connection,\n# permission is enforced automatically:\nfrom nolag import NoLag\n\nclient = NoLag('your_access_token')\nawait client.connect()\n\ndef on_error(err):\n    # NoLagServerError: .code, .error, .topic, .hint\n    print('Refused:', err)\n\nclient.on('error', on_error)\n\nroom = client.set_app(APP_SLUG).set_room('general')\n\n# Delivered only if the actor has 'subscribe' or 'pubSub' permission\nawait room.subscribe('announcements')\n\n# Accepted only if the actor has 'publish' or 'pubSub' permission\nawait room.emit('chat', {'text': 'Hello!'})\n",[57,1073,1074,1079,1084,1089,1093,1098,1103,1107,1112,1117,1122,1126,1131,1135,1140,1144,1149,1154,1158,1163],{"__ignoreMap":169},[173,1075,1076],{"class":175,"line":176},[173,1077,1078],{},"# Grants are managed over REST (above). On the realtime connection,\n",[173,1080,1081],{"class":175,"line":183},[173,1082,1083],{},"# permission is enforced automatically:\n",[173,1085,1086],{"class":175,"line":204},[173,1087,1088],{},"from nolag import NoLag\n",[173,1090,1091],{"class":175,"line":215},[173,1092,262],{"emptyLinePlaceholder":261},[173,1094,1095],{"class":175,"line":225},[173,1096,1097],{},"client = NoLag('your_access_token')\n",[173,1099,1100],{"class":175,"line":234},[173,1101,1102],{},"await client.connect()\n",[173,1104,1105],{"class":175,"line":240},[173,1106,262],{"emptyLinePlaceholder":261},[173,1108,1109],{"class":175,"line":246},[173,1110,1111],{},"def on_error(err):\n",[173,1113,1114],{"class":175,"line":252},[173,1115,1116],{},"    # NoLagServerError: .code, .error, .topic, .hint\n",[173,1118,1119],{"class":175,"line":258},[173,1120,1121],{},"    print('Refused:', err)\n",[173,1123,1124],{"class":175,"line":265},[173,1125,262],{"emptyLinePlaceholder":261},[173,1127,1128],{"class":175,"line":271},[173,1129,1130],{},"client.on('error', on_error)\n",[173,1132,1133],{"class":175,"line":284},[173,1134,262],{"emptyLinePlaceholder":261},[173,1136,1137],{"class":175,"line":293},[173,1138,1139],{},"room = client.set_app(APP_SLUG).set_room('general')\n",[173,1141,1142],{"class":175,"line":302},[173,1143,262],{"emptyLinePlaceholder":261},[173,1145,1146],{"class":175,"line":310},[173,1147,1148],{},"# Delivered only if the actor has 'subscribe' or 'pubSub' permission\n",[173,1150,1151],{"class":175,"line":315},[173,1152,1153],{},"await room.subscribe('announcements')\n",[173,1155,1156],{"class":175,"line":321},[173,1157,262],{"emptyLinePlaceholder":261},[173,1159,1160],{"class":175,"line":330},[173,1161,1162],{},"# Accepted only if the actor has 'publish' or 'pubSub' permission\n",[173,1164,1165],{"class":175,"line":338},[173,1166,1167],{},"await room.emit('chat', {'text': 'Hello!'})\n",[163,1169,1171],{"className":720,"code":1170,"filename":722,"language":723,"meta":169,"style":169},"// Grants are managed over REST (above). On the realtime connection,\n// permission is enforced automatically:\nclient := nolag.New(\"your_access_token\")\nclient.OnError(func(err *nolag.ServerError) {\n    fmt.Println(\"Refused:\", err.Code, err.Name, err.Topic, err.Hint)\n})\nif err := client.Connect(); err != nil {\n    log.Fatal(err)\n}\n\nroom := client.SetApp(appSlug).SetRoom(\"general\")\n\n// Delivered only if the actor has \"subscribe\" or \"pubSub\" permission\nroom.Subscribe(\"announcements\", func(data any, meta nolag.MessageMeta) {\n    fmt.Println(\"Announcement:\", data)\n})\n\n// Accepted only if the actor has \"publish\" or \"pubSub\" permission\nroom.Emit(\"chat\", map[string]string{\"text\": \"Hello!\"})\n",[57,1172,1173,1177,1181,1186,1191,1196,1200,1205,1209,1213,1217,1222,1226,1231,1236,1241,1245,1249,1254],{"__ignoreMap":169},[173,1174,1175],{"class":175,"line":176},[173,1176,853],{},[173,1178,1179],{"class":175,"line":183},[173,1180,858],{},[173,1182,1183],{"class":175,"line":204},[173,1184,1185],{},"client := nolag.New(\"your_access_token\")\n",[173,1187,1188],{"class":175,"line":215},[173,1189,1190],{},"client.OnError(func(err *nolag.ServerError) {\n",[173,1192,1193],{"class":175,"line":225},[173,1194,1195],{},"    fmt.Println(\"Refused:\", err.Code, err.Name, err.Topic, err.Hint)\n",[173,1197,1198],{"class":175,"line":234},[173,1199,537],{},[173,1201,1202],{"class":175,"line":240},[173,1203,1204],{},"if err := client.Connect(); err != nil {\n",[173,1206,1207],{"class":175,"line":246},[173,1208,772],{},[173,1210,1211],{"class":175,"line":252},[173,1212,777],{},[173,1214,1215],{"class":175,"line":258},[173,1216,262],{"emptyLinePlaceholder":261},[173,1218,1219],{"class":175,"line":265},[173,1220,1221],{},"room := client.SetApp(appSlug).SetRoom(\"general\")\n",[173,1223,1224],{"class":175,"line":271},[173,1225,262],{"emptyLinePlaceholder":261},[173,1227,1228],{"class":175,"line":284},[173,1229,1230],{},"// Delivered only if the actor has \"subscribe\" or \"pubSub\" permission\n",[173,1232,1233],{"class":175,"line":293},[173,1234,1235],{},"room.Subscribe(\"announcements\", func(data any, meta nolag.MessageMeta) {\n",[173,1237,1238],{"class":175,"line":302},[173,1239,1240],{},"    fmt.Println(\"Announcement:\", data)\n",[173,1242,1243],{"class":175,"line":310},[173,1244,537],{},[173,1246,1247],{"class":175,"line":315},[173,1248,262],{"emptyLinePlaceholder":261},[173,1250,1251],{"class":175,"line":321},[173,1252,1253],{},"// Accepted only if the actor has \"publish\" or \"pubSub\" permission\n",[173,1255,1256],{"class":175,"line":330},[173,1257,1258],{},"room.Emit(\"chat\", map[string]string{\"text\": \"Hello!\"})\n",[18,1260,1262],{"id":1261},"common-patterns","Common Patterns",[1264,1265,1267],"h3",{"id":1266},"broadcast-channel","Broadcast Channel",[14,1269,1270],{},"A backend service publishes announcements, clients subscribe to receive them:",[129,1272,1273,1281],{},[132,1274,1275,1278,1279],{},[57,1276,1277],{},"service"," actors: ",[57,1280,69],{},[132,1282,1283,379,1286,1278,1289],{},[57,1284,1285],{},"user",[57,1287,1288],{},"device",[57,1290,59],{},[1264,1292,1294],{"id":1293},"chat-room","Chat Room",[14,1296,1297],{},"All participants can read and write messages:",[129,1299,1300],{},[132,1301,1302,1303],{},"All actors: ",[57,1304,79],{},[1264,1306,1308],{"id":1307},"private-notifications","Private Notifications",[14,1310,1311],{},"A backend service sends notifications, one user reads them:",[129,1313,1314,1320],{},[132,1315,1316,1278,1318],{},[57,1317,1277],{},[57,1319,69],{},[132,1321,1322,1323],{},"The user's actor token: ",[57,1324,59],{},[14,1326,1327,1328,526,1330,526,1332,526,1334,526,1337,526,1340,1343,1344,1347,1348,1350,1351,1353],{},"Actor types are ",[57,1329,1288],{},[57,1331,1285],{},[57,1333,1277],{},[57,1335,1336],{},"session",[57,1338,1339],{},"agent",[57,1341,1342],{},"orchestrator"," and ",[57,1345,1346],{},"observer",". A grant by ",[57,1349,119],{}," covers every actor of that type; a grant by ",[57,1352,115],{}," covers one actor and takes precedence.",[18,1355,1357],{"id":1356},"next-steps","Next Steps",[129,1359,1360,1366],{},[132,1361,1362],{},[91,1363,1365],{"href":1364},"/docs/authentication","Authentication Guide",[132,1367,1368],{},[91,1369,1371],{"href":1370},"/docs/concepts/topics","Topics & Pub/Sub",[1373,1374,1375],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":169,"searchDepth":183,"depth":183,"links":1377},[1378,1379,1380,1381,1382,1387],{"id":20,"depth":183,"text":21},{"id":27,"depth":183,"text":28},{"id":85,"depth":183,"text":86},{"id":145,"depth":183,"text":146},{"id":1261,"depth":183,"text":1262,"children":1383},[1384,1385,1386],{"id":1266,"depth":204,"text":1267},{"id":1293,"depth":204,"text":1294},{"id":1307,"depth":204,"text":1308},{"id":1356,"depth":183,"text":1357},"Learn how to configure fine-grained access control for NoLag topics.","md",{},"/docs/concepts/acl",{"title":5,"description":1388},"docs/concepts/acl","RzDchRFYGP1GGdTml-qFddiY4UiWxHD_xt4I22Fc3fo",1789869419051]