[{"data":1,"prerenderedAt":897},["ShallowReactive",2],{"docs:/docs/authentication":3},{"id":4,"title":5,"body":6,"description":890,"extension":891,"meta":892,"navigation":277,"path":893,"seo":894,"stem":895,"__hash__":896},"docs/docs/authentication.md","Authentication",{"type":7,"value":8,"toc":874},"minimark",[9,13,17,80,85,99,106,111,133,137,166,176,201,208,212,215,219,242,246,473,477,484,605,612,616,623,731,738,742,754,757,770,796,802,814,818,849,853,870],[10,11,5],"h1",{"id":12},"authentication",[14,15,16],"p",{},"NoLag has two kinds of credential, and they are not interchangeable:",[18,19,20,36],"table",{},[21,22,23],"thead",{},[24,25,26,30,33],"tr",{},[27,28,29],"th",{},"Credential",[27,31,32],{},"Used for",[27,34,35],{},"Where it goes",[37,38,39,54,67],"tbody",{},[24,40,41,48,51],{},[42,43,44],"td",{},[45,46,47],"strong",{},"API key",[42,49,50],{},"Managing resources over the REST API: apps, rooms, actors",[42,52,53],{},"Your backend or a setup script",[24,55,56,61,64],{},[42,57,58],{},[45,59,60],{},"Access token",[42,62,63],{},"Connecting a client to the broker over WebSocket",[42,65,66],{},"The client, or a backend service",[24,68,69,74,77],{},[42,70,71],{},[45,72,73],{},"Client token",[42,75,76],{},"Connecting an untrusted browser or mobile client",[42,78,79],{},"Minted per session by your backend",[81,82,84],"h2",{"id":83},"api-keys","API Keys",[14,86,87,88,93,94,98],{},"An API key authenticates calls to the ",[89,90,92],"a",{"href":91},"/docs/api-reference","REST API"," at\n",[95,96,97],"code",{},"https://api.nolag.app/v1",". It is the one credential you create by hand, and it is\nthe bootstrap secret for scripted or agent-driven setup.",[14,100,101,102,105],{},"API keys are ",[45,103,104],{},"project-scoped",", so the key itself determines which project's\nresources you can reach and no organization or project id appears in the URL.",[107,108,110],"h3",{"id":109},"creating-an-api-key","Creating an API Key",[112,113,114,125,130],"ol",{},[115,116,117,118,124],"li",{},"Log in to the ",[89,119,123],{"href":120,"rel":121},"https://portal.nolag.app",[122],"nofollow","NoLag Dashboard",".",[115,126,127,128,124],{},"Open your project and go to ",[45,129,84],{},[115,131,132],{},"Create a key and copy it immediately. The secret half is shown only once.",[107,134,136],{"id":135},"key-format","Key Format",[138,139,145],"pre",{"className":140,"code":141,"filename":142,"language":143,"meta":144,"style":144},"language-bash shiki shiki-themes github-light github-dark","nlg_live_{keyId}.{secret}\n","Terminal","bash","",[95,146,147],{"__ignoreMap":144},[148,149,152,156,160,163],"span",{"class":150,"line":151},"line",1,[148,153,155],{"class":154},"sScJk","nlg_live_",[148,157,159],{"class":158},"sZZnC","{keyId}",[148,161,124],{"class":162},"sj4cs",[148,164,165],{"class":158},"{secret}\n",[14,167,168,169,171,172,175],{},"Live keys are prefixed ",[95,170,155],{},", sandbox keys ",[95,173,174],{},"nlg_sandbox_",". Send the whole\nstring, including the dot and the secret, as a bearer token:",[138,177,179],{"className":140,"code":178,"filename":142,"language":143,"meta":144,"style":144},"curl https://api.nolag.app/v1/apps \\\n  -H \"Authorization: Bearer nlg_live_xxx.secret\"\n",[95,180,181,192],{"__ignoreMap":144},[148,182,183,186,189],{"class":150,"line":151},[148,184,185],{"class":154},"curl",[148,187,188],{"class":158}," https://api.nolag.app/v1/apps",[148,190,191],{"class":162}," \\\n",[148,193,195,198],{"class":150,"line":194},2,[148,196,197],{"class":162},"  -H",[148,199,200],{"class":158}," \"Authorization: Bearer nlg_live_xxx.secret\"\n",[202,203,205],"callout",{"type":204},"warning",[14,206,207],{},"An API key can create and delete every app, room, and actor in its project. Keep it\non a server, never in a browser, mobile binary, or public repository.",[81,209,211],{"id":210},"access-tokens","Access Tokens",[14,213,214],{},"Access tokens are used to authenticate your clients with NoLag. Each token is associated with an Actor (user, device, or server) and determines what topics they can access.",[107,216,218],{"id":217},"obtaining-tokens","Obtaining Tokens",[112,220,221,226,229,236,239],{},[115,222,117,223],{},[89,224,123],{"href":120,"rel":225},[122],[115,227,228],{},"Navigate to your project",[115,230,231,232,235],{},"Go to ",[45,233,234],{},"Actors"," section",[115,237,238],{},"Create a new Actor or select an existing one",[115,240,241],{},"Copy the access token (shown only once on creation)",[81,243,245],{"id":244},"using-access-tokens","Using Access Tokens",[247,248,249,346,387],"code-tabs",{},[138,250,255],{"className":251,"code":252,"filename":253,"language":254,"meta":144,"style":144},"language-typescript shiki shiki-themes github-light github-dark","import { NoLag } from '@nolag/js-sdk'\n\n// Using an access token\nconst client = NoLag('your_access_token')\nawait client.connect()\n\nconsole.log('Authenticated and connected!')\n","TypeScript","typescript",[95,256,257,273,279,286,310,325,330],{"__ignoreMap":144},[148,258,259,263,267,270],{"class":150,"line":151},[148,260,262],{"class":261},"szBVR","import",[148,264,266],{"class":265},"sVt8B"," { NoLag } ",[148,268,269],{"class":261},"from",[148,271,272],{"class":158}," '@nolag/js-sdk'\n",[148,274,275],{"class":150,"line":194},[148,276,278],{"emptyLinePlaceholder":277},true,"\n",[148,280,282],{"class":150,"line":281},3,[148,283,285],{"class":284},"sJ8bj","// Using an access token\n",[148,287,289,292,295,298,301,304,307],{"class":150,"line":288},4,[148,290,291],{"class":261},"const",[148,293,294],{"class":162}," client",[148,296,297],{"class":261}," =",[148,299,300],{"class":154}," NoLag",[148,302,303],{"class":265},"(",[148,305,306],{"class":158},"'your_access_token'",[148,308,309],{"class":265},")\n",[148,311,313,316,319,322],{"class":150,"line":312},5,[148,314,315],{"class":261},"await",[148,317,318],{"class":265}," client.",[148,320,321],{"class":154},"connect",[148,323,324],{"class":265},"()\n",[148,326,328],{"class":150,"line":327},6,[148,329,278],{"emptyLinePlaceholder":277},[148,331,333,336,339,341,344],{"class":150,"line":332},7,[148,334,335],{"class":265},"console.",[148,337,338],{"class":154},"log",[148,340,303],{"class":265},[148,342,343],{"class":158},"'Authenticated and connected!'",[148,345,309],{"class":265},[138,347,352],{"className":348,"code":349,"filename":350,"language":351,"meta":144,"style":144},"language-python shiki shiki-themes github-light github-dark","from nolag import NoLag\n\n# Using an access token\nclient = NoLag('your_access_token')\nawait client.connect()\n\nprint('Authenticated and connected!')\n","Python","python",[95,353,354,359,363,368,373,378,382],{"__ignoreMap":144},[148,355,356],{"class":150,"line":151},[148,357,358],{},"from nolag import NoLag\n",[148,360,361],{"class":150,"line":194},[148,362,278],{"emptyLinePlaceholder":277},[148,364,365],{"class":150,"line":281},[148,366,367],{},"# Using an access token\n",[148,369,370],{"class":150,"line":288},[148,371,372],{},"client = NoLag('your_access_token')\n",[148,374,375],{"class":150,"line":312},[148,376,377],{},"await client.connect()\n",[148,379,380],{"class":150,"line":327},[148,381,278],{"emptyLinePlaceholder":277},[148,383,384],{"class":150,"line":332},[148,385,386],{},"print('Authenticated and connected!')\n",[138,388,393],{"className":389,"code":390,"filename":391,"language":392,"meta":144,"style":144},"language-go shiki shiki-themes github-light github-dark","package main\n\nimport (\n    \"fmt\"\n\n    nolag \"github.com/NoLagApp/go-sdk\"\n)\n\nfunc main() {\n    // Using an access token\n    client := nolag.New(\"your_access_token\")\n    client.Connect()\n\n    fmt.Println(\"Authenticated and connected!\")\n}\n","Go","go",[95,394,395,400,404,409,414,418,423,427,432,438,444,450,456,461,467],{"__ignoreMap":144},[148,396,397],{"class":150,"line":151},[148,398,399],{},"package main\n",[148,401,402],{"class":150,"line":194},[148,403,278],{"emptyLinePlaceholder":277},[148,405,406],{"class":150,"line":281},[148,407,408],{},"import (\n",[148,410,411],{"class":150,"line":288},[148,412,413],{},"    \"fmt\"\n",[148,415,416],{"class":150,"line":312},[148,417,278],{"emptyLinePlaceholder":277},[148,419,420],{"class":150,"line":327},[148,421,422],{},"    nolag \"github.com/NoLagApp/go-sdk\"\n",[148,424,425],{"class":150,"line":332},[148,426,309],{},[148,428,430],{"class":150,"line":429},8,[148,431,278],{"emptyLinePlaceholder":277},[148,433,435],{"class":150,"line":434},9,[148,436,437],{},"func main() {\n",[148,439,441],{"class":150,"line":440},10,[148,442,443],{},"    // Using an access token\n",[148,445,447],{"class":150,"line":446},11,[148,448,449],{},"    client := nolag.New(\"your_access_token\")\n",[148,451,453],{"class":150,"line":452},12,[148,454,455],{},"    client.Connect()\n",[148,457,459],{"class":150,"line":458},13,[148,460,278],{"emptyLinePlaceholder":277},[148,462,464],{"class":150,"line":463},14,[148,465,466],{},"    fmt.Println(\"Authenticated and connected!\")\n",[148,468,470],{"class":150,"line":469},15,[148,471,472],{},"}\n",[81,474,476],{"id":475},"client-tokens-browser-and-mobile","Client Tokens (Browser and Mobile)",[14,478,479,480,483],{},"Access tokens are long-lived, so they belong on servers, not in browsers. For untrusted clients, your backend mints a short-lived JWT (a ",[45,481,482],{},"client token",") signed with a project-level signing key, and the browser connects with that instead:",[138,485,488],{"className":251,"code":486,"filename":487,"language":254,"meta":144,"style":144},"import { NoLag } from '@nolag/js-sdk'\n\n// The SDK calls your endpoint for a fresh token on every connect\nconst client = NoLag(async () => {\n  const res = await fetch('/api/nolag-token')\n  const { token } = await res.json()\n  return token\n})\nawait client.connect()\n","Browser",[95,489,490,500,504,509,533,556,582,590,595],{"__ignoreMap":144},[148,491,492,494,496,498],{"class":150,"line":151},[148,493,262],{"class":261},[148,495,266],{"class":265},[148,497,269],{"class":261},[148,499,272],{"class":158},[148,501,502],{"class":150,"line":194},[148,503,278],{"emptyLinePlaceholder":277},[148,505,506],{"class":150,"line":281},[148,507,508],{"class":284},"// The SDK calls your endpoint for a fresh token on every connect\n",[148,510,511,513,515,517,519,521,524,527,530],{"class":150,"line":288},[148,512,291],{"class":261},[148,514,294],{"class":162},[148,516,297],{"class":261},[148,518,300],{"class":154},[148,520,303],{"class":265},[148,522,523],{"class":261},"async",[148,525,526],{"class":265}," () ",[148,528,529],{"class":261},"=>",[148,531,532],{"class":265}," {\n",[148,534,535,538,541,543,546,549,551,554],{"class":150,"line":312},[148,536,537],{"class":261},"  const",[148,539,540],{"class":162}," res",[148,542,297],{"class":261},[148,544,545],{"class":261}," await",[148,547,548],{"class":154}," fetch",[148,550,303],{"class":265},[148,552,553],{"class":158},"'/api/nolag-token'",[148,555,309],{"class":265},[148,557,558,560,563,566,569,572,574,577,580],{"class":150,"line":327},[148,559,537],{"class":261},[148,561,562],{"class":265}," { ",[148,564,565],{"class":162},"token",[148,567,568],{"class":265}," } ",[148,570,571],{"class":261},"=",[148,573,545],{"class":261},[148,575,576],{"class":265}," res.",[148,578,579],{"class":154},"json",[148,581,324],{"class":265},[148,583,584,587],{"class":150,"line":332},[148,585,586],{"class":261},"  return",[148,588,589],{"class":265}," token\n",[148,591,592],{"class":150,"line":429},[148,593,594],{"class":265},"})\n",[148,596,597,599,601,603],{"class":150,"line":434},[148,598,315],{"class":261},[148,600,318],{"class":265},[148,602,321],{"class":154},[148,604,324],{"class":265},[14,606,607,608,124],{},"The client token names an actor and expires within minutes; all permissions still resolve from the actor. See the full guide: ",[89,609,611],{"href":610},"/docs/client-tokens","Client Tokens",[81,613,615],{"id":614},"actor-types","Actor Types",[14,617,618,619,622],{},"Every actor is created with an ",[95,620,621],{},"actorType",". It describes what the connection is,\nwhich makes actors easier to filter and audit — and for two of them it also\nchanges how the broker treats the connection:",[18,624,625,639],{},[21,626,627],{},[24,628,629,633,636],{},[27,630,631],{},[95,632,621],{},[27,634,635],{},"Use for",[27,637,638],{},"Session",[37,640,641,654,666,678,690,705,719],{},[24,642,643,648,651],{},[42,644,645],{},[95,646,647],{},"device",[42,649,650],{},"Browsers, mobile apps, IoT hardware",[42,652,653],{},"clean",[24,655,656,661,664],{},[42,657,658],{},[95,659,660],{},"user",[42,662,663],{},"Authenticated end users",[42,665,653],{},[24,667,668,673,676],{},[42,669,670],{},[95,671,672],{},"service",[42,674,675],{},"Backend services and microservices",[42,677,653],{},[24,679,680,685,688],{},[42,681,682],{},[95,683,684],{},"session",[42,686,687],{},"Short-lived or temporary connections",[42,689,653],{},[24,691,692,697,700],{},[42,693,694],{},[95,695,696],{},"agent",[42,698,699],{},"Autonomous LLM-powered connections",[42,701,702],{},[45,703,704],{},"persists",[24,706,707,712,715],{},[42,708,709],{},[95,710,711],{},"orchestrator",[42,713,714],{},"Coordination actors that dispatch work across agents",[42,716,717],{},[45,718,704],{},[24,720,721,726,729],{},[42,722,723],{},[95,724,725],{},"observer",[42,727,728],{},"Read-only audit and monitoring connections",[42,730,653],{},[14,732,733,734,124],{},"Permissions come from the actor's topic access, not from its type. See\n",[89,735,737],{"href":736},"/docs/concepts/acl","Access Control",[107,739,741],{"id":740},"why-the-session-column-matters","Why the session column matters",[14,743,744,746,747,749,750,753],{},[95,745,696],{}," and ",[95,748,711],{}," connections hold a ",[45,751,752],{},"persistent session",". When one\ndisconnects, the broker keeps its subscriptions and queues messages for it, so a\nworker that goes away finds its work waiting when it comes back. That is what\nmakes an agent that scales to zero — or is woken by a webhook — workable.",[14,755,756],{},"It has two consequences worth knowing before you pick a type.",[14,758,759,762,763,769],{},[45,760,761],{},"A session belongs to a client instance, not to a credential."," Two processes\nsharing one agent token are two attempts at the same session: the first keeps a\nresumable one and the rest get clean sessions instead. If you want several\nconcurrent workers under one token to each keep their own, give each a stable\n",[89,764,766],{"href":765},"/docs/protocol#1-authentication",[95,767,768],{},"clientId",":",[247,771,772],{},[138,773,775],{"className":251,"code":774,"filename":253,"language":254,"meta":144,"style":144},"const client = NoLag(token, { clientId: process.env.WORKER_NAME })\n",[95,776,777],{"__ignoreMap":144},[148,778,779,781,783,785,787,790,793],{"class":150,"line":151},[148,780,291],{"class":261},[148,782,294],{"class":162},[148,784,297],{"class":261},[148,786,300],{"class":154},[148,788,789],{"class":265},"(token, { clientId: process.env.",[148,791,792],{"class":162},"WORKER_NAME",[148,794,795],{"class":265}," })\n",[14,797,798,801],{},[45,799,800],{},"Sessions expire on the plan's session window."," A subscription left behind by\na persistent connection survives until then, so an agent that reconnects under a\ndifferent load-balance group name can briefly belong to both.",[14,803,804,805,807,808,810,811,813],{},"If you do not want any of this — a per-request connection, a browser, a\nshort-lived job — use ",[95,806,672],{},", ",[95,809,684],{}," or ",[95,812,647],{},". They connect clean, any\nnumber of them can share a token concurrently, and nothing is retained when they\ngo.",[81,815,817],{"id":816},"security-best-practices","Security Best Practices",[819,820,821,831,837,843],"ul",{},[115,822,823,826,827,830],{},[45,824,825],{},"Never ship an access token to a browser"," - Mint short-lived ",[89,828,829],{"href":610},"client tokens"," on your backend instead",[115,832,833,836],{},[45,834,835],{},"Rotate tokens regularly"," - Especially for production environments",[115,838,839,842],{},[45,840,841],{},"Use least privilege"," - Only grant necessary permissions to each Actor",[115,844,845,848],{},[45,846,847],{},"Monitor usage"," - Check the dashboard for unusual activity",[81,850,852],{"id":851},"next-steps","Next Steps",[819,854,855,859,865],{},[115,856,857],{},[89,858,611],{"href":610},[115,860,861],{},[89,862,864],{"href":863},"/docs/getting-started","Quick Start Guide",[115,866,867],{},[89,868,869],{"href":736},"Access Control Lists",[871,872,873],"style",{},"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 .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 pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":144,"searchDepth":194,"depth":194,"links":875},[876,880,883,884,885,888,889],{"id":83,"depth":194,"text":84,"children":877},[878,879],{"id":109,"depth":281,"text":110},{"id":135,"depth":281,"text":136},{"id":210,"depth":194,"text":211,"children":881},[882],{"id":217,"depth":281,"text":218},{"id":244,"depth":194,"text":245},{"id":475,"depth":194,"text":476},{"id":614,"depth":194,"text":615,"children":886},[887],{"id":740,"depth":281,"text":741},{"id":816,"depth":194,"text":817},{"id":851,"depth":194,"text":852},"Learn how to authenticate with NoLag using access tokens and API keys.","md",{},"/docs/authentication",{"title":5,"description":890},"docs/authentication","VMDacGnS2tawetZR2JQpTewqEhSOOS3J23X8XJbixhk",1788160342703]