[{"data":1,"prerenderedAt":1305},["ShallowReactive",2],{"docs:/docs/self-hosting/configuration":3},{"id":4,"title":5,"body":6,"description":1297,"extension":1298,"meta":1299,"navigation":1300,"path":1301,"seo":1302,"stem":1303,"__hash__":1304},"docs/docs/self-hosting/configuration.md","Configuration",{"type":7,"value":8,"toc":1282},"minimark",[9,13,25,48,55,60,125,129,250,254,350,354,360,420,424,494,498,530,534,567,571,577,741,756,760,763,909,913,927,1074,1078,1085],[10,11,5],"h1",{"id":12},"configuration",[14,15,16,17,21,22,24],"p",{},"kraken is configured with environment variables, which are substituted into its ",[18,19,20],"code",{},"sys.config"," when the release starts. The defaults below are the ones baked into kraken's Docker image. A few settings exist only in ",[18,23,20],{},"; they are listed at the end of this page.",[14,26,27,28,31,32,35,36,39,40,43,44,47],{},"The repository's ",[18,29,30],{},"docker-compose.yml"," passes only ",[18,33,34],{},"AUTH_ALLOW_ALL"," through from your shell. To set anything else, add it under ",[18,37,38],{},"environment:"," in a compose file, or pass ",[18,41,42],{},"-e NAME=value"," to ",[18,45,46],{},"docker run",".",[14,49,50,51,54],{},"This page describes v0.9.0. Where kraken's own ",[18,52,53],{},"docs/CONFIG.md"," says otherwise, this page reflects what the code does.",[56,57,59],"h2",{"id":58},"listeners","Listeners",[61,62,63,79],"table",{},[64,65,66],"thead",{},[67,68,69,73,76],"tr",{},[70,71,72],"th",{},"Variable",[70,74,75],{},"Default",[70,77,78],{},"Meaning",[80,81,82,105],"tbody",{},[67,83,84,90,95],{},[85,86,87],"td",{},[18,88,89],{},"WS_PORT",[85,91,92],{},[18,93,94],{},"8080",[85,96,97,98,101,102],{},"HTTP port: the WebSocket endpoint ",[18,99,100],{},"/ws"," and the health check ",[18,103,104],{},"/health",[67,106,107,112,117],{},[85,108,109],{},[18,110,111],{},"MQTT_PORT",[85,113,114],{},[18,115,116],{},"1883",[85,118,119,120,124],{},"Port for kraken's MQTT 3.1.1 listener. ",[121,122,123],"strong",{},"No MQTT client can connect in v0.9.0",": the connection handler fails to start for every connection. Do not expose this port.",[56,126,128],{"id":127},"backends","Backends",[61,130,131,142],{},[64,132,133],{},[67,134,135,137,139],{},[70,136,72],{},[70,138,75],{},[70,140,141],{},"Values",[80,143,144,175,206,227],{},[67,145,146,151,156],{},[85,147,148],{},[18,149,150],{},"AUTH_BACKEND",[85,152,153],{},[18,154,155],{},"static",[85,157,158,160,161,164,165,170,171,47],{},[18,159,155],{}," (a token file), ",[18,162,163],{},"http"," (your service), or an Erlang module name. See ",[166,167,169],"a",{"href":168},"/docs/self-hosting/static-auth","Static Auth File"," and ",[166,172,174],{"href":173},"/docs/self-hosting/http-auth","HTTP Auth Contract",[67,176,177,182,187],{},[85,178,179],{},[18,180,181],{},"BROKER_BACKEND",[85,183,184],{},[18,185,186],{},"syn",[85,188,189,191,192,195,196,198,199,201,202,47],{},[18,190,186],{}," (built in, no dependencies) or ",[18,193,194],{},"mqtt"," (an external MQTT broker), or a module name. ",[18,197,186],{}," ignores QoS; ",[18,200,194],{}," passes it to the external broker. See ",[166,203,205],{"href":204},"/docs/self-hosting/scaling","Scaling and MQTT",[67,207,208,213,218],{},[85,209,210],{},[18,211,212],{},"STORE_BACKEND",[85,214,215],{},[18,216,217],{},"ets",[85,219,220,222,223,226],{},[18,221,217],{}," (in memory) or ",[18,224,225],{},"noop",", or a module name. Where recorded messages go.",[67,228,229,234,238],{},[85,230,231],{},[18,232,233],{},"CONTROL_BACKEND",[85,235,236],{},[18,237,225],{},[85,239,240,242,243,245,246,47],{},[18,241,225],{}," or ",[18,244,163],{},", or a module name. Usage, subscription and webhook-failure reports. See ",[166,247,249],{"href":248},"/docs/self-hosting/plugins#control","Plugins",[56,251,253],{"id":252},"auth-and-control-plane","Auth and control plane",[61,255,256,266],{},[64,257,258],{},[67,259,260,262,264],{},[70,261,72],{},[70,263,75],{},[70,265,78],{},[80,267,268,286,304,320,334],{},[67,269,270,275,280],{},[85,271,272],{},[18,273,274],{},"AUTH_FILE",[85,276,277],{},[18,278,279],{},"/app/examples/auth.json",[85,281,282,283,285],{},"Token file for the ",[18,284,155],{}," backend",[67,287,288,292,297],{},[85,289,290],{},[18,291,34],{},[85,293,294],{},[18,295,296],{},"false",[85,298,299,300,303],{},"Accept any token with access to every topic. ",[121,301,302],{},"Insecure","; local development only.",[67,305,306,311,314],{},[85,307,308],{},[18,309,310],{},"AUTH_HTTP_URL",[85,312,313],{},"empty",[85,315,316,317,319],{},"Base URL for the ",[18,318,163],{}," auth backend. Its path is kept.",[67,321,322,327,329],{},[85,323,324],{},[18,325,326],{},"CONTROL_HTTP_URL",[85,328,313],{},[85,330,316,331,333],{},[18,332,163],{}," control backend",[67,335,336,341,343],{},[85,337,338],{},[18,339,340],{},"BACKEND_SECRET",[85,342,313],{},[85,344,345,346,349],{},"Sent as ",[18,347,348],{},"Authorization: Bearer \u003Csecret>"," to both HTTP backends",[56,351,353],{"id":352},"mqtt-broker-backend","MQTT broker backend",[14,355,356,357,47],{},"Used only with ",[18,358,359],{},"BROKER_BACKEND=mqtt",[61,361,362,372],{},[64,363,364],{},[67,365,366,368,370],{},[70,367,72],{},[70,369,75],{},[70,371,78],{},[80,373,374,386,404],{},[67,375,376,381,383],{},[85,377,378],{},[18,379,380],{},"MQTT_BROKER_HOST",[85,382,313],{},[85,384,385],{},"Host of the external MQTT broker",[67,387,388,393,398],{},[85,389,390],{},[18,391,392],{},"MQTT_BROKER_PORT",[85,394,395],{},[18,396,397],{},"1884",[85,399,400,401,403],{},"Its port. Most brokers listen on ",[18,402,116],{},", so you will usually set this.",[67,405,406,415,417],{},[85,407,408,411,412],{},[18,409,410],{},"MQTT_BROKER_USERNAME",", ",[18,413,414],{},"MQTT_BROKER_PASSWORD",[85,416,313],{},[85,418,419],{},"Credentials, if the broker wants them",[56,421,423],{"id":422},"message-recording","Message recording",[61,425,426,436],{},[64,427,428],{},[67,429,430,432,434],{},[70,431,72],{},[70,433,75],{},[70,435,78],{},[80,437,438,460,477],{},[67,439,440,445,450],{},[85,441,442],{},[18,443,444],{},"RECORD_MESSAGES",[85,446,447],{},[18,448,449],{},"true",[85,451,452,453,170,456,459],{},"When on, kraken gives every publish a message id and writes it to the store backend. Deliveries then carry ",[18,454,455],{},"msgId",[18,457,458],{},"requiresAck: true",", and the SDKs acknowledge them. Nothing in v0.9.0 lets a client read recorded messages back, so for most deployments this only costs memory. The nolag-core quickstart turns it off.",[67,461,462,467,472],{},[85,463,464],{},[18,465,466],{},"STORE_TTL_SECONDS",[85,468,469],{},[18,470,471],{},"3600",[85,473,474,476],{},[18,475,217],{}," store: how long a recorded message is kept",[67,478,479,484,489],{},[85,480,481],{},[18,482,483],{},"STORE_MAX_MESSAGES",[85,485,486],{},[18,487,488],{},"10000",[85,490,491,493],{},[18,492,217],{}," store: above this many messages, the oldest tenth is dropped",[56,495,497],{"id":496},"limits","Limits",[61,499,500,510],{},[64,501,502],{},[67,503,504,506,508],{},[70,505,72],{},[70,507,75],{},[70,509,78],{},[80,511,512],{},[67,513,514,519,524],{},[85,515,516],{},[18,517,518],{},"MAX_MESSAGE_SIZE",[85,520,521],{},[18,522,523],{},"921600",[85,525,526,529],{},[121,527,528],{},"Not enforced in v0.9.0."," Every publish is held to a fixed 921,600 byte ceiling whatever this says, and the same goes for per-token sizes from an auth backend.",[56,531,533],{"id":532},"internal","Internal",[61,535,536,546],{},[64,537,538],{},[67,539,540,542,544],{},[70,541,72],{},[70,543,75],{},[70,545,78],{},[80,547,548],{},[67,549,550,555,560],{},[85,551,552],{},[18,553,554],{},"INTERNAL_SECRET",[85,556,557],{},[18,558,559],{},"change_me",[85,561,562,563,566],{},"Guards an internal HTTP endpoint on the WebSocket port. It is internal and unsupported: do not build on it. Change the default anyway, and do not route ",[18,564,565],{},"/internal/"," through your reverse proxy.",[56,568,570],{"id":569},"clustering","Clustering",[14,572,573,574,576],{},"See ",[166,575,205],{"href":204}," for how these fit together.",[61,578,579,589],{},[64,580,581],{},[67,582,583,585,587],{},[70,584,72],{},[70,586,75],{},[70,588,78],{},[80,590,591,618,632,646,670,685,702,717],{},[67,592,593,598,603],{},[85,594,595],{},[18,596,597],{},"CLUSTER_STRATEGY",[85,599,600],{},[18,601,602],{},"standalone",[85,604,605,411,607,242,610,613,614,617],{},[18,606,602],{},[18,608,609],{},"epmd",[18,611,612],{},"dns",". ",[18,615,616],{},"gossip"," is accepted but does not form a cluster in v0.9.0.",[67,619,620,625,627],{},[85,621,622],{},[18,623,624],{},"CLUSTER_HOSTS",[85,626,313],{},[85,628,629,631],{},[18,630,609],{},": comma-separated full node names of the peers",[67,633,634,639,641],{},[85,635,636],{},[18,637,638],{},"CLUSTER_DNS_QUERY",[85,640,313],{},[85,642,643,645],{},[18,644,612],{},": a name whose A records are the peers' IP addresses",[67,647,648,653,658],{},[85,649,650],{},[18,651,652],{},"CLUSTER_NODE_BASENAME",[85,654,655],{},[18,656,657],{},"kraken_proxy",[85,659,660,662,663,666,667,47],{},[18,661,612],{},": the part before ",[18,664,665],{},"@"," in each peer's node name. Set it to match your nodes' ",[18,668,669],{},"ERLANG_NODE_NAME",[67,671,672,677,682],{},[85,673,674],{},[18,675,676],{},"CLUSTER_POLL_INTERVAL",[85,678,679],{},[18,680,681],{},"30000",[85,683,684],{},"Milliseconds between discovery attempts",[67,686,687,691,696],{},[85,688,689],{},[18,690,669],{},[85,692,693],{},[18,694,695],{},"kraken@127.0.0.1",[85,697,698,699,701],{},"This node's full name. kraken runs with long names, so the part after ",[18,700,665],{}," must be a fully qualified domain name or an IP address.",[67,703,704,709,714],{},[85,705,706],{},[18,707,708],{},"ERLANG_COOKIE",[85,710,711],{},[18,712,713],{},"kraken_dev_cookie",[85,715,716],{},"Shared secret between nodes; must match across the cluster. Change it.",[67,718,719,727,735],{},[85,720,721,411,724],{},[18,722,723],{},"CLUSTER_GOSSIP_PORT",[18,725,726],{},"CLUSTER_MULTICAST_ADDR",[85,728,729,411,732],{},[18,730,731],{},"45892",[18,733,734],{},"230.1.1.1",[85,736,737,738,740],{},"Used only by ",[18,739,616],{},", which does not work in v0.9.0",[14,742,743,744,746,747,170,750,753,754,47],{},"kraken's ",[18,745,53],{}," also lists ",[18,748,749],{},"CLUSTER_DNS_NAME",[18,751,752],{},"CLUSTER_GOSSIP_SECRET",". Nothing in v0.9.0 reads either; DNS discovery uses ",[18,755,638],{},[56,757,759],{"id":758},"fixed-limits","Fixed limits",[14,761,762],{},"These are constants in the code, the same for every connection whatever the auth backend returns. We measured each of the first four against v0.9.0.",[61,764,765,778],{},[64,766,767],{},[67,768,769,772,775],{},[70,770,771],{},"Limit",[70,773,774],{},"Value",[70,776,777],{},"When exceeded",[80,779,780,798,819,833,847,858,869,883,896],{},[67,781,782,785,788],{},[85,783,784],{},"Publishes per connection",[85,786,787],{},"50 per second",[85,789,790,791,411,794,797],{},"The rest of that second's publishes are refused with error ",[18,792,793],{},"42910",[18,795,796],{},"rate_limit_exceeded",". Other frame types do not count.",[67,799,800,803,806],{},[85,801,802],{},"Publish payload",[85,804,805],{},"921,600 bytes, measured as packed MessagePack",[85,807,808,809,411,812,815,816],{},"Refused with error ",[18,810,811],{},"42930",[18,813,814],{},"message_too_large",", carrying ",[18,817,818],{},"maxSizeBytes",[67,820,821,824,827],{},[85,822,823],{},"WebSocket frame",[85,825,826],{},"1 MiB (1,048,576 bytes)",[85,828,829,830],{},"The connection is closed with code ",[18,831,832],{},"1009",[67,834,835,838,841],{},[85,836,837],{},"Idle connection",[85,839,840],{},"60 seconds without any frame from the client",[85,842,829,843,846],{},[18,844,845],{},"1000",". The SDKs send a heartbeat every 30 seconds.",[67,848,849,852,855],{},[85,850,851],{},"Auth cache",[85,853,854],{},"30 seconds per token",[85,856,857],{},"Successful validations only",[67,859,860,863,866],{},[85,861,862],{},"Revalidation",[85,864,865],{},"Every 10 minutes per connection",[85,867,868],{},"Checked on the client's heartbeats",[67,870,871,874,877],{},[85,872,873],{},"Filters",[85,875,876],{},"100 per subscription",[85,878,879,880],{},"Refused with ",[18,881,882],{},"too_many_filters (max 100)",[67,884,885,891,894],{},[85,886,887,888,890],{},"Retained messages (",[18,889,186],{}," broker)",[85,892,893],{},"The last one per topic, kept 1 hour, in memory",[85,895],{},[67,897,898,901,904],{},[85,899,900],{},"Room-access check",[85,902,903],{},"2 second timeout; refusals remembered for 5 seconds",[85,905,906,907],{},"Configurable in ",[18,908,20],{},[56,910,912],{"id":911},"settings-only-in-sysconfig","Settings only in sys.config",[14,914,915,916,919,920,922,923,47],{},"These have no environment variable. Change them by editing ",[18,917,918],{},"config/sys.config.src"," and rebuilding the image, or in the ",[18,921,20],{}," of an application that ",[166,924,926],{"href":925},"/docs/self-hosting/plugins#embedding-kraken","embeds kraken",[61,928,929,940],{},[64,930,931],{},[67,932,933,936,938],{},[70,934,935],{},"Key",[70,937,75],{},[70,939,78],{},[80,941,942,960,975,990,1004,1018,1041,1058],{},[67,943,944,949,953],{},[85,945,946],{},[18,947,948],{},"cache_miss_fallback_enabled",[85,950,951],{},[18,952,449],{},[85,954,955,956,959],{},"Ask the auth backend's ",[18,957,958],{},"/check-room-access"," when a subscribe misses the cached grants",[67,961,962,967,972],{},[85,963,964],{},[18,965,966],{},"cache_miss_fallback_timeout_ms",[85,968,969],{},[18,970,971],{},"2000",[85,973,974],{},"Timeout for that check",[67,976,977,982,987],{},[85,978,979],{},[18,980,981],{},"acl_deny_cache_ttl_ms",[85,983,984],{},[18,985,986],{},"5000",[85,988,989],{},"How long a refused check is remembered per actor and address",[67,991,992,997,1001],{},[85,993,994],{},[18,995,996],{},"release_shared_subs_on_close",[85,998,999],{},[18,1000,449],{},[85,1002,1003],{},"Release a closing connection's load-balanced subscriptions. Applies only to persistent sessions with durable delivery on.",[67,1005,1006,1011,1015],{},[85,1007,1008],{},[18,1009,1010],{},"fallback_compat",[85,1012,1013],{},[18,1014,449],{},[85,1016,1017],{},"Wildcard-resolved subscriptions also listen on the topic names used by brokers older than protocol version 2, for rolling upgrades",[67,1019,1020,1031,1035],{},[85,1021,1022,411,1025,411,1028],{},[18,1023,1024],{},"presence_store_backend",[18,1026,1027],{},"wake_backend",[18,1029,1030],{},"delivery_store_backend",[85,1032,1033],{},[18,1034,225],{},[85,1036,1037,1038,47],{},"Plugin slots without an environment variable. See ",[166,1039,249],{"href":1040},"/docs/self-hosting/plugins",[67,1042,1043,1048,1052],{},[85,1044,1045],{},[18,1046,1047],{},"durable_delivery",[85,1049,1050],{},[18,1051,296],{},[85,1053,1054,1055,1057],{},"Must be ",[18,1056,449],{}," for a delivery store backend to take effect",[67,1059,1060,1065,1067],{},[85,1061,1062],{},[18,1063,1064],{},"wake_secret",[85,1066,313],{},[85,1068,1069,1070,1073],{},"Signing secret for the ",[18,1071,1072],{},"kraken_wake_http"," module",[56,1075,1077],{"id":1076},"the-nolagcore-example-host","The @nolag/core example host",[14,1079,1080,1081,1084],{},"The example host in the nolag-core repository reads these. ",[18,1082,1083],{},"CoreModule"," itself reads no environment variables; a host passes it what it needs.",[61,1086,1087,1097],{},[64,1088,1089],{},[67,1090,1091,1093,1095],{},[70,1092,72],{},[70,1094,75],{},[70,1096,78],{},[80,1098,1099,1114,1135,1147,1166,1187,1204,1220,1236,1249,1261],{},[67,1100,1101,1106,1111],{},[85,1102,1103],{},[18,1104,1105],{},"PORT",[85,1107,1108],{},[18,1109,1110],{},"3000",[85,1112,1113],{},"HTTP port",[67,1115,1116,1124,1132],{},[85,1117,1118,411,1121],{},[18,1119,1120],{},"POSTGRES_HOST",[18,1122,1123],{},"POSTGRES_PORT",[85,1125,1126,411,1129],{},[18,1127,1128],{},"localhost",[18,1130,1131],{},"5432",[85,1133,1134],{},"Database address",[67,1136,1137,1142,1144],{},[85,1138,1139],{},[18,1140,1141],{},"POSTGRES_SOCKET_PATH",[85,1143,313],{},[85,1145,1146],{},"A Unix socket directory; takes precedence over host and port",[67,1148,1149,1160,1163],{},[85,1150,1151,411,1154,411,1157],{},[18,1152,1153],{},"POSTGRES_USER",[18,1155,1156],{},"POSTGRES_PASSWORD",[18,1158,1159],{},"POSTGRES_DATABASE",[85,1161,1162],{},"none",[85,1164,1165],{},"Required",[67,1167,1168,1176,1184],{},[85,1169,1170,411,1173],{},[18,1171,1172],{},"POSTGRES_POOL_MIN",[18,1174,1175],{},"POSTGRES_POOL_MAX",[85,1177,1178,411,1181],{},[18,1179,1180],{},"1",[18,1182,1183],{},"10",[85,1185,1186],{},"Connection pool size",[67,1188,1189,1194,1198],{},[85,1190,1191],{},[18,1192,1193],{},"SKIP_MIGRATIONS",[85,1195,1196],{},[18,1197,296],{},[85,1199,1200,1201,1203],{},"Set to ",[18,1202,449],{}," to skip applying core's migrations at startup",[67,1205,1206,1211,1213],{},[85,1207,1208],{},[18,1209,1210],{},"SIGNING_KEY_ENCRYPTION_KEY",[85,1212,313],{},[85,1214,1215,1216,47],{},"32 random bytes, base64. Needed for signing keys and ",[166,1217,1219],{"href":1218},"/docs/client-tokens","client tokens",[67,1221,1222,1227,1229],{},[85,1223,1224],{},[18,1225,1226],{},"CORS_ORIGINS",[85,1228,313],{},[85,1230,1231,1232,1235],{},"Comma-separated origins allowed to call the host from a browser. ",[18,1233,1234],{},"*"," is ignored.",[67,1237,1238,1243,1246],{},[85,1239,1240],{},[18,1241,1242],{},"DEFAULT_MAX_CONNECTIONS",[85,1244,1245],{},"unlimited",[85,1247,1248],{},"Connection limit for projects whose document set no limits",[67,1250,1251,1256,1258],{},[85,1252,1253],{},[18,1254,1255],{},"DEFAULT_MAX_MESSAGE_SIZE_BYTES",[85,1257,1245],{},[85,1259,1260],{},"Passed to kraken, which does not enforce it",[67,1262,1263,1268,1272],{},[85,1264,1265],{},[18,1266,1267],{},"DEFAULT_SESSION_EXPIRY_SECONDS",[85,1269,1270],{},[18,1271,471],{},[85,1273,1274,1275,170,1278,1281],{},"Session expiry for ",[18,1276,1277],{},"agent",[18,1279,1280],{},"orchestrator"," actors",{"title":1283,"searchDepth":1284,"depth":1284,"links":1285},"",2,[1286,1287,1288,1289,1290,1291,1292,1293,1294,1295,1296],{"id":58,"depth":1284,"text":59},{"id":127,"depth":1284,"text":128},{"id":252,"depth":1284,"text":253},{"id":352,"depth":1284,"text":353},{"id":422,"depth":1284,"text":423},{"id":496,"depth":1284,"text":497},{"id":532,"depth":1284,"text":533},{"id":569,"depth":1284,"text":570},{"id":758,"depth":1284,"text":759},{"id":911,"depth":1284,"text":912},{"id":1076,"depth":1284,"text":1077},"Every environment variable kraken v0.9.0 reads, with its default and what it really does, the limits fixed in the code, the settings that live only in sys.config, and the @nolag/core example host's settings.","md",{},true,"/docs/self-hosting/configuration",{"title":5,"description":1297},"docs/self-hosting/configuration","PZ6FLK_RoV3_sOG182nvIqfWkVkDjH_NFotL8yygbMA",1791536074807]