[{"data":1,"prerenderedAt":42211},["ShallowReactive",2],{"docs-\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Fdocker\u002F":3,"docs-nav":340},{"id":4,"title":5,"body":6,"description":328,"extension":329,"layout":330,"meta":331,"navGroup":332,"navOrder":88,"navTitle":5,"navigation":187,"originalPath":333,"path":334,"redirect":330,"seo":335,"stem":336,"updated":337,"version":338,"__hash__":339},"docs\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Fdocker.md","Docker Install",{"type":7,"value":8,"toc":319},"minimark",[9,13,22,27,45,49,103,108,115,159,163,263,267,274,278,315],[10,11,5],"h1",{"id":12},"docker-install",[14,15,16,17,21],"p",{},"Run the Device Agent in a container. Bind-mount your ",[18,19,20],"code",{},"device.yml"," and expose the editor port.",[23,24,26],"h2",{"id":25},"prerequisites","Prerequisites",[28,29,30,34],"ul",{},[31,32,33],"li",{},"Docker or Docker Compose",[31,35,36,37,39,40],{},"A ",[18,38,20],{}," configuration from ",[41,42,44],"a",{"href":43},"\u002Fdocs\u002Fdevice-agent\u002Fregister","Register your Remote Instance",[23,46,48],{"id":47},"docker-run","Docker run",[50,51,56],"pre",{"className":52,"code":53,"language":54,"meta":55,"style":55},"language-bash shiki shiki-themes github-light github-dark","docker run \\\n  --mount type=bind,src=\u002Fpath\u002Fto\u002Fdevice.yml,target=\u002Fopt\u002Fflowfuse-device\u002Fdevice.yml \\\n  -p 1880:1880 \\\n  flowfuse\u002Fdevice-agent:latest\n","bash","",[18,57,58,75,86,97],{"__ignoreMap":55},[59,60,63,67,71],"span",{"class":61,"line":62},"line",1,[59,64,66],{"class":65},"sScJk","docker",[59,68,70],{"class":69},"sZZnC"," run",[59,72,74],{"class":73},"sj4cs"," \\\n",[59,76,78,81,84],{"class":61,"line":77},2,[59,79,80],{"class":73},"  --mount",[59,82,83],{"class":69}," type=bind,src=\u002Fpath\u002Fto\u002Fdevice.yml,target=\u002Fopt\u002Fflowfuse-device\u002Fdevice.yml",[59,85,74],{"class":73},[59,87,89,92,95],{"class":61,"line":88},3,[59,90,91],{"class":73},"  -p",[59,93,94],{"class":69}," 1880:1880",[59,96,74],{"class":73},[59,98,100],{"class":61,"line":99},4,[59,101,102],{"class":69},"  flowfuse\u002Fdevice-agent:latest\n",[104,105,107],"h3",{"id":106},"time-zone","Time zone",[14,109,110,111,114],{},"Set the container time zone using the ",[18,112,113],{},"TZ"," environment variable:",[50,116,118],{"className":52,"code":117,"language":54,"meta":55,"style":55},"docker run \\\n  -e TZ=Europe\u002FLondon \\\n  --mount type=bind,src=\u002Fpath\u002Fto\u002Fdevice.yml,target=\u002Fopt\u002Fflowfuse-device\u002Fdevice.yml \\\n  -p 1880:1880 \\\n  flowfuse\u002Fdevice-agent:latest\n",[18,119,120,128,138,146,154],{"__ignoreMap":55},[59,121,122,124,126],{"class":61,"line":62},[59,123,66],{"class":65},[59,125,70],{"class":69},[59,127,74],{"class":73},[59,129,130,133,136],{"class":61,"line":77},[59,131,132],{"class":73},"  -e",[59,134,135],{"class":69}," TZ=Europe\u002FLondon",[59,137,74],{"class":73},[59,139,140,142,144],{"class":61,"line":88},[59,141,80],{"class":73},[59,143,83],{"class":69},[59,145,74],{"class":73},[59,147,148,150,152],{"class":61,"line":99},[59,149,91],{"class":73},[59,151,94],{"class":69},[59,153,74],{"class":73},[59,155,157],{"class":61,"line":156},5,[59,158,102],{"class":69},[23,160,162],{"id":161},"docker-compose","Docker Compose",[50,164,168],{"className":165,"code":166,"language":167,"meta":55,"style":55},"language-yaml shiki shiki-themes github-light github-dark","version: '3.9'\n\nservices:\n  device:\n    image: flowfuse\u002Fdevice-agent:latest\n    ports:\n      - \"1880:1880\"\n    volumes:\n      - \u002Fpath\u002Fto\u002Fdevice.yml:\u002Fopt\u002Fflowfuse-device\u002Fdevice.yml\n    environment:\n      - TZ=UTC\n","yaml",[18,169,170,183,189,197,204,214,222,231,239,247,255],{"__ignoreMap":55},[59,171,172,176,180],{"class":61,"line":62},[59,173,175],{"class":174},"s9eBZ","version",[59,177,179],{"class":178},"sVt8B",": ",[59,181,182],{"class":69},"'3.9'\n",[59,184,185],{"class":61,"line":77},[59,186,188],{"emptyLinePlaceholder":187},true,"\n",[59,190,191,194],{"class":61,"line":88},[59,192,193],{"class":174},"services",[59,195,196],{"class":178},":\n",[59,198,199,202],{"class":61,"line":99},[59,200,201],{"class":174},"  device",[59,203,196],{"class":178},[59,205,206,209,211],{"class":61,"line":156},[59,207,208],{"class":174},"    image",[59,210,179],{"class":178},[59,212,213],{"class":69},"flowfuse\u002Fdevice-agent:latest\n",[59,215,217,220],{"class":61,"line":216},6,[59,218,219],{"class":174},"    ports",[59,221,196],{"class":178},[59,223,225,228],{"class":61,"line":224},7,[59,226,227],{"class":178},"      - ",[59,229,230],{"class":69},"\"1880:1880\"\n",[59,232,234,237],{"class":61,"line":233},8,[59,235,236],{"class":174},"    volumes",[59,238,196],{"class":178},[59,240,242,244],{"class":61,"line":241},9,[59,243,227],{"class":178},[59,245,246],{"class":69},"\u002Fpath\u002Fto\u002Fdevice.yml:\u002Fopt\u002Fflowfuse-device\u002Fdevice.yml\n",[59,248,250,253],{"class":61,"line":249},10,[59,251,252],{"class":174},"    environment",[59,254,196],{"class":178},[59,256,258,260],{"class":61,"line":257},11,[59,259,227],{"class":178},[59,261,262],{"class":69},"TZ=UTC\n",[23,264,266],{"id":265},"verify","Verify",[14,268,269,270,273],{},"Once running and assigned, access the Node-RED editor at ",[18,271,272],{},"http:\u002F\u002F\u003Cdevice-ip>:1880",".",[23,275,277],{"id":276},"notes","Notes",[28,279,280,287,296],{},[31,281,282,283,286],{},"Device Agent 3.x requires Node.js 18 in the base image; the ",[18,284,285],{},"latest"," tag uses Node.js 18.",[31,288,289,290,293,294,273],{},"For 2.x, use a fixed tag like ",[18,291,292],{},"2.8.0"," instead of ",[18,295,285],{},[31,297,298,299,302,303,306,307,310,311,273],{},"Ensure outbound TCP 443 to ",[18,300,301],{},"app.flowfuse.com"," and ",[18,304,305],{},"mqtt.flowfuse.cloud"," and access to ",[18,308,309],{},"https:\u002F\u002Fregistry.npmjs.com"," unless using a module cache. See ",[41,312,314],{"href":313},"\u002Fdocs\u002Fdevice-agent\u002Frunning#running-with-no-access-to-npmjs.org","Running with no access to npmjs.org",[316,317,318],"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 pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":55,"searchDepth":77,"depth":77,"links":320},[321,322,325,326,327],{"id":25,"depth":77,"text":26},{"id":47,"depth":77,"text":48,"children":323},[324],{"id":106,"depth":88,"text":107},{"id":161,"depth":77,"text":162},{"id":265,"depth":77,"text":266},{"id":276,"depth":77,"text":277},"Run the Device Agent in a container. Bind-mount your device.yml and expose the editor port.","md",null,{},"DeviceAgentInstallation","device-agent\u002Finstall\u002Fdocker.md","\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Fdocker",{"title":5,"description":328},"docs\u002Fdevice-agent\u002Finstall\u002Fdocker","2026-06-10 16:37:19 +0000","2.31.2","qkQIBYAE0dmQN2lFufOyDTFPLWjNoLp1QdGG2HsVnxI",[341,526,543,1283,1297,1404,1479,1836,2895,3810,3857,3957,4058,4075,4664,4679,4877,6209,6647,6836,7062,7330,7346,9038,10035,10194,10310,10429,10493,10647,10872,11026,11224,11455,11626,11805,11854,12171,12187,13321,13539,13554,14755,15261,15470,15543,15866,17026,17534,17634,17716,17732,17804,17931,18571,20268,21012,21193,21348,22754,22979,23523,23768,24862,24970,24983,25072,26945,28416,29739,31294,32608,33345,33528,33543,33630,33802,33827,34113,34553,34592,34684,34749,35337,35426,35714,35975,36287,36488,37123,37268,37675,37777,37884,38070,38176,38190,38504,38650,39163,39282,39364,39586,39673,40694,40821,41675,41926,42001,42114],{"id":342,"title":343,"body":344,"description":351,"extension":329,"layout":330,"meta":519,"navGroup":330,"navOrder":330,"navTitle":520,"navigation":187,"originalPath":521,"path":522,"redirect":330,"seo":523,"stem":524,"updated":337,"version":338,"__hash__":525},"docs\u002Fdocs\u002Fadmin\u002Ffeature-flags.md","Enabling Soft-Launched Features for Specific Teams",{"type":7,"value":345,"toc":509},[346,349,352,356,360,368,383,387,393,397,419,423,430,480,484,496,499,506],[10,347,343],{"id":348},"enabling-soft-launched-features-for-specific-teams",[14,350,351],{},"During a soft launch campaign, new features are gated behind PostHog feature flags\nand enabled on a per-team basis. This guide describes the process for enabling a\nfeature flag for a specific team on FlowFuse Cloud.",[23,353,355],{"id":354},"process","Process",[104,357,359],{"id":358},"_1-change-request","1. Change Request",[14,361,362,363,367],{},"A change request is created in the ",[364,365,366],"strong",{},"CloudProject"," repository. The request must include:",[28,369,370,377],{},[31,371,372,373,376],{},"The ",[364,374,375],{},"internal team ID"," of the team that needs the feature enabled",[31,378,372,379,382],{},[364,380,381],{},"feature"," that needs enabling",[104,384,386],{"id":385},"_2-identify-the-feature-flag-key","2. Identify the Feature Flag Key",[14,388,389,390,273],{},"The developer or admin handling the request should know which PostHog feature flag\nkey gates the requested feature. If unsure, ask in the ",[364,391,392],{},"Slack engineering channel",[104,394,396],{"id":395},"_3-update-the-feature-flag-in-posthog","3. Update the Feature Flag in PostHog",[398,399,400,410,416],"ol",{},[31,401,402,403,406,407],{},"Log into ",[364,404,405],{},"PostHog"," on the ",[364,408,409],{},"production project",[31,411,412,413],{},"In the left-hand menu, navigate to ",[364,414,415],{},"Features > Feature Flags",[31,417,418],{},"Find the relevant feature flag key in the list and open it for editing",[104,420,422],{"id":421},"_4-add-a-release-condition","4. Add a Release Condition",[14,424,425,426,429],{},"Under ",[364,427,428],{},"Release conditions",", add a new condition set for the team:",[398,431,432,435,447,464,474],{},[31,433,434],{},"Click to add a new condition set",[31,436,437,438,441,442,446],{},"Give the condition set a ",[364,439,440],{},"description"," — use the team or customer name for\nreadability (see ",[41,443,445],{"href":444},"#organizing-condition-sets","Organizing Condition Sets"," below)",[31,448,449,450,455,456,455,459],{},"Add a filter: ",[364,451,452],{},[18,453,454],{},"team-id"," — ",[364,457,458],{},"equals",[364,460,461],{},[18,462,463],{},"\u003Cgiven-team-id>",[31,465,466,467,470,471],{},"Set the ",[364,468,469],{},"rollout percentage"," to ",[364,472,473],{},"100%",[31,475,476,479],{},[364,477,478],{},"Save"," the feature flag",[104,481,483],{"id":482},"_5-verify-and-close","5. Verify and Close",[28,485,486,493],{},[31,487,488,489,492],{},"Verify that the feature flag change is reflected on ",[364,490,491],{},"production\u002Fcloud"," — confirm\nthe team now has access to the feature",[31,494,495],{},"Once verified, close the change request in the CloudProject repository",[23,497,445],{"id":498},"organizing-condition-sets",[14,500,501,502,505],{},"When adding release conditions, prefer ",[364,503,504],{},"one condition set per customer",". If a\ncustomer has multiple teams, group them under the same condition set. This approach\nkeeps things readable because each condition set can have a description identifying\nthe customer, avoiding the need to track and correlate raw team IDs.",[14,507,508],{},"If the feature flag already has existing condition sets, follow the pattern that was\nestablished when the flag was first created. Use your judgement on the best approach\n— the goal is to keep the list of conditions manageable and easy to audit.",{"title":55,"searchDepth":77,"depth":77,"links":510},[511,518],{"id":354,"depth":77,"text":355,"children":512},[513,514,515,516,517],{"id":358,"depth":88,"text":359},{"id":385,"depth":88,"text":386},{"id":395,"depth":88,"text":396},{"id":421,"depth":88,"text":422},{"id":482,"depth":88,"text":483},{"id":498,"depth":77,"text":445},{},"Soft Launch Enablement","admin\u002Ffeature-flags.md","\u002Fdocs\u002Fadmin\u002Ffeature-flags",{"title":343,"description":351},"docs\u002Fadmin\u002Ffeature-flags","krGBcH9PldfChz3Ghmp6qJSEWcrhMPJbe-PxokRnq7k",{"id":527,"title":55,"body":528,"description":55,"extension":329,"layout":532,"meta":533,"navGroup":534,"navOrder":99,"navTitle":535,"navigation":187,"originalPath":536,"path":537,"redirect":538,"seo":540,"stem":541,"updated":337,"version":338,"__hash__":542},"docs\u002Fdocs\u002Fadmin\u002Findex.md",{"type":7,"value":529,"toc":530},[],{"title":55,"searchDepth":77,"depth":77,"links":531},[],"redirect",{},"FlowFuse Self-Hosted","Administering FlowFuse","admin\u002FREADME.md","\u002Fdocs\u002Fadmin",{"to":539},"\u002Fdocs\u002Fadmin\u002Fintroduction",{"description":55},"docs\u002Fadmin\u002Findex","4F7rj2iJQdQI28D3uQOV_nWEc7blZRzcLPbrAx71uWU",{"id":544,"title":535,"body":545,"description":55,"extension":329,"layout":330,"meta":1278,"navGroup":330,"navOrder":62,"navTitle":535,"navigation":187,"originalPath":1279,"path":539,"redirect":330,"seo":1280,"stem":1281,"updated":337,"version":338,"__hash__":1282},"docs\u002Fdocs\u002Fadmin\u002Fintroduction.md",{"type":7,"value":546,"toc":1265},[547,550,554,627,630,634,637,642,646,649,720,724,727,730,744,747,751,754,757,761,764,767,772,1081,1085,1088,1091,1094,1102,1105,1109,1115,1118,1121,1141,1145,1150,1153,1162,1165,1169,1172,1180,1183,1221,1225,1232,1236,1244,1247,1251,1254],[10,548,535],{"id":549},"administering-flowfuse",[23,551,553],{"id":552},"getting-started","Getting started",[28,555,556,562,569,579,586,592,598,604,610,616,622],{},[31,557,558],{},[41,559,561],{"href":560},"\u002Fdocs\u002Fcontribute\u002Farchitecture.md","Understanding the FlowFuse Architecture",[31,563,564,568],{},[41,565,567],{"href":566},"\u002Fdocs\u002Finstall","Install\u002FUpgrade"," - requirements, deployment models, installation methods and upgrading",[31,570,571,578],{},[41,572,574,577],{"href":573},"\u002Fdocs\u002Finstall\u002Fconfiguration.md",[18,575,576],{},"flowforge.yml"," configuration"," - base platform configuration, done before you run.",[31,580,581,585],{},[41,582,584],{"href":583},"\u002Fdocs\u002Finstall\u002Ffirst-run.md","First Run Setup"," - create your admin user",[31,587,588],{},[41,589,591],{"href":590},"\u002Fdocs\u002Fuser\u002Fconcepts.md","FlowFuse Concepts",[31,593,594],{},[41,595,597],{"href":596},"\u002Fdocs\u002Fadmin\u002Ftelemetry.md","Usage Telemetry",[31,599,600],{},[41,601,603],{"href":602},"\u002Fdocs\u002Fadmin\u002Fsso\u002F","Single-Sign On",[31,605,606],{},[41,607,609],{"href":608},"\u002Fdocs\u002Fadmin\u002Flicensing.md","Licensing",[31,611,612],{},[41,613,615],{"href":614},"\u002Fdocs\u002Fadmin\u002Fuser_management.md","User management",[31,617,618],{},[41,619,621],{"href":620},"\u002Fdocs\u002Fadmin\u002Fmonitoring.md","Platform Monitoring",[31,623,624],{},[41,625,520],{"href":626},"\u002Fdocs\u002Fadmin\u002Ffeature-flags.md",[23,628,535],{"id":629},"administering-flowfuse-1",[104,631,633],{"id":632},"accessing-the-admin-settings","Accessing the Admin Settings",[14,635,636],{},"The Admin Settings can be accessed from the main menu:",[638,639],"img",{"src":640,"width":641},"\u002Fdocs\u002Fadmin\u002Fimages\u002Fadmin-menu-option.png",300,[104,643,645],{"id":644},"admin-settings","Admin Settings",[14,647,648],{},"The Admin Settings view lets you manage the platform and its users. The\nfollowing settings are available:",[28,650,651,668,684,695,705],{},[31,652,653,656,657,660,661,664,665,667],{},[364,654,655],{},"Allow new users to register on the login screen"," (default: ",[18,658,659],{},"false",")",[662,663],"br",{},"With this option enabled, the platform login page allows visitors to register\nwith the platform.",[662,666],{},"This option is only available if email sending has been enabled.",[31,669,670,656,673,660,675,677,678,680,681,683],{},[364,671,672],{},"Create a personal team for users when they register",[18,674,659],{},[662,676],{},"With this option enabled, the platform will automatically create a Team\nfor the user. This allows them to start creating Node-RED instances straight away.",[662,679],{},"By default, this doesn't happen, which means the user must either manually\ncreate the Team (if that option is enabled), or be invited to an existing\nTeam.",[662,682],{},"When enabled, a choice of what type of team should be created for the user\nis shown.",[31,685,686,689,690,692,693,667],{},[364,687,688],{},"Allow users to reset their password on the login screen"," (default: `false)",[662,691],{},"With this option enabled, a 'forgot your password' link is shown on the login\nscreen, and provides a workflow where a user can reset their password via a\nlink emailed to them.",[662,694],{},[31,696,697,656,700,660,702,704],{},[364,698,699],{},"Allow users to create teams",[18,701,659],{},[662,703],{},"This option allows users to create new Teams on the platform. By default,\nit is not enabled which means all Teams must be created by an Admin.",[31,706,707,656,710,660,712,714,715,717,718,667],{},[364,708,709],{},"Allow users to invite external users to teams",[18,711,659],{},[662,713],{},"This option allows users to invite people to join a Team who are not currently\nregistered users of the platform. It sends an email with an invitation to\nsign-up to the platform and join the Team.",[662,716],{},"By default, this is not enabled - users must be added by an Admin.",[662,719],{},[104,721,723],{"id":722},"managing-users","Managing Users",[14,725,726],{},"The Users page of Admin Settings can be used to manage the user on the platform.",[14,728,729],{},"It can be used to:",[28,731,732,738],{},[31,733,734,735,737],{},"Add new users to the platform.",[662,736],{},"With the 0.1.0 release, the admin sets the new user's password and it is left\nto the admin to share the login details with the user outside of the platform.",[31,739,740,741,743],{},"Edit a user's details.",[662,742],{},"This includes making them an admin - giving them full access to the platform.",[14,745,746],{},"It also provides a list of all pending user invitations, showing who invited whom to which team.",[104,748,750],{"id":749},"managing-teams","Managing Teams",[14,752,753],{},"With the 0.1.0 release, the Teams page just lists the teams on the platform.",[14,755,756],{},"Further team management options will come in later releases.",[104,758,760],{"id":759},"managing-team-types","Managing Team Types",[14,762,763],{},"The Team Types page can be used to manage the Team Types on the platform.",[14,765,766],{},"They determine what features of the platform are available to teams of a given type,\nincluding what Instance Types are available and any limits that should be applied.",[768,769,771],"h4",{"id":770},"team-type-features","Team Type Features",[28,773,774,786,797,808,819,834,846,857,868,879,890,901,909,917,928,939,950,960,971,982,993,1004,1015,1026,1038,1049,1060,1071],{},[31,775,776,779,781,782],{},[364,777,778],{},"Team Library",[662,780],{},"Enables Team Level sharing of flow fragments and examples. More details ",[41,783,785],{"href":784},"\u002Fdocs\u002Fuser\u002Fshared-library","here",[31,787,788,791,793,794],{},[364,789,790],{},"Project Nodes",[662,792],{},"Enables the FlowFuse Project Nodes which allows Instances within a Team to pass messages between them. More details ",[41,795,785],{"href":796},"\u002Fdocs\u002Fuser\u002Fprojectnodes",[31,798,799,802,804,805],{},[364,800,801],{},"Custom NPM Catalogs",[662,803],{},"Allows .npmrc entries to be passed to Hosted and Remote Instances and also custom Node-RED Catalog URLs to be injected. This allows Node-RED nodes to be loaded from 3rd party registries (e.g. npm proxy instances). More details ",[41,806,785],{"href":807},"\u002Fdocs\u002Fuser\u002Finstance-settings#palette",[31,809,810,813,815,816],{},[364,811,812],{},"NPM Packages",[662,814],{},"Allows a Team to upload and then access their own private Node-RED nodes (or other  NodeJS module packages). This can include Subflows packaged using the FlowFuse Subflow Packaging Sidebar (Requires Team Library Feature to be enabled). More details ",[41,817,785],{"href":818},"\u002Fdocs\u002Fuser\u002Fcustom-npm-packages",[31,820,821,824,826,827,833],{},[364,822,823],{},"Certified Nodes",[662,825],{},"Allows the Team access to ",[41,828,832],{"href":829,"rel":830},"https:\u002F\u002Fflowfuse.com\u002Fcertified-nodes\u002F",[831],"nofollow","FlowFuse Certified nodes"," (Requires security token from FlowFuse, only available to Enterprise license holders)",[31,835,836,839,826,841,833],{},[364,837,838],{},"FlowFuse Exclusive Nodes",[662,840],{},[41,842,845],{"href":843,"rel":844},"https:\u002F\u002Fflowfuse.com\u002Fnode-red\u002Fflowfuse\u002F",[831],"FlowFuse Exclusive nodes",[31,847,848,851,853,854],{},[364,849,850],{},"Email Alerts",[662,852],{},"Allows Teams to enable email alerts when an Hosted Instance crashes or is approaching the CPU\u002FMemory limits set for it's Stack (requires email to be enabled). More details ",[41,855,785],{"href":856},"\u002Fdocs\u002Fuser\u002Finstance-settings#alerts",[31,858,859,862,864,865],{},[364,860,861],{},"Protected Instances",[662,863],{},"Allows Hosted Instances to be marked as Protected. This disables Editor access and requires all flows to be updated via a Pipeline. More details ",[41,866,785],{"href":867},"\u002Fdocs\u002Fuser\u002Fdevops-pipelines#protected-instances",[31,869,870,873,875,876],{},[364,871,872],{},"Git Integration",[662,874],{},"This allows a GitHub project to be used as the source or output of a Pipeline. More details ",[41,877,785],{"href":878},"\u002Fdocs\u002Fuser\u002Fdevops-pipelines#git-repository-stage",[31,880,881,884,886,887],{},[364,882,883],{},"API\u002FDebug Length Limits",[662,885],{},"Lets a Team change the size of the max payload a Hosted Instance can accept and also the size messages sent to the Debug Sidebar will be truncated to. More details ",[41,888,785],{"href":889},"\u002Fdocs\u002Fuser\u002Finstance-settings#editor",[31,891,892,895,897,898],{},[364,893,894],{},"Static Assets",[662,896],{},"Allows Hosted Instances to share static files stored in the Instance Persistent Storage space via HTTP. More details ",[41,899,785],{"href":900},"\u002Fdocs\u002Fuser\u002Fstatic-asset-service",[31,902,903,906,908],{},[364,904,905],{},"Instance Resources",[662,907],{},"Enables CPU and Memory historical charts for Hosted and Remote Instances.",[31,910,911,914,916],{},[364,912,913],{},"Application Level RBAC",[662,915],{},"Allows Team level Roles to be applied at an Application level inside the Team.",[31,918,919,922,924,925],{},[364,920,921],{},"Team-based Endpoint Security",[662,923],{},"Allows HTTP endpoints provided by a Hosted or Remote Instance to require a FlowFuse account and membership to the allow access. Also applies to Node-RED Dashboards hosted on the instances. More details ",[41,926,785],{"href":927},"\u002Fdocs\u002Fuser\u002Fhttp-access-tokens",[31,929,930,933,935,936],{},[364,931,932],{},"Device Groups",[662,934],{},"Allows Remote Instances to be placed in groups which can be used as the source or target of a Pipeline stage. More details ",[41,937,785],{"href":938},"\u002Fdocs\u002Fuser\u002Fdevice-groups",[31,940,941,944,946,947],{},[364,942,943],{},"Device Auto Snapshot",[662,945],{},"Creates a snapshot on every deploy when the Remote Instance is in Developer Mode. These snapshots keep a rolling set of last 10 deploys. More details ",[41,948,785],{"href":949},"\u002Fdocs\u002Fuser\u002Fsnapshots",[31,951,952,955,957,958],{},[364,953,954],{},"Instance Auto Snapshot",[662,956],{},"Creates a snapshot on every deploy from a Hosted Instance. These snapshots keep a rolling set of the last 10 deploys. More details ",[41,959,785],{"href":949},[31,961,962,965,967,968],{},[364,963,964],{},"Custom Hostnames",[662,966],{},"Allows Hosted instances to be accessed on a second hostname with an arbitrary domain (Kubernetes only, requires specific configuration). More details ",[41,969,785],{"href":970},"\u002Fdocs\u002Fuser\u002Fcustom-hostnames",[31,972,973,976,978,979],{},[364,974,975],{},"High Availability",[662,977],{},"Allows for 2 copies of Node-RED to be running for a single Hosted Node-RED instance to provide Load Balanced higher throughput and fail over protection (Kubernetes only). More details ",[41,980,785],{"href":981},"\u002Fdocs\u002Fuser\u002Fhigh-availability",[31,983,984,987,989,990],{},[364,985,986],{},"Bill of Materials \u002F Dependencies",[662,988],{},"Provides a Team level view of what Node-RED nodes are installed and what versions are being used and which have newer versions available. More details ",[41,991,785],{"href":992},"\u002Fdocs\u002Fuser\u002Fbill-of-materials",[31,994,995,998,1000,1001],{},[364,996,997],{},"Version History Timeline",[662,999],{},"Enables a graphical representation of Snapshot creation to allow a clearer picture of when they are created. More details ",[41,1002,785],{"href":1003},"\u002Fdocs\u002Fuser\u002Fsnapshots#timeline-view",[31,1005,1006,1009,1011,1012],{},[364,1007,1008],{},"Team Broker",[662,1010],{},"Provides a Team scoped MQTT broker and Asynchronous API documentation generation. More details ",[41,1013,785],{"href":1014},"\u002Fdocs\u002Fuser\u002Fteambroker",[31,1016,1017,1020,1022,1023],{},[364,1018,1019],{},"Tables",[662,1021],{},"Provides a Team scoped shared SQL Relational Database (Requires specific configuration). More details ",[41,1024,785],{"href":1025},"\u002Fdocs\u002Fuser\u002Fff-tables",[31,1027,1028,1031,1033,1034,1037],{},[364,1029,1030],{},"AI Features",[662,1032],{},"Global toggle for all AI functionality within the team. When disabled, all AI features below are unavailable regardless of their individual settings. Requires ",[18,1035,1036],{},"ai.enabled: true"," in the platform configuration.",[31,1039,1040,1043,1045,1046],{},[364,1041,1042],{},"Expert Assistant",[662,1044],{},"Enables the FlowFuse Expert chat assistant for the team. Provides AI-powered support for building and debugging Node-RED flows. Enabled by default (opt-out). Requires the AI Features flag to be enabled. More details ",[41,1047,785],{"href":1048},"\u002Fdocs\u002Fuser\u002Fexpert\u002F",[31,1050,1051,1054,1056,1057],{},[364,1052,1053],{},"Expert Insights",[662,1055],{},"Enables the Insights mode of FlowFuse Expert, allowing users to query live operational data from Node-RED instances via MCP. Enabled by default (opt-out). Requires the AI Features flag to be enabled. More details ",[41,1058,785],{"href":1059},"\u002Fdocs\u002Fuser\u002Fexpert\u002Fchat\u002F",[31,1061,1062,1065,1067,1068],{},[364,1063,1064],{},"Assistant Inline Code Completion",[662,1066],{},"Allows LLM assistance when writing Function nodes. Requires the AI Features flag to be enabled and assistant service configuration. More details ",[41,1069,785],{"href":1070},"\u002Fdocs\u002Fuser\u002Fexpert\u002Fnode-red-embedded-ai\u002F",[31,1072,1073,1076,1078,1079],{},[364,1074,1075],{},"Generated Snapshot Descriptions",[662,1077],{},"Enables AI-generated descriptions of Snapshots. Requires the AI Features flag to be enabled and assistant service configuration. More details ",[41,1080,785],{"href":1070},[104,1082,1084],{"id":1083},"managing-instance-types","Managing Instance Types",[14,1086,1087],{},"The Instance Types page can be used to manage the Instance Types on the platform.",[14,1089,1090],{},"When billing is enabled, an instance type can be associated with a particular\nStripe Product\u002FPrice - allowing each type to have a different monthly price\nassociated with it.",[14,1092,1093],{},"The Instance Types page shows what types are currently active, how many stacks\neach type has been assigned to it, and how many instances have been created of that\ntype.",[14,1095,1096,1097,1101],{},"Whenever a new Instance Type is created, it will need to be manually enabled for\nthe individual ",[41,1098,1100],{"href":1099},"#managing-team-types","Team Types"," before they will be available\nfor teams to use.",[14,1103,1104],{},"Instance Types also control the default Stack (and in turn, the default Node-RED versions) that Instances will run with if there are multiple available Stacks associated with an Instance Type.",[104,1106,1108],{"id":1107},"managing-stacks","Managing Stacks",[1110,1111,1112],"blockquote",{},[14,1113,1114],{},"Admin Settings > Stacks",[14,1116,1117],{},"The Stacks page can be used to manage the Stacks on the platform. It can be used\nto create and edit the stacks on the platform.",[14,1119,1120],{},"For Deployment specific information about working with stacks, refer to the\ndocumentation of your chosen deployment model:",[28,1122,1123,1129,1135],{},[31,1124,1125],{},[41,1126,1128],{"href":1127},"\u002Fdocs\u002Fcontribute\u002Flocal\u002Fstacks.md","Local Stacks",[31,1130,1131],{},[41,1132,1134],{"href":1133},"\u002Fdocs\u002Finstall\u002Fdocker\u002Fstacks.md","Docker Stacks",[31,1136,1137],{},[41,1138,1140],{"href":1139},"\u002Fdocs\u002Finstall\u002Fkubernetes\u002Fstacks.md","Kubernetes Stacks",[768,1142,1144],{"id":1143},"create-stack","Create Stack",[1146,1147,1149],"h5",{"id":1148},"upgrading-stacks","Upgrading Stacks",[14,1151,1152],{},"You can create a new version of an existing stack via the\ndrop-down menu in the stack table. This allows the platform to notify users\nthat an update is available for their instance, allowing them to upgrade the stack\nat their convenience.",[14,1154,1155,1159],{},[638,1156],{"alt":1157,"dataZoomable":55,"src":1158},"Screenshot of FlowFuse showing where admins can create new stacks","\u002Fdocs\u002Fadmin\u002Fimages\u002Fadmin-stacks-create-new-version.png",[1160,1161,1157],"em",{},[14,1163,1164],{},"Node-RED instances that use the old stack will offer the new stack as a one-click upgrade option.",[1146,1166,1168],{"id":1167},"create-new-stack","Create New Stack",[14,1170,1171],{},"Alternatively, click 'Create stack' to create an entirely new stack.",[14,1173,1174,1178],{},[638,1175],{"alt":1176,"dataZoomable":55,"src":1177},"Screenshot of FlowFuse showing the \"Create Stack\" dialog","\u002Fdocs\u002Fadmin\u002Fimages\u002Fadmin-stack-create.png",[1160,1179,1176],{},[14,1181,1182],{},"When prompted for the Node-RED version, the value here depends on the setup you're running:",[28,1184,1185,1205,1214],{},[31,1186,1187,1190,1191,1193,1194,1197,1198,1200,1201,1204],{},[364,1188,1189],{},"Local",": Provide the exact stack name (Node-RED version) that was installed. For example,\nif you ran the script with ",[18,1192,285],{}," and it resulted in ",[18,1195,1196],{},"3.1.9"," being installed, you should enter ",[18,1199,1196],{},".\nThis must match the directory name created in your ",[18,1202,1203],{},"stacks"," directory. If you changed the\ndirectory name for any reason, make sure to use that name.",[31,1206,1207,1210,1211,660],{},[364,1208,1209],{},"Docker",": Support the container image name (",[41,1212,1213],{"href":1133},"docs",[31,1215,1216,1210,1219,660],{},[364,1217,1218],{},"k8s",[41,1220,1213],{"href":1139},[768,1222,1224],{"id":1223},"updating-stacks","Updating Stacks",[14,1226,1227,1228,1231],{},"It is ",[1160,1229,1230],{},"not"," possible to edit a stack that is being used by Instances.",[104,1233,1235],{"id":1234},"managing-templates","Managing Templates",[14,1237,1238,1239,1243],{},"With ",[41,1240,1242],{"href":1241},"\u002Fdocs\u002Fuser\u002Fconcepts\u002F#template","templates"," administrators\ncan apply Node-RED configuration options as default.\nFor these options, the administrator can lock the selected value so users cannot change\nthem, or keep them editable by end-users.",[14,1245,1246],{},"If you edit a template that is being used by an Application Instance, those changes will get\napplied the next time the instance is restarted.",[768,1248,1250],{"id":1249},"disallow-using-nodes","Disallow using nodes",[14,1252,1253],{},"On FlowFuse Cloud, but recommended on self-managed installs on Docker and Kubernetes,\nwhere certain nodes will not work so are excluded from being used by the template.",[14,1255,1256,1257,1260,1261,1264],{},"When adding ",[18,1258,1259],{},"31-tcpin.js,32-udp.js,10-file.js,23-watch.js,90-exec.js"," to the ",[18,1262,1263],{},"Exclude node by filename"," section of a template and locking this value users are prevented from using these.",{"title":55,"searchDepth":77,"depth":77,"links":1266},[1267,1268],{"id":552,"depth":77,"text":553},{"id":629,"depth":77,"text":535,"children":1269},[1270,1271,1272,1273,1274,1275,1276,1277],{"id":632,"depth":88,"text":633},{"id":644,"depth":88,"text":645},{"id":722,"depth":88,"text":723},{"id":749,"depth":88,"text":750},{"id":759,"depth":88,"text":760},{"id":1083,"depth":88,"text":1084},{"id":1107,"depth":88,"text":1108},{"id":1234,"depth":88,"text":1235},{},"admin\u002Fintroduction.md",{"title":535,"description":55},"docs\u002Fadmin\u002Fintroduction","AIS-sfjDSyyKLFV3WShYte7jlePGSlBCpKCH6__UvIw",{"id":1284,"title":609,"body":1285,"description":55,"extension":329,"layout":532,"meta":1289,"navGroup":330,"navOrder":330,"navTitle":609,"navigation":187,"originalPath":1290,"path":1291,"redirect":1292,"seo":1294,"stem":1295,"updated":337,"version":338,"__hash__":1296},"docs\u002Fdocs\u002Fadmin\u002Flicensing.md",{"type":7,"value":1286,"toc":1287},[],{"title":55,"searchDepth":77,"depth":77,"links":1288},[],{},"admin\u002Flicensing.md","\u002Fdocs\u002Fadmin\u002Flicensing",{"to":1293},"\u002Fpricing\u002F",{"description":55},"docs\u002Fadmin\u002Flicensing","Xf9-50ahRzWV1LYYV6XmDajS-t2Ji16eLdRQ7x7etIs",{"id":1298,"title":621,"body":1299,"description":1306,"extension":329,"layout":330,"meta":1397,"navGroup":330,"navOrder":330,"navTitle":1398,"navigation":187,"originalPath":1399,"path":1400,"redirect":330,"seo":1401,"stem":1402,"updated":337,"version":338,"__hash__":1403},"docs\u002Fdocs\u002Fadmin\u002Fmonitoring.md",{"type":7,"value":1300,"toc":1394},[1301,1304,1307,1314,1321,1324,1335,1339,1342,1345,1357,1360,1391],[10,1302,621],{"id":1303},"platform-monitoring",[14,1305,1306],{},"FlowFuse provides an API end-point that can be used to monitor statistical\ninformation about the platform.",[14,1308,1309,1310,1313],{},"The end-point is accessible to any logged-in Admin user at the url (replace ",[18,1311,1312],{},"example.com","\nwith the domain of your FlowFuse instance)",[28,1315,1316],{},[31,1317,1318],{},[18,1319,1320],{},"https:\u002F\u002Fexample.com\u002Fapi\u002Fv1\u002Fadmin\u002Fstats",[14,1322,1323],{},"By default, it returns a JSON object containing key statistics such as the number\nof users, instances, and other information.",[14,1325,1326,1327,1330,1331,1334],{},"If the ",[18,1328,1329],{},"accept"," header of the http request includes ",[18,1332,1333],{},"application\u002Fopenmetrics-text","\nthen the response is formatted as OpenMetrics text. This can be directly consumed\nby tools such as Prometheus.",[23,1336,1338],{"id":1337},"enabling-token-based-access","Enabling token-based access",[14,1340,1341],{},"In Admin Settings there is an option to allow token-based access to platform statistics.",[14,1343,1344],{},"When enabled, the platform will generate an access token that can be used to access\nthe end-point without having a full Admin login. This is useful when configuring\ntools such as Prometheus to monitor the platform.",[398,1346,1347,1350],{},[31,1348,1349],{},"Under Admin Settings -> General, check the 'allow token-based access' option",[31,1351,1352,1353,1356],{},"A dialog is shown containing the token. This is the ",[1160,1354,1355],{},"only"," time the token\nwill be shown - make sure you record its value.",[14,1358,1359],{},"The token should be provided as a bearer token in any http request to the end-point;",[50,1361,1363],{"className":52,"code":1362,"language":54,"meta":55,"style":55},"TOKEN=your_generated_token\ncurl -H 'Authorization: Bearer $TOKEN' https:\u002F\u002Fexample.com\u002Fapi\u002Fv1\u002Fadmin\u002Fstats\n",[18,1364,1365,1377],{"__ignoreMap":55},[59,1366,1367,1370,1374],{"class":61,"line":62},[59,1368,1369],{"class":178},"TOKEN",[59,1371,1373],{"class":1372},"szBVR","=",[59,1375,1376],{"class":69},"your_generated_token\n",[59,1378,1379,1382,1385,1388],{"class":61,"line":77},[59,1380,1381],{"class":65},"curl",[59,1383,1384],{"class":73}," -H",[59,1386,1387],{"class":69}," 'Authorization: Bearer $TOKEN'",[59,1389,1390],{"class":69}," https:\u002F\u002Fexample.com\u002Fapi\u002Fv1\u002Fadmin\u002Fstats\n",[316,1392,1393],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .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":55,"searchDepth":77,"depth":77,"links":1395},[1396],{"id":1337,"depth":77,"text":1338},{},"Monitoring","admin\u002Fmonitoring.md","\u002Fdocs\u002Fadmin\u002Fmonitoring",{"title":621,"description":1306},"docs\u002Fadmin\u002Fmonitoring","gCop5RUZ6KwHJxeGc7XdNS3dhu0vaDa1MPwsl-dynAI",{"id":1405,"title":1406,"body":1407,"description":1416,"extension":329,"layout":330,"meta":1472,"navGroup":330,"navOrder":330,"navTitle":1473,"navigation":187,"originalPath":1474,"path":1475,"redirect":330,"seo":1476,"stem":1477,"updated":337,"version":338,"__hash__":1478},"docs\u002Fdocs\u002Fadmin\u002Fsso\u002Findex.md","Configuring Single Sign-On",{"type":7,"value":1408,"toc":1468},[1409,1412,1417,1420,1423,1426,1429,1432,1436,1439,1442,1450,1454,1457,1460],[10,1410,1406],{"id":1411},"configuring-single-sign-on",[14,1413,1414],{},[1160,1415,1416],{},"This feature is only available on self-hosted Enterprise licensed instances of FlowFuse.",[14,1418,1419],{},"FlowFuse allows users to sign in through their SAML identity provider, such\nas Google Workspace, or using LDAP against a directory service provider.",[14,1421,1422],{},"The platform can be configured with multiple SSO configurations and uses the\nuser's email domain to identify which provider should be used.",[14,1424,1425],{},"Since FlowFuse 2.7, the SSO configuration can be configured to automatically\nregisters when they first sign in.",[14,1427,1428],{},"Otherwise, a user will have to first register on the platform, providing a temporary\npassword in order to create an account. They will then be able to log in via their SSO provider.",[14,1430,1431],{},"By default, admin users can still log in with their original FlowFuse username and password, which ensures they are not locked out if there is a problem with the SSO configuration.",[23,1433,1435],{"id":1434},"saml-sso","SAML SSO",[14,1437,1438],{},"SAML based SSO allows the FlowFuse platform to authenticate users against their\nidentity provider such as Google Workspace.",[14,1440,1441],{},"Once enabled for a particular email domain, regular users on that domain will be\ndirected to the Identity Provider in order to log in. They will no longer be able\nto log in with their local password, nor will they be able to change their email\naddress in User Settings.",[28,1443,1444],{},[31,1445,1446],{},[41,1447,1449],{"href":1448},"\u002Fdocs\u002Fadmin\u002Fsso\u002Fsaml","Configuring SAML SSO",[23,1451,1453],{"id":1452},"ldap-sso","LDAP SSO",[14,1455,1456],{},"LDAP based SSO allows the FlowFuse platform to authenticate users against a directory\nservice provider, such as OpenLDAP.",[14,1458,1459],{},"When logging in, the users credentials are passed to the service provider to verify.",[28,1461,1462],{},[31,1463,1464],{},[41,1465,1467],{"href":1466},"\u002Fdocs\u002Fadmin\u002Fsso\u002Fldap","Configuring LDAP SSO",{"title":55,"searchDepth":77,"depth":77,"links":1469},[1470,1471],{"id":1434,"depth":77,"text":1435},{"id":1452,"depth":77,"text":1453},{},"Configuring Single Sign-On (SSO)","admin\u002Fsso\u002FREADME.md","\u002Fdocs\u002Fadmin\u002Fsso",{"title":1406,"description":1416},"docs\u002Fadmin\u002Fsso\u002Findex","YkTYKUxu4O50wtrUG3e4d-hO4qEjNnWgYADeYD6zO8Y",{"id":1480,"title":1481,"body":1482,"description":1416,"extension":329,"layout":330,"meta":1831,"navGroup":330,"navOrder":330,"navTitle":1453,"navigation":187,"originalPath":1832,"path":1466,"redirect":330,"seo":1833,"stem":1834,"updated":337,"version":338,"__hash__":1835},"docs\u002Fdocs\u002Fadmin\u002Fsso\u002Fldap.md","Configuring LDAP based Single Sign-On",{"type":7,"value":1483,"toc":1820},[1484,1487,1491,1498,1501,1505,1524,1528,1531,1536,1582,1593,1597,1600,1603,1611,1614,1620,1630,1634,1637,1644,1647,1651,1654,1661,1664,1696,1700,1718,1732,1735,1756,1762,1765,1782,1786,1793,1804,1808,1811],[10,1485,1481],{"id":1486},"configuring-ldap-based-single-sign-on",[14,1488,1489],{},[1160,1490,1416],{},[14,1492,1493,1494,1497],{},"The SSO Configurations are managed by the platform Administrator under the\n",[18,1495,1496],{},"Admin Settings > Settings > SSO"," section.",[14,1499,1500],{},"The user must already exist on the FlowFuse platform before they can sign in via SSO.",[104,1502,1504],{"id":1503},"create-a-sso-configuration","Create a SSO Configuration",[398,1506,1507,1515,1518],{},[31,1508,1509,1510,1512],{},"Click 'Create SSO Configuration' to create a new config",[662,1511],{},[638,1513],{"alt":55,"src":1514},"\u002Fdocs\u002Fadmin\u002Fsso\u002Fimages\u002Fcreate-sso-config-ldap.png",[31,1516,1517],{},"Give the configuration a name to help identify it, and provide the email domain\nname this configuration should apply to. Ensure the LDAP option is selected - this\ncannot be changed after the configuration is created.",[31,1519,1520,1521,1523],{},"Click 'Create configuration'",[662,1522],{},"At this point, the configuration has been created and metadata generated for the\nconfiguration, but it is not active.",[104,1525,1527],{"id":1526},"configuration-ldap","Configuration LDAP",[14,1529,1530],{},"The following fields are provided for configuring LDAP. You will need to refer to\nyour LDAP service provider details for the correct values to enter:",[14,1532,1533],{},[638,1534],{"alt":55,"src":1535},"\u002Fdocs\u002Fadmin\u002Fsso\u002Fimages\u002Fedit-sso-config-ldap.png",[28,1537,1538,1544,1550,1556,1562,1568],{},[31,1539,1540,1543],{},[18,1541,1542],{},"Server"," - the address of the LDAP server, including port number.",[31,1545,1546,1549],{},[18,1547,1548],{},"Username"," - the bind DN to use to connect to the server. The user must have\npermission to lookup users in the directory.",[31,1551,1552,1555],{},[18,1553,1554],{},"Password"," - the password to connect to the server with.",[31,1557,1558,1561],{},[18,1559,1560],{},"Base DN"," - the base object under which user searches are performed.",[31,1563,1564,1567],{},[18,1565,1566],{},"User Search Filter"," - the filter used to search for a user. See below for more details.",[31,1569,1570,1573,1574],{},[18,1571,1572],{},"Enable TLS"," - whether to use TLS for the LDAP connection\n",[28,1575,1576],{},[31,1577,1578,1581],{},[18,1579,1580],{},"Verify Server Certificate"," - when TLS is enabled, whether to perform strict certification\nvalidation.",[14,1583,1584,1585,1588,1589,1592],{},"You can save the configuration at any time by clicking the ",[18,1586,1587],{},"Update configuration","\nbutton. The configuration will only be enabled when you tick the ",[18,1590,1591],{},"active"," checkbox\nand save the changes.",[104,1594,1596],{"id":1595},"session-length-overrides","Session Length Overrides",[14,1598,1599],{},"Each SSO configuration can override the platform default Max Session life an Max Session Idle time.",[14,1601,1602],{},"These are Controlled by the \"Custom Session Expiry (hours)\" and \"Custom Session Idle Time (hours)\" respectively.",[14,1604,1605,1609],{},[638,1606],{"alt":1607,"src":1608},"Settings for Custom Session lifetime","\u002Fdocs\u002Fadmin\u002Fsso\u002Fimages\u002Fedit-sso-custom-session.png",[1160,1610,1607],{},[768,1612,1566],{"id":1613},"user-search-filter",[14,1615,1616,1617,273],{},"The search filter is used when checking if a user exists within the directory, using\nthe standard LDAP query notation. The default search filter is ",[18,1618,1619],{},"(uid=${username})",[14,1621,1622,1623,302,1626,1629],{},"The platform will replace ",[18,1624,1625],{},"${username}",[18,1627,1628],{},"${email}"," with the user's details when\nthey attempt to login.",[23,1631,1633],{"id":1632},"creating-new-users","Creating new users",[14,1635,1636],{},"With FlowFuse 2.7, the SSO Configuration now includes an option to automatically\nregister users who sign in via the configuration.",[14,1638,1639,1640,1643],{},"This option is not enabled by default, but can be enabled but selecting the ",[18,1641,1642],{},"Allow Provisioning of New Users on first login","\noption in the SOO configuration.",[14,1645,1646],{},"When creating the user, the platform will use information provided by the LDAP provider\nto create the username. The user will be directed to their settings page where they\ncan modify their user details to their preferred values.",[23,1648,1650],{"id":1649},"managing-team-membership-with-ldap-groups","Managing Team Membership with LDAP Groups",[14,1652,1653],{},"LDAP implementations can also be used to group users",[14,1655,1656,1657,1660],{},"To enable this option, select the ",[18,1658,1659],{},"Manage roles using group assertions"," in the SSO configuration.",[14,1662,1663],{},"The following configuration options should then be set:",[28,1665,1666,1672],{},[31,1667,1668,1671],{},[18,1669,1670],{},"Group DN"," - this is the base DN to be used to search for group membership.",[31,1673,1674,1677,1678],{},[18,1675,1676],{},"Team Scope"," - this determines what teams can be managed using this configuration. There are two options:\n",[28,1679,1680,1686],{},[31,1681,1682,1685],{},[18,1683,1684],{},"Apply to all teams"," - this will allow the SAML groups to manage all teams on the platform. This is\nsuitable for a self-hosted installation of FlowFuse with a single SSO configuration for all users on\nthe platform.",[31,1687,1688,1691,1692,1695],{},[18,1689,1690],{},"Apply to selected teams"," - this will restrict what teams can be managed to the provided list. This\nis suitable for shared-tenancy platforms with multiple SSO configurations for different groups of users,\nsuch as FlowFuse Cloud.\nWhen this option is selected, an additional option is available - ",[18,1693,1694],{},"Allow users to be in other teams",". This\nwill allow users who sign-in via this SSO configuration to be members of teams not in the list above.\nTheir membership of those teams will not be managed by the SSO groups.\nIf that option is disabled, then the user will be removed from any teams not in the list above.",[104,1697,1699],{"id":1698},"ldap-groups-configuration","LDAP Groups configuration",[14,1701,1702,1703,1706,1707,1710,1711,1706,1714,1717],{},"A user's team membership is managed by what groups they are in. When the user logs in, the LDAP provider\nwill be queried for a list of groups they are a member of. This can be either as a ",[18,1704,1705],{},"member"," or ",[18,1708,1709],{},"uniqueMember"," of a ",[18,1712,1713],{},"groupOfNames",[18,1715,1716],{},"groupOfUniqueNames"," respectively.",[14,1719,1720,1721,1724,1725,1728,1729,273],{},"The group name is used to identify a team, using its slug property, and the user's role in the team.\nThe name must take the form ",[18,1722,1723],{},"ff-\u003Cteam>-\u003Crole>",". For example, the group ",[18,1726,1727],{},"ff-development-owner"," will\ncontainer the owners of the team ",[18,1730,1731],{},"development",[14,1733,1734],{},"The valid roles for a user in a team are:",[28,1736,1737,1742,1746,1751],{},[31,1738,1739],{},[18,1740,1741],{},"owner",[31,1743,1744],{},[18,1745,1705],{},[31,1747,1748],{},[18,1749,1750],{},"viewer",[31,1752,1753],{},[18,1754,1755],{},"dashboard",[14,1757,1758,1761],{},[1160,1759,1760],{},"Note",": this uses the team slug property to identify the team. This has been chosen to simplify managing\nthe groups in the LDAP Provider - rather than using the team's id. However, a team's slug can be changed\nby a team owner. Doing so will break the link between the group and the team membership - so should only\nbe done with care.",[14,1763,1764],{},"An optional prefix and suffix can be include in the group name to support LDAP providers that have existing naming policies. The SSO configuration can be configured with the lengths of these values so they will be stripped off before the group name is validated.",[14,1766,1767,1768,1771,1772,1775,1776,1779,1780,273],{},"For example, if an organisation requires all groups to begin with ",[18,1769,1770],{},"acme-org-",", a prefix length of ",[18,1773,1774],{},"9"," can be set and the group ",[18,1777,1778],{},"acme-org-ff-development-owner"," will be handled as ",[18,1781,1727],{},[23,1783,1785],{"id":1784},"managing-admin-users","Managing Admin users",[14,1787,1788,1789,1792],{},"The SSO Configuration can be configured to manage the admin users of the platform by enabling the\n",[18,1790,1791],{},"Manage Admin roles using group assertions"," option. Once enabled, the name of a group can be provided\nthat will be used to identify whether a user is an admin or not.",[14,1794,1795,1796,1799,1800,1803],{},"*",[1160,1797,1798],{},"Note:"," the platform will refuse to remove the admin flag from a user if they are the only admin\non the platform. It is ",[1160,1801,1802],{},"strongly"," recommended to have an admin user on the system that is not\nmanaged via SSO to ensure continued access in case of any issues with the SSO provider.",[23,1805,1807],{"id":1806},"providers","Providers",[14,1809,1810],{},"The following is the node-exhaustive list of the providers that are known to work with FlowFuse LDAP SSO.",[28,1812,1813],{},[31,1814,1815],{},[41,1816,1819],{"href":1817,"rel":1818},"https:\u002F\u002Fwww.openldap.org\u002F",[831],"OpenLDAP",{"title":55,"searchDepth":77,"depth":77,"links":1821},[1822,1823,1824,1825,1826,1829,1830],{"id":1503,"depth":88,"text":1504},{"id":1526,"depth":88,"text":1527},{"id":1595,"depth":88,"text":1596},{"id":1632,"depth":77,"text":1633},{"id":1649,"depth":77,"text":1650,"children":1827},[1828],{"id":1698,"depth":88,"text":1699},{"id":1784,"depth":77,"text":1785},{"id":1806,"depth":77,"text":1807},{},"admin\u002Fsso\u002Fldap.md",{"title":1481,"description":1416},"docs\u002Fadmin\u002Fsso\u002Fldap","H70XWyCahWTriNI4velSkrIdmvX3DD-K59CKVNlV1JA",{"id":1837,"title":1838,"body":1839,"description":1848,"extension":329,"layout":330,"meta":2890,"navGroup":330,"navOrder":330,"navTitle":1435,"navigation":187,"originalPath":2891,"path":1448,"redirect":330,"seo":2892,"stem":2893,"updated":337,"version":338,"__hash__":2894},"docs\u002Fdocs\u002Fadmin\u002Fsso\u002Fsaml.md","Configuring SAML based Single Sign-On",{"type":7,"value":1840,"toc":2866},[1841,1844,1849,1853,1856,1860,1863,1874,1877,1901,1904,1907,1917,1920,1937,1940,1943,1946,1950,1953,1957,1960,1962,1991,1997,2001,2004,2007,2010,2013,2048,2050,2052,2054,2060,2064,2072,2074,2076,2080,2083,2086,2089,2093,2095,2123,2127,2130,2138,2140,2158,2163,2166,2176,2178,2183,2189,2193,2196,2199,2203,2209,2217,2220,2235,2238,2240,2243,2275,2278,2285,2288,2352,2355,2483,2487,2494,2500,2503,2506,2513,2519,2522,2568,2577,2580,2592,2638,2641,2648,2651,2656,2659,2673,2687,2696,2699,2705,2718,2721,2724,2727,2745,2748,2757,2760,2776,2779,2782,2789,2792,2823,2826,2829],[10,1842,1838],{"id":1843},"configuring-saml-based-single-sign-on",[14,1845,1846],{},[1160,1847,1848],{},"This feature is only available on FlowFuse Cloud and self-hosted Enterprise licensed instances of FlowFuse.",[14,1850,1493,1851,1497],{},[18,1852,1496],{},[14,1854,1855],{},"To fully configure SAML SSO, you will need to generate a configuration in FlowFuse,\nprovide some of the generated values to your Identity Provider, and copy back some\nvalues they provide.",[23,1857,1859],{"id":1858},"configuring-sso-on-flowfuse-cloud","Configuring SSO on FlowFuse Cloud",[14,1861,1862],{},"Configuring SSO on FlowFuse Cloud requires co-ordinating tasks between the customer and\nFlowFuse Cloud administrators.",[14,1864,1865],{},[364,1866,1867,1868,1873],{},"All changes must be made via a ",[41,1869,1872],{"href":1870,"rel":1871},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002FCloudProject\u002Fissues\u002Fnew?assignees=&labels=change-request&projects=&template=change-request.yml&title=Change%3A+",[831],"Production Change Request"," in the CloudProject repository - even if you are actioning it directly.",[14,1875,1876],{},"When a customer requests SSO to be setup for their users, we require the following information:",[398,1878,1879,1886,1895,1898],{},[31,1880,1881,1882,1885],{},"Confirm the customer's entitlement for SSO enablement. It is only available to ",[1160,1883,1884],{},"Enterprise"," tier customers.",[31,1887,1888,1889,1894],{},"The email domain that will be covered by the configuration. Note that each SSO configuration can only be applied to a single domain. If a customer has multiple domains, each one will require its own SSO configuration. ",[41,1890,1893],{"href":1891,"rel":1892},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Fissues\u002F5011",[831],"Issue #5011"," has been raised to make this more flexible in the future.",[31,1896,1897],{},"Whether it is SAML or LDAP based SSO",[31,1899,1900],{},"What Identify Provider they are using for their SSO.",[14,1902,1903],{},"Once this information has been provided, create a Change Request issue in the CloudProject repository recording this information.",[14,1905,1906],{},"We can then create a draft SSO configuration in the Admin\u002FSettings\u002FSSO section. The configuration should not be marked as active yet.",[14,1908,1909,1910,302,1913,1916],{},"From the draft configuration, the values of ",[18,1911,1912],{},"ACS URL",[18,1914,1915],{},"Entity ID \u002F Issuer"," can be given to the customer. These values will be required by their Identify Provider. Refer to the provider-specific documentation for how those values get applied.",[14,1918,1919],{},"In return, the customer then needs to provide:",[398,1921,1922,1927,1932],{},[31,1923,1924],{},[18,1925,1926],{},"Identity Provider Single Sign-On URL",[31,1928,1929],{},[18,1930,1931],{},"Identity Provider Issuer ID \u002F URL",[31,1933,1934],{},[18,1935,1936],{},"X.509 Certificate Public Key",[14,1938,1939],{},"Again, refer to the provider-specific documentation for where to find these values as each provider has its own terminology.",[14,1941,1942],{},"These values should be applied to the draft SSO configuration in FlowFuse Cloud. The configuration can then be marked as active.",[14,1944,1945],{},"It is recommended to do this final step whilst on a call with the customer so they can test the setup in real time.",[104,1947,1949],{"id":1948},"common-issues","Common Issues",[14,1951,1952],{},"Aside from navigating the mismatched terminology between services providers, the most common issue we hit is where a login attempt fails and 'Invalid Document Signature' is shown in FlowFuse logs. This is because we expect both the SAML Assertions and Responses to be signed by the public certificate. The default configuration for many providers is to only sign the assertions - check the provider-specific documentation for the appropriate option to enable to address this.",[23,1954,1956],{"id":1955},"configuring-sso","Configuring SSO",[14,1958,1959],{},"The following instructions give more details information on how to setup SSO.",[104,1961,1504],{"id":1503},[398,1963,1964,1971,1974,1983],{},[31,1965,1509,1966,1968],{},[662,1967],{},[638,1969],{"alt":55,"src":1970},"\u002Fdocs\u002Fadmin\u002Fsso\u002Fimages\u002Fcreate-sso-config.png",[31,1972,1973],{},"Give the configuration a name to help identify it, and provide the email domain\nname this configuration should apply to. Ensure the SAML option is selected - this\ncannot be changed after the configuration is created.",[31,1975,1520,1976,1523,1978,1980],{},[662,1977],{},[662,1979],{},[638,1981],{"alt":55,"src":1982},"\u002Fdocs\u002Fadmin\u002Fsso\u002Fimages\u002Fedit-sso-config.png",[31,1984,1985,1986,302,1988,1990],{},"Copy the ",[18,1987,1912],{},[18,1989,1915],{}," values as you will need to configure\nyour Identity Provider with these values.",[14,1992,1584,1993,1588,1995,1592],{},[18,1994,1587],{},[18,1996,1591],{},[104,1998,2000],{"id":1999},"configure-your-identify-provider","Configure your Identify Provider",[14,2002,2003],{},"Every Identity Provider uses slightly different terminology and varies what\ninformation they require and what they provide. This can make it a tricky task\nto complete.",[14,2005,2006],{},"We provide specific guides for the providers we have verified below.",[14,2008,2009],{},"If you have a working configuration for a provider not listed here, please do\nshare the details so we can add them to the list.",[14,2011,2012],{},"The general points are:",[398,2014,2015,2037],{},[31,2016,2017,2018],{},"Your Identity Provider will supply you with some of the following values that\nshould be entered into your FlowFuse SAML SSO Configuration:",[28,2019,2020,2026,2031],{},[31,2021,2022,2025],{},[18,2023,2024],{},"Single Sign-On URL"," - also referred to as 'SAML Endpoint', 'Login URL' or 'IdP SSO URL'.",[31,2027,2028],{},[18,2029,2030],{},"Issuer ID \u002F URL",[31,2032,2033,2036],{},[18,2034,2035],{},"X.509 Certification Public Key"," - the public key of a certificate used to sign\nSAML requests.",[31,2038,2039,2040,2043,2044,2047],{},"Configure the ",[18,2041,2042],{},"NameID"," SAML option to be ",[18,2045,2046],{},"EmailAddress"," and have it return the email\nof the user logging in. This is how FlowFuse will verify they are a known user\non the platform.",[104,2049,1596],{"id":1595},[14,2051,1599],{},[14,2053,1602],{},[14,2055,2056,2058],{},[638,2057],{"alt":1607,"src":1608},[1160,2059,1607],{},[104,2061,2063],{"id":2062},"enable-your-saml-sso-configuration","Enable your SAML SSO Configuration",[14,2065,2066,2067,2069,2070,273],{},"Once you have setup both sides of the configuration you can enable it for use\nby ticking the ",[18,2068,1591],{}," checkbox and clicking ",[18,2071,1587],{},[23,2073,1633],{"id":1632},[14,2075,1636],{},[14,2077,1639,2078,1643],{},[18,2079,1642],{},[14,2081,2082],{},"When creating the user, the platform will use information provided by the SAML provider\nto create the username. The user will be directed to their settings page where they\ncan modify their user details to their preferred values.",[14,2084,2085],{},"## Managing Team Membership with SAML Groups",[14,2087,2088],{},"Some SAML providers allow user group information to be shared as part of the sign-in process.\nWhen properly configured, this can be used to manage what FlowFuse teams a user has access to.",[14,2090,1656,2091,1660],{},[18,2092,1659],{},[14,2094,1663],{},[28,2096,2097,2107],{},[31,2098,2099,2102,2103,2106],{},[18,2100,2101],{},"Group Assertion Name"," - this is used to identify the group membership information in the response\nsent by the Identity Provider. It defaults to ",[18,2104,2105],{},"ff-roles"," but can be customised if the Identify Provider\nrequires it.",[31,2108,2109,1677,2111],{},[18,2110,1676],{},[28,2112,2113,2117],{},[31,2114,2115,1685],{},[18,2116,1684],{},[31,2118,2119,1691,2121,1695],{},[18,2120,1690],{},[18,2122,1694],{},[104,2124,2126],{"id":2125},"saml-groups-configuration","SAML Groups configuration",[14,2128,2129],{},"A user's team membership is managed by what groups they are in. When the user logs in, the SAML provider\nmust be configured to provide a list of groups they are a member of as a SAML assertion.",[14,2131,1720,2132,1724,2134,1728,2136,273],{},[18,2133,1723],{},[18,2135,1727],{},[18,2137,1731],{},[14,2139,1734],{},[28,2141,2142,2146,2150,2154],{},[31,2143,2144],{},[18,2145,1741],{},[31,2147,2148],{},[18,2149,1705],{},[31,2151,2152],{},[18,2153,1750],{},[31,2155,2156],{},[18,2157,1755],{},[14,2159,2160,2162],{},[1160,2161,1760],{},": this uses the team slug property to identify the team. This has been chosen to simplify managing\nthe groups in the SAML Provider - rather than using the team's id. However, a team's slug can be changed\nby a team owner. Doing so will break the link between the group and the team membership - so should only\nbe done with care.",[14,2164,2165],{},"An optional prefix and suffix can be include in the group name to support SAML providers that have existing naming policies. The SSO configuration can be configured with the lengths of these values so they will be stripped off before the group name is validated.",[14,2167,1767,2168,1771,2170,1775,2172,1779,2174,273],{},[18,2169,1770],{},[18,2171,1774],{},[18,2173,1778],{},[18,2175,1727],{},[23,2177,1785],{"id":1784},[14,2179,2180,2181,1792],{},"The SSO Configuration can be configured to managed the admin users of the platform by enabling the\n",[18,2182,1791],{},[14,2184,1795,2185,1799,2187,1803],{},[1160,2186,1798],{},[1160,2188,1802],{},[23,2190,2192],{"id":2191},"direct-sso-login","Direct SSO Login",[14,2194,2195],{},"For Self Hosted users there is an option in the Admin Settings to enable buttons on the login page for each active SAML SSO provider.",[14,2197,2198],{},"These buttons will redirect to the SSO provider rather than requiring users to enter and email address in the username field to select the correct provider.",[23,2200,2202],{"id":2201},"forcing-all-users-to-use-sso","Forcing All Users to Use SSO",[14,2204,2205,2206,273],{},"For self-hosted installations that need to ensure no user can bypass SSO, there is an option in ",[364,2207,2208],{},"Admin Settings > Settings > SSO > Force all logins for non-admin users via a single SAML SSO provider",[14,2210,2211,2215],{},[638,2212],{"alt":2213,"src":2214},"SSO settings page showing the option to force all non-admin users to log in via a single SAML SSO provider","\u002Fdocs\u002Fadmin\u002Fsso\u002Fimages\u002Fforce-sso.png",[1160,2216,2213],{},[14,2218,2219],{},"When this option is enabled:",[28,2221,2222,2225,2228],{},[31,2223,2224],{},"All users are redirected to the configured SSO provider at login, regardless of their email domain",[31,2226,2227],{},"The email and password login form is no longer presented as a fallback option",[31,2229,2230,2231,2234],{},"Admin users can bypass SSO by accessing ",[18,2232,2233],{},"\u002Fadmin"," routes",[14,2236,2237],{},"This is intended for organisations running a single identity provider across the entire platform, where per-domain SSO configuration is not sufficient to cover all users.",[23,2239,1807],{"id":1806},[14,2241,2242],{},"The following is a non-exhaustive list of the providers that are known to work\nwith FlowFuse SAML SSO.",[28,2244,2245,2251,2257,2263,2269],{},[31,2246,2247],{},[41,2248,2250],{"href":2249},"#microsoft-entra","Microsoft Entra",[31,2252,2253],{},[41,2254,2256],{"href":2255},"#google-workspace","Google Workspace",[31,2258,2259],{},[41,2260,2262],{"href":2261},"#onelogin","OneLogin",[31,2264,2265],{},[41,2266,2268],{"href":2267},"#okta","Okta",[31,2270,2271],{},[41,2272,2274],{"href":2273},"#keycloak","Keycloak",[104,2276,2250],{"id":2277},"microsoft-entra",[14,2279,2280,2281,273],{},"Microsoft provide a guide for creating a custom SAML Application ",[41,2282,785],{"href":2283,"rel":2284},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fentra\u002Fidentity\u002Fenterprise-apps\u002Fadd-application-portal",[831],[14,2286,2287],{},"The following tables map the Entra terminology to the FlowFuse settings.",[2289,2290,2291,2304],"table",{},[2292,2293,2294],"thead",{},[2295,2296,2297,2301],"tr",{},[2298,2299,2300],"th",{},"FlowFuse Setting",[2298,2302,2303],{},"Entra Setting",[2305,2306,2307,2319,2330,2341],"tbody",{},[2295,2308,2309,2314],{},[2310,2311,2312],"td",{},[18,2313,1912],{},[2310,2315,2316],{},[18,2317,2318],{},"Reply URL (Assertion Consumer Service URL)",[2295,2320,2321,2325],{},[2310,2322,2323],{},[18,2324,1931],{},[2310,2326,2327],{},[18,2328,2329],{},"Microsoft Entra Identifier",[2295,2331,2332,2336],{},[2310,2333,2334],{},[18,2335,1926],{},[2310,2337,2338],{},[18,2339,2340],{},"Login URL",[2295,2342,2343,2347],{},[2310,2344,2345],{},[18,2346,1936],{},[2310,2348,2349],{},[18,2350,2351],{},"Certificate (Base64)",[14,2353,2354],{},"Follow these steps to properly configure SAML SSO for Microsoft Entra:",[398,2356,2357,2365,2430],{},[31,2358,2359,2360],{},"In FlowFuse:\n",[398,2361,2362],{},[31,2363,2364],{},"Create a draft SSO configuration in FlowFuse with the appropriate email domain",[31,2366,2367,2368],{},"In Entra:\n",[398,2369,2370,2373,2395,2409,2419],{},[31,2371,2372],{},"Create a SAML application - use the guide linked above for more information",[31,2374,2375,2376],{},"Copy the following values from the FlowFuse SSO configuration into the corresponding Entra configuration:\n",[398,2377,2378,2387],{},[31,2379,2380,2381,2384,2385],{},"Set ",[18,2382,2383],{},"Reply URL"," to the value of ",[18,2386,1912],{},[31,2388,2380,2389,2384,2392],{},[18,2390,2391],{},"Identifier (Entity ID)",[18,2393,2394],{},"Entity ID\u002FIssuer",[31,2396,2397,2398,2401,2402,2405,2406,273],{},"Within the ",[18,2399,2400],{},"SAML Signing Certificate"," configuration, the ",[18,2403,2404],{},"Signing Option"," must be set to ",[18,2407,2408],{},"Sign SAML response and assertion",[31,2410,372,2411,2414,2415,2418],{},[18,2412,2413],{},"Unique User Identifier (Name ID)"," claim must be configured to return the value of the ",[18,2416,2417],{},"user.mail"," source attribute.",[31,2420,2421,2422,2425,2426,2429],{},"Download the ",[18,2423,2424],{},"Federation Metadata XML"," file from the ",[18,2427,2428],{},"SAML Certificates"," section of the Entra application.",[31,2431,2359,2432],{},[398,2433,2434],{},[31,2435,2436,2437],{},"From the metadata XML file, copy the follow properties into the FlowFuse SSO configuration:\n",[398,2438,2439,2455,2468],{},[31,2440,2380,2441,2443,2444,2447,2448,2451,2452,273],{},[18,2442,1926],{}," to the value of the ",[18,2445,2446],{},"Location"," attribute of the ",[18,2449,2450],{},"\u003CSingleSignOnService>"," tag. This should look like ",[18,2453,2454],{},"https:\u002F\u002Flogin.microsoftonline.com\u002F\u003Capp-id>\u002Fsaml2",[31,2456,2380,2457,2443,2459,2447,2462,2451,2465],{},[18,2458,1931],{},[18,2460,2461],{},"entityID",[18,2463,2464],{},"\u003CEntityDescriptor>",[18,2466,2467],{},"https:\u002F\u002Fsts.windows.net\u002F\u003Capp-id>\u002F",[31,2469,2380,2470,2443,2472,2475,2476,2478,2479,2482],{},[18,2471,1936],{},[18,2473,2474],{},"\u003Cds:X509Certificate>"," tag. This does ",[1160,2477,1230],{}," need to have the ",[18,2480,2481],{},"-----BEGIN CERTIFICATE-----\u002F-----END CERTIFICATE-----"," wrapper.",[768,2484,2486],{"id":2485},"group-membership-configuration","Group Membership Configuration",[14,2488,2489,2490,2493],{},"By default, when enabled, Entra will share group assertions under the name ",[18,2491,2492],{},"http:\u002F\u002Fschemas.microsoft.com\u002Fws\u002F2008\u002F06\u002Fidentity\u002Fclaims\u002Fgroups"," and provides the groups as a list of object ids.",[14,2495,2496,2497,2499],{},"Either the ",[18,2498,2101],{}," should be set to this name, or Entra configured to use a custom\nassertion name that matches the FlowFuse SSO Configuration value.",[14,2501,2502],{},"Entra must also be configured to return group names rather than object ids.",[104,2504,2256],{"id":2505},"google-workspace",[14,2507,2508,2509,273],{},"Google provide a guide for creating a custom SAML Application ",[41,2510,785],{"href":2511,"rel":2512},"https:\u002F\u002Fsupport.google.com\u002Fa\u002Fanswer\u002F6087519?hl=en",[831],[14,2514,2515,2516,273],{},"Google Workspace only supports HTTPS-based SSO URLs. You cannot use it when developing\nlocally using ",[18,2517,2518],{},"http:\u002F\u002Flocalhost:3000",[14,2520,2521],{},"The following table maps the Google Workspace terminology to the FlowFuse settings.",[2289,2523,2524,2533],{},[2292,2525,2526],{},[2295,2527,2528,2530],{},[2298,2529,2300],{},[2298,2531,2532],{},"Google Workspace Setting",[2305,2534,2535,2546,2557],{},[2295,2536,2537,2541],{},[2310,2538,2539],{},[18,2540,1926],{},[2310,2542,2543],{},[18,2544,2545],{},"SSO URL",[2295,2547,2548,2552],{},[2310,2549,2550],{},[18,2551,1931],{},[2310,2553,2554],{},[18,2555,2556],{},"Entity ID",[2295,2558,2559,2563],{},[2310,2560,2561],{},[18,2562,1936],{},[2310,2564,2565],{},[18,2566,2567],{},"Certificate",[14,2569,2397,2570,2401,2573,2576],{},[18,2571,2572],{},"Service provider details",[18,2574,2575],{},"Signed response"," option must be enabled.",[104,2578,2262],{"id":2579},"onelogin",[14,2581,2582,2583,2588,2589,273],{},"Follow ",[41,2584,2587],{"href":2585,"rel":2586},"https:\u002F\u002Fonelogin.service-now.com\u002Fsupport?id=kb_article&sys_id=93f95543db109700d5505eea4b96198f",[831],"this guide","\nto create a ",[18,2590,2591],{},"OneLogin SAML Test Connector",[2289,2593,2594,2603],{},[2292,2595,2596],{},[2295,2597,2598,2600],{},[2298,2599,2300],{},[2298,2601,2602],{},"OneLogin Setting",[2305,2604,2605,2616,2627],{},[2295,2606,2607,2611],{},[2310,2608,2609],{},[18,2610,1926],{},[2310,2612,2613],{},[18,2614,2615],{},"SAML 2.0 Endpoint (HTTP)",[2295,2617,2618,2622],{},[2310,2619,2620],{},[18,2621,1931],{},[2310,2623,2624],{},[18,2625,2626],{},"Issuer URL",[2295,2628,2629,2633],{},[2310,2630,2631],{},[18,2632,1936],{},[2310,2634,2635],{},[18,2636,2637],{},"X.509 Certificate",[104,2639,2268],{"id":2640},"okta",[14,2642,2643,2644,2647],{},"Within your Okta Admin dashboard, browse the App Integration catalog and add a new\ninstance of the ",[18,2645,2646],{},"SAML Service Provider"," integration.",[14,2649,2650],{},"On the Sign-On Options section, ensure SAML 2.0 is selected. Below that section\nyou will see a notice saying:",[1110,2652,2653],{},[14,2654,2655],{},"SAML 2.0 in not configured until you complete the setup instructions.",[14,2657,2658],{},"Click the 'View setup instructions' button to open the page in a new window.",[14,2660,2661,2662,2665,2666,302,2669,2672],{},"Follow the instructions on that - copying the ",[18,2663,2664],{},"Identity Provider Issuer",",\n",[18,2667,2668],{},"Identity Provider HTTP POST URL",[18,2670,2671],{},"Identity Provider Certificate"," values\ninto the FlowFuse SSO configuration.",[14,2674,2675,2676,2679,2680,302,2683,2686],{},"Back on the Okta SAML Application configuration page, under the ",[18,2677,2678],{},"Advanced Sign-on Settings","\nsection enter the ",[18,2681,2682],{},"Assertion Consumer Service URL",[18,2684,2685],{},"Service Provider Entity Id","\nfrom the FlowFuse SSO configuration.",[14,2688,425,2689,2692,2693,273],{},[18,2690,2691],{},"Credential Details"," section, change the Application username format to ",[18,2694,2695],{},"Email",[768,2697,2486],{"id":2698},"group-membership-configuration-1",[14,2700,2701,2702,273],{},"To configure Okta to return Group assertions, edit the Settings of the SAML Service Provider's\nSAML 2.0 configuration. Expand the 'Attributes' section and add a ",[18,2703,2704],{},"Group Attribute Statement",[14,2706,2707,2708,2710,2711,2713,2714,2717],{},"The name must match the ",[18,2709,2101],{}," in the FlowFuse SSO configuration (default: ",[18,2712,2105],{},").\nYou can optionally add a filter of ",[18,2715,2716],{},"ff-"," so that it only returns groups used by FlowFuse.",[104,2719,2274],{"id":2720},"keycloak",[14,2722,2723],{},"Within your Keycloak Admin Console, create a new Client with the following settings:",[14,2725,2726],{},"Under the General Settings:",[28,2728,2729,2737],{},[31,2730,2380,2731,470,2734],{},[18,2732,2733],{},"Client type",[18,2735,2736],{},"SAML",[31,2738,466,2739,1260,2742,2744],{},[18,2740,2741],{},"Client ID",[18,2743,1915],{}," value from the FlowFuse SSO configuration.",[14,2746,2747],{},"Under the Login Settings:",[28,2749,2750],{},[31,2751,2380,2752,1260,2755,2744],{},[18,2753,2754],{},"Valid redirect URIs",[18,2756,1912],{},[14,2758,2759],{},"Once created and you are shown the full client configuration, make the following additional changes:",[28,2761,2762,2770],{},[31,2763,2380,2764,470,2767],{},[18,2765,2766],{},"Name ID format",[18,2768,2769],{},"email",[31,2771,2772,2773],{},"Under the 'Keys' tab, turn off ",[18,2774,2775],{},"Client signature required",[14,2777,2778],{},"Save the changes.",[14,2780,2781],{},"Next, select the 'Download adapter config' option under the 'Action' dropdown menu. Select the\n'Mod Auth Mellon files' format and click Download.",[14,2783,2784,2785,2788],{},"This will download a zip file. Extract the zip and open the ",[18,2786,2787],{},"idp-metadata.xml"," file in a text editor.",[14,2790,2791],{},"The final task is to copy some of the contents of the XML file into the FlowFuse SSO configuration.",[28,2793,2794,2803,2814],{},[31,2795,2796,2797,2799,2800,2802],{},"Copy the value of the ",[18,2798,2461],{}," attribute into the ",[18,2801,1931],{}," property",[31,2804,2805,2806,2809,2810,2799,2812,2802],{},"Find one of the ",[18,2807,2808],{},"md:SingleSignOnService"," tags and copy the value of its ",[18,2811,2446],{},[18,2813,1926],{},[31,2815,2816,2817,2820,2821,2802],{},"Copy the contents of the ",[18,2818,2819],{},"ds:X509Certificate"," tag into the ",[18,2822,1936],{},[768,2824,2486],{"id":2825},"group-membership-configuration-2",[14,2827,2828],{},"In Keycloak and the Realm setup with FlowFuse as a client:",[28,2830,2831,2834,2839,2842,2845,2848,2854,2857,2860,2863],{},[31,2832,2833],{},"Create a new \"Client Scope\"",[31,2835,2836,2837],{},"Give it a name and ensure the \"Protocol\" is ",[18,2838,2736],{},[31,2840,2841],{},"After saving the scope, select the \"Mappers\" tab",[31,2843,2844],{},"\"Add mapper\" and pick \"By configuration\"",[31,2846,2847],{},"Select \"Group list\" from the options",[31,2849,2850,2851,2853],{},"Give it a name and set \"Group attribute name\" to ",[18,2852,2105],{}," (this must match the value configured in FlowFuse, default 'ff-roles')",[31,2855,2856],{},"Enable 'Single Group Attribute'",[31,2858,2859],{},"Ensure that \"Full group path\" is unchecked",[31,2861,2862],{},"Save and return to the \"Clients\" list and select your FlowFuse Client created earlier",[31,2864,2865],{},"Under \"Client scopes\", use the \"Add client scope\" button to add the new scope",{"title":55,"searchDepth":77,"depth":77,"links":2867},[2868,2871,2877,2880,2881,2882,2883],{"id":1858,"depth":77,"text":1859,"children":2869},[2870],{"id":1948,"depth":88,"text":1949},{"id":1955,"depth":77,"text":1956,"children":2872},[2873,2874,2875,2876],{"id":1503,"depth":88,"text":1504},{"id":1999,"depth":88,"text":2000},{"id":1595,"depth":88,"text":1596},{"id":2062,"depth":88,"text":2063},{"id":1632,"depth":77,"text":1633,"children":2878},[2879],{"id":2125,"depth":88,"text":2126},{"id":1784,"depth":77,"text":1785},{"id":2191,"depth":77,"text":2192},{"id":2201,"depth":77,"text":2202},{"id":1806,"depth":77,"text":1807,"children":2884},[2885,2886,2887,2888,2889],{"id":2277,"depth":88,"text":2250},{"id":2505,"depth":88,"text":2256},{"id":2579,"depth":88,"text":2262},{"id":2640,"depth":88,"text":2268},{"id":2720,"depth":88,"text":2274},{},"admin\u002Fsso\u002Fsaml.md",{"title":1838,"description":1848},"docs\u002Fadmin\u002Fsso\u002Fsaml","bSmyPeQmdJSnfQwuBvROzdw1bRJi1tXpOlu7-Qpj6mA",{"id":2896,"title":597,"body":2897,"description":2904,"extension":329,"layout":330,"meta":3803,"navGroup":330,"navOrder":330,"navTitle":3804,"navigation":187,"originalPath":3805,"path":3806,"redirect":330,"seo":3807,"stem":3808,"updated":337,"version":338,"__hash__":3809},"docs\u002Fdocs\u002Fadmin\u002Ftelemetry.md",{"type":7,"value":2898,"toc":3793},[2899,2902,2905,2908,2911,2919,2923,2927,2930,2936,2941,2963,2967,2970,3337,3451,3458,3462,3465,3473,3479,3483,3486,3489,3530,3533,3631,3746,3757,3761,3764,3790],[10,2900,597],{"id":2901},"usage-telemetry",[14,2903,2904],{},"The platform shares anonymous usage information with us at FlowFuse. This helps\nus understand how the platform is being used, what areas need improvement\nand how we should prioritise future work.",[14,2906,2907],{},"Ultimately, it helps us produce a better platform for all its users.",[14,2909,2910],{},"We do not collect:",[28,2912,2913,2916],{},[31,2914,2915],{},"Any personally identifiable information. We do store a secure hash of the sending IP address - but the plain value is never stored",[31,2917,2918],{},"Any specific details of the flows running on the platform.",[23,2920,2922],{"id":2921},"core-telemetry","Core Telemetry",[104,2924,2926],{"id":2925},"configuring-telemetry","Configuring Telemetry",[14,2928,2929],{},"By default, usage telemetry is enabled on the platform. The administrator can\nopt-out of sharing information as part of the initial setup, or through the Admin\nSettings section of the platform UI.",[14,2931,2932,2933,2935],{},"It is also possible to disable in the ",[18,2934,576],{}," configuration file. This\noverrides whatever option is set in the Admin Settings UI.",[14,2937,2938],{},[364,2939,2940],{},"IMPORTANT: Licensed installations cannot disable telemetry",[50,2942,2944],{"className":165,"code":2943,"language":167,"meta":55,"style":55},"telemetry:\n  enabled: false\n",[18,2945,2946,2953],{"__ignoreMap":55},[59,2947,2948,2951],{"class":61,"line":62},[59,2949,2950],{"class":174},"telemetry",[59,2952,196],{"class":178},[59,2954,2955,2958,2960],{"class":61,"line":77},[59,2956,2957],{"class":174},"  enabled",[59,2959,179],{"class":178},[59,2961,2962],{"class":73},"false\n",[104,2964,2966],{"id":2965},"collected-data","Collected Data",[14,2968,2969],{},"The following pieces of information are included in the telemetry sent back to us:",[50,2971,2975],{"className":2972,"code":2973,"language":2974,"meta":55,"style":55},"language-json shiki shiki-themes github-light github-dark","{\n  \"instanceId\": \"5db51f99-c6fb-4340-9c19-78adce58cc1b\",\n  \"os\": { \"type\": \"Darwin\", \"release\": \"20.5.0\", \"arch\": \"x64\" },\n  \"env\": { \"nodejs\": \"v16.19.1\", \"flowforge\": \"1.0.0\" },\n  \"platform\": {\n    \"counts\": {\n      \"users\": 6,\n      \"teams\": 5,\n      \"projects\": 4,\n      \"devices\": 9,\n      \"projectSnapshots\": 16,\n      \"projectTemplates\": 7,\n      \"projectStacks\": 2,\n      \"libraryEntries\": 0,\n      \"sharedLibraryEntries\": 2\n    },\n    \"config\": {\n      \"driver\": \"localfs\",\n      \"broker\": {\n        \"enabled\": true\n      },\n      \"fileStore\": {\n        \"enabled\": true\n      },\n      \"email\": {\n        \"enabled\": false\n      }\n    },\n    \"license\": {\n      \"id\": \"4c105579-782b-4d53-af62-cf7fa69f6b43\",\n      \"type\": \"DEV\"\n    }\n  }\n}\n","json",[18,2976,2977,2982,2994,3034,3061,3069,3076,3088,3100,3112,3123,3135,3148,3161,3174,3185,3191,3199,3212,3220,3231,3237,3245,3254,3259,3267,3276,3282,3287,3295,3308,3319,3325,3331],{"__ignoreMap":55},[59,2978,2979],{"class":61,"line":62},[59,2980,2981],{"class":178},"{\n",[59,2983,2984,2987,2989,2992],{"class":61,"line":77},[59,2985,2986],{"class":73},"  \"instanceId\"",[59,2988,179],{"class":178},[59,2990,2991],{"class":69},"\"5db51f99-c6fb-4340-9c19-78adce58cc1b\"",[59,2993,2665],{"class":178},[59,2995,2996,2999,3002,3005,3007,3010,3013,3016,3018,3021,3023,3026,3028,3031],{"class":61,"line":88},[59,2997,2998],{"class":73},"  \"os\"",[59,3000,3001],{"class":178},": { ",[59,3003,3004],{"class":73},"\"type\"",[59,3006,179],{"class":178},[59,3008,3009],{"class":69},"\"Darwin\"",[59,3011,3012],{"class":178},", ",[59,3014,3015],{"class":73},"\"release\"",[59,3017,179],{"class":178},[59,3019,3020],{"class":69},"\"20.5.0\"",[59,3022,3012],{"class":178},[59,3024,3025],{"class":73},"\"arch\"",[59,3027,179],{"class":178},[59,3029,3030],{"class":69},"\"x64\"",[59,3032,3033],{"class":178}," },\n",[59,3035,3036,3039,3041,3044,3046,3049,3051,3054,3056,3059],{"class":61,"line":99},[59,3037,3038],{"class":73},"  \"env\"",[59,3040,3001],{"class":178},[59,3042,3043],{"class":73},"\"nodejs\"",[59,3045,179],{"class":178},[59,3047,3048],{"class":69},"\"v16.19.1\"",[59,3050,3012],{"class":178},[59,3052,3053],{"class":73},"\"flowforge\"",[59,3055,179],{"class":178},[59,3057,3058],{"class":69},"\"1.0.0\"",[59,3060,3033],{"class":178},[59,3062,3063,3066],{"class":61,"line":156},[59,3064,3065],{"class":73},"  \"platform\"",[59,3067,3068],{"class":178},": {\n",[59,3070,3071,3074],{"class":61,"line":216},[59,3072,3073],{"class":73},"    \"counts\"",[59,3075,3068],{"class":178},[59,3077,3078,3081,3083,3086],{"class":61,"line":224},[59,3079,3080],{"class":73},"      \"users\"",[59,3082,179],{"class":178},[59,3084,3085],{"class":73},"6",[59,3087,2665],{"class":178},[59,3089,3090,3093,3095,3098],{"class":61,"line":233},[59,3091,3092],{"class":73},"      \"teams\"",[59,3094,179],{"class":178},[59,3096,3097],{"class":73},"5",[59,3099,2665],{"class":178},[59,3101,3102,3105,3107,3110],{"class":61,"line":241},[59,3103,3104],{"class":73},"      \"projects\"",[59,3106,179],{"class":178},[59,3108,3109],{"class":73},"4",[59,3111,2665],{"class":178},[59,3113,3114,3117,3119,3121],{"class":61,"line":249},[59,3115,3116],{"class":73},"      \"devices\"",[59,3118,179],{"class":178},[59,3120,1774],{"class":73},[59,3122,2665],{"class":178},[59,3124,3125,3128,3130,3133],{"class":61,"line":257},[59,3126,3127],{"class":73},"      \"projectSnapshots\"",[59,3129,179],{"class":178},[59,3131,3132],{"class":73},"16",[59,3134,2665],{"class":178},[59,3136,3138,3141,3143,3146],{"class":61,"line":3137},12,[59,3139,3140],{"class":73},"      \"projectTemplates\"",[59,3142,179],{"class":178},[59,3144,3145],{"class":73},"7",[59,3147,2665],{"class":178},[59,3149,3151,3154,3156,3159],{"class":61,"line":3150},13,[59,3152,3153],{"class":73},"      \"projectStacks\"",[59,3155,179],{"class":178},[59,3157,3158],{"class":73},"2",[59,3160,2665],{"class":178},[59,3162,3164,3167,3169,3172],{"class":61,"line":3163},14,[59,3165,3166],{"class":73},"      \"libraryEntries\"",[59,3168,179],{"class":178},[59,3170,3171],{"class":73},"0",[59,3173,2665],{"class":178},[59,3175,3177,3180,3182],{"class":61,"line":3176},15,[59,3178,3179],{"class":73},"      \"sharedLibraryEntries\"",[59,3181,179],{"class":178},[59,3183,3184],{"class":73},"2\n",[59,3186,3188],{"class":61,"line":3187},16,[59,3189,3190],{"class":178},"    },\n",[59,3192,3194,3197],{"class":61,"line":3193},17,[59,3195,3196],{"class":73},"    \"config\"",[59,3198,3068],{"class":178},[59,3200,3202,3205,3207,3210],{"class":61,"line":3201},18,[59,3203,3204],{"class":73},"      \"driver\"",[59,3206,179],{"class":178},[59,3208,3209],{"class":69},"\"localfs\"",[59,3211,2665],{"class":178},[59,3213,3215,3218],{"class":61,"line":3214},19,[59,3216,3217],{"class":73},"      \"broker\"",[59,3219,3068],{"class":178},[59,3221,3223,3226,3228],{"class":61,"line":3222},20,[59,3224,3225],{"class":73},"        \"enabled\"",[59,3227,179],{"class":178},[59,3229,3230],{"class":73},"true\n",[59,3232,3234],{"class":61,"line":3233},21,[59,3235,3236],{"class":178},"      },\n",[59,3238,3240,3243],{"class":61,"line":3239},22,[59,3241,3242],{"class":73},"      \"fileStore\"",[59,3244,3068],{"class":178},[59,3246,3248,3250,3252],{"class":61,"line":3247},23,[59,3249,3225],{"class":73},[59,3251,179],{"class":178},[59,3253,3230],{"class":73},[59,3255,3257],{"class":61,"line":3256},24,[59,3258,3236],{"class":178},[59,3260,3262,3265],{"class":61,"line":3261},25,[59,3263,3264],{"class":73},"      \"email\"",[59,3266,3068],{"class":178},[59,3268,3270,3272,3274],{"class":61,"line":3269},26,[59,3271,3225],{"class":73},[59,3273,179],{"class":178},[59,3275,2962],{"class":73},[59,3277,3279],{"class":61,"line":3278},27,[59,3280,3281],{"class":178},"      }\n",[59,3283,3285],{"class":61,"line":3284},28,[59,3286,3190],{"class":178},[59,3288,3290,3293],{"class":61,"line":3289},29,[59,3291,3292],{"class":73},"    \"license\"",[59,3294,3068],{"class":178},[59,3296,3298,3301,3303,3306],{"class":61,"line":3297},30,[59,3299,3300],{"class":73},"      \"id\"",[59,3302,179],{"class":178},[59,3304,3305],{"class":69},"\"4c105579-782b-4d53-af62-cf7fa69f6b43\"",[59,3307,2665],{"class":178},[59,3309,3311,3314,3316],{"class":61,"line":3310},31,[59,3312,3313],{"class":73},"      \"type\"",[59,3315,179],{"class":178},[59,3317,3318],{"class":69},"\"DEV\"\n",[59,3320,3322],{"class":61,"line":3321},32,[59,3323,3324],{"class":178},"    }\n",[59,3326,3328],{"class":61,"line":3327},33,[59,3329,3330],{"class":178},"  }\n",[59,3332,3334],{"class":61,"line":3333},34,[59,3335,3336],{"class":178},"}\n",[2289,3338,3339,3349],{},[2292,3340,3341],{},[2295,3342,3343,3346],{},[2298,3344,3345],{},"Property",[2298,3347,3348],{},"Description",[2305,3350,3351,3361,3371,3381,3391,3401,3411,3421,3431,3441],{},[2295,3352,3353,3358],{},[2310,3354,3355],{},[18,3356,3357],{},"instanceId",[2310,3359,3360],{},"A unique identifier for the FlowFuse instance.",[2295,3362,3363,3368],{},[2310,3364,3365],{},[18,3366,3367],{},"os",[2310,3369,3370],{},"Information about the operating system",[2295,3372,3373,3378],{},[2310,3374,3375],{},[18,3376,3377],{},"env",[2310,3379,3380],{},"Node.js and FlowFuse versions",[2295,3382,3383,3388],{},[2310,3384,3385],{},[18,3386,3387],{},"platform.counts",[2310,3389,3390],{},"A snapshot of the number of users, teams, projects (instances), etc, in use on the platform.",[2295,3392,3393,3398],{},[2310,3394,3395],{},[18,3396,3397],{},"platform.config.driver",[2310,3399,3400],{},"Which backend driver is being used",[2295,3402,3403,3408],{},[2310,3404,3405],{},[18,3406,3407],{},"platform.config.broker.enabled",[2310,3409,3410],{},"A flag indicating whether the the internal comms broker is enabled",[2295,3412,3413,3418],{},[2310,3414,3415],{},[18,3416,3417],{},"platform.config.fileStore.enabled",[2310,3419,3420],{},"A flag indicating whether the file store is enabled",[2295,3422,3423,3428],{},[2310,3424,3425],{},[18,3426,3427],{},"platform.config.email.enabled",[2310,3429,3430],{},"A flag indicating whether email is enabled",[2295,3432,3433,3438],{},[2310,3434,3435],{},[18,3436,3437],{},"platform.license.id",[2310,3439,3440],{},"The ID of the license loaded",[2295,3442,3443,3448],{},[2310,3444,3445],{},[18,3446,3447],{},"platform.license.type",[2310,3449,3450],{},"The type of the license",[14,3452,3453,3454,3457],{},"When the data is collected, we also store the timestamp the data was received and\na ",[364,3455,3456],{},"hash"," of the sending IP address - we do not store the plain value.",[104,3459,3461],{"id":3460},"schedule","Schedule",[14,3463,3464],{},"For the core tracking, the platform will send the telemetry data:",[28,3466,3467,3470],{},[31,3468,3469],{},"30 seconds after the platform starts up (but only if the platform has already been initialised)",[31,3471,3472],{},"Once every 24 hours at a time randomly picked when the platform starts",[14,3474,3475,3476,273],{},"The data is sent via an HTTP Post to ",[18,3477,3478],{},"https:\u002F\u002Fping.flowforge.com",[23,3480,3482],{"id":3481},"frontend-telemetry","Frontend Telemetry",[14,3484,3485],{},"The FlowFuse UI can be configured to track usage to help understand how users are navigating the pages.",[14,3487,3488],{},"It supports integration with two different services:",[28,3490,3491,3506,3516],{},[31,3492,3493,3497,3498,3501,3502,3505],{},[41,3494,405],{"href":3495,"rel":3496},"https:\u002F\u002Fposthog.com\u002F",[831]," ",[1160,3499,3500],{},"(recommended)",": You will require your own API key to pass into the ",[18,3503,3504],{},"yml",", which will begin the logging of user interactions.",[31,3507,3508,3497,3513,3515],{},[41,3509,3512],{"href":3510,"rel":3511},"https:\u002F\u002Fsentry.io\u002F",[831],"Sentry",[1160,3514,3500],{},": You will need to specify your Sentry DSN for the frontend and back-end",[31,3517,3518,179,3523,3526,3527,3529],{},[41,3519,3522],{"href":3520,"rel":3521},"https:\u002F\u002Fplausible.io\u002F",[831],"Plausible",[1160,3524,3525],{},"(deprecated since 0.9 and will be removed in the future)",": You can setup your own account, and pass the relevant domain to the ",[18,3528,3504],{}," in the telemetry configuration. As this option is deprecated, details of how to configure are no longer provided.",[104,3531,2926],{"id":3532},"configuring-telemetry-1",[2289,3534,3535,3544],{},[2292,3536,3537],{},[2295,3538,3539,3542],{},[2298,3540,3541],{},"Option",[2298,3543,3348],{},[2305,3545,3546,3559,3572,3584,3608,3619],{},[2295,3547,3548,3553],{},[2310,3549,3550],{},[18,3551,3552],{},"telemetry.enabled",[2310,3554,3555,3556],{},"Enables the anonymous usage telemetry of the platform. Default: ",[18,3557,3558],{},"true",[2295,3560,3561,3566],{},[2310,3562,3563],{},[18,3564,3565],{},"telemetry.backend.sentry.dsn",[2310,3567,3568,3569],{},"The API key provided to you from your own sentry account. Default: ",[18,3570,3571],{},"null",[2295,3573,3574,3579],{},[2310,3575,3576],{},[18,3577,3578],{},"telemetry.frontend.posthog.apikey",[2310,3580,3581,3582],{},"The API key provided to you from your own PostHog account. Default: ",[18,3583,3571],{},[2295,3585,3586,3591],{},[2310,3587,3588],{},[18,3589,3590],{},"telemetry.frontend.posthog.capture_pageview",[2310,3592,3593,3594,3597,3598,3601,3602,3605,3606],{},"FlowFuse is designed as to provide custom posthog ",[18,3595,3596],{},"$pageview"," events that provide more detail on navigation than the default, and suit a single page application better. As such, we recommend setting this to false in order to prevent duplicate ",[18,3599,3600],{},"pageleave","\u002F",[18,3603,3604],{},"pageview"," events firing. Default: ",[18,3607,3558],{},[2295,3609,3610,3615],{},[2310,3611,3612],{},[18,3613,3614],{},"telemetry.frontend.sentry.dsn",[2310,3616,3568,3617],{},[18,3618,3571],{},[2295,3620,3621,3626],{},[2310,3622,3623],{},[18,3624,3625],{},"telemetry.frontend.sentry.production_mode",[2310,3627,3628,3629],{},"Should this instance be treated as production (lower session count recorded). Default: ",[18,3630,659],{},[50,3632,3634],{"className":165,"code":3633,"language":167,"meta":55,"style":55},"telemetry:\n  enabled: true\n  frontend:\n    posthog:\n      apikey: \u003Capi-key>\n      capture_pageview: false\n    sentry:\n      dsn: \u003Cdsn-key>\n      production_mode: true\n  backend:\n    sentry:\n      dsn: \u003Cdsn-key>\n    prometheus:\n      enabled: true\n",[18,3635,3636,3642,3650,3657,3664,3674,3683,3690,3700,3709,3716,3722,3730,3737],{"__ignoreMap":55},[59,3637,3638,3640],{"class":61,"line":62},[59,3639,2950],{"class":174},[59,3641,196],{"class":178},[59,3643,3644,3646,3648],{"class":61,"line":77},[59,3645,2957],{"class":174},[59,3647,179],{"class":178},[59,3649,3230],{"class":73},[59,3651,3652,3655],{"class":61,"line":88},[59,3653,3654],{"class":174},"  frontend",[59,3656,196],{"class":178},[59,3658,3659,3662],{"class":61,"line":99},[59,3660,3661],{"class":174},"    posthog",[59,3663,196],{"class":178},[59,3665,3666,3669,3671],{"class":61,"line":156},[59,3667,3668],{"class":174},"      apikey",[59,3670,179],{"class":178},[59,3672,3673],{"class":69},"\u003Capi-key>\n",[59,3675,3676,3679,3681],{"class":61,"line":216},[59,3677,3678],{"class":174},"      capture_pageview",[59,3680,179],{"class":178},[59,3682,2962],{"class":73},[59,3684,3685,3688],{"class":61,"line":224},[59,3686,3687],{"class":174},"    sentry",[59,3689,196],{"class":178},[59,3691,3692,3695,3697],{"class":61,"line":233},[59,3693,3694],{"class":174},"      dsn",[59,3696,179],{"class":178},[59,3698,3699],{"class":69},"\u003Cdsn-key>\n",[59,3701,3702,3705,3707],{"class":61,"line":241},[59,3703,3704],{"class":174},"      production_mode",[59,3706,179],{"class":178},[59,3708,3230],{"class":73},[59,3710,3711,3714],{"class":61,"line":249},[59,3712,3713],{"class":174},"  backend",[59,3715,196],{"class":178},[59,3717,3718,3720],{"class":61,"line":257},[59,3719,3687],{"class":174},[59,3721,196],{"class":178},[59,3723,3724,3726,3728],{"class":61,"line":3137},[59,3725,3694],{"class":174},[59,3727,179],{"class":178},[59,3729,3699],{"class":69},[59,3731,3732,3735],{"class":61,"line":3150},[59,3733,3734],{"class":174},"    prometheus",[59,3736,196],{"class":178},[59,3738,3739,3742,3744],{"class":61,"line":3163},[59,3740,3741],{"class":174},"      enabled",[59,3743,179],{"class":178},[59,3745,3230],{"class":73},[14,3747,3748,3749,3752,3753,3756],{},"Sentry reads the environment variable ",[18,3750,3751],{},"SENTRY_ENV",", falling back to ",[18,3754,3755],{},"NODE_ENV"," to set the environment for both frontend and backend.",[768,3758,3760],{"id":3759},"telemetry-during-build","Telemetry During Build",[14,3762,3763],{},"Configure .env with the auth token, org, and project name for the frontend project.",[50,3765,3767],{"className":165,"code":3766,"language":167,"meta":55,"style":55},"# Used for BUILD time sentry reporting\nSENTRY_AUTH_TOKEN=\nSENTRY_ORG=\nSENTRY_PROJECT=\n",[18,3768,3769,3775,3780,3785],{"__ignoreMap":55},[59,3770,3771],{"class":61,"line":62},[59,3772,3774],{"class":3773},"sJ8bj","# Used for BUILD time sentry reporting\n",[59,3776,3777],{"class":61,"line":77},[59,3778,3779],{"class":69},"SENTRY_AUTH_TOKEN=\n",[59,3781,3782],{"class":61,"line":88},[59,3783,3784],{"class":69},"SENTRY_ORG=\n",[59,3786,3787],{"class":61,"line":99},[59,3788,3789],{"class":69},"SENTRY_PROJECT=\n",[316,3791,3792],{},"html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":55,"searchDepth":77,"depth":77,"links":3794},[3795,3800],{"id":2921,"depth":77,"text":2922,"children":3796},[3797,3798,3799],{"id":2925,"depth":88,"text":2926},{"id":2965,"depth":88,"text":2966},{"id":3460,"depth":88,"text":3461},{"id":3481,"depth":77,"text":3482,"children":3801},[3802],{"id":3532,"depth":88,"text":2926},{},"Telemetry","admin\u002Ftelemetry.md","\u002Fdocs\u002Fadmin\u002Ftelemetry",{"title":597,"description":2904},"docs\u002Fadmin\u002Ftelemetry","owmEctRkjXp0FwNoGhC8CiXXVTPTaMIiVQFHotJtCgM",{"id":3811,"title":615,"body":3812,"description":55,"extension":329,"layout":330,"meta":3850,"navGroup":330,"navOrder":330,"navTitle":3851,"navigation":187,"originalPath":3852,"path":3853,"redirect":330,"seo":3854,"stem":3855,"updated":337,"version":338,"__hash__":3856},"docs\u002Fdocs\u002Fadmin\u002Fuser_management.md",{"type":7,"value":3813,"toc":3845},[3814,3817,3821,3824,3826,3829,3832,3836,3839,3842],[10,3815,615],{"id":3816},"user-management",[23,3818,3820],{"id":3819},"user-registration","User registration",[14,3822,3823],{},"Depending on where FlowFuse is installed, users should or should not be allowed\nto sign up for the service. User registration can be configured in the admin panel.\nGo to \"Admin Settings\" > \"Settings\" and select or deselect \"Allow new users to register on the login screen\".",[23,3825,1633],{"id":1632},[14,3827,3828],{},"To add new users to the platform go to \"Admin Settings\" > \"Users\" and click the\n\"New User\" button. Fill out the form, and provide the new user with their password.",[14,3830,3831],{},"To require users to change their password the next time they log in, use the \"Expire Password\" option\non the \"Edit User\" dialog.",[23,3833,3835],{"id":3834},"deleting-a-user","Deleting a user",[14,3837,3838],{},"Users can only be removed if they are not the sole owners of any teams.",[14,3840,3841],{},"As such they must either delete their teams first or ensure their teams have\nalternative owners. They can either do this themselves or an Admin do it for them.",[14,3843,3844],{},"They can then be removed via the \"Edit User\" dialog in the Admin view.",{"title":55,"searchDepth":77,"depth":77,"links":3846},[3847,3848,3849],{"id":3819,"depth":77,"text":3820},{"id":1632,"depth":77,"text":1633},{"id":3834,"depth":77,"text":3835},{},"User Management","admin\u002Fuser_management.md","\u002Fdocs\u002Fadmin\u002Fuser_management",{"title":615,"description":55},"docs\u002Fadmin\u002Fuser_management","_XgnNErFlydyWo1hk9BCLk1wxe8Msmr7NNN7FxD14Wc",{"id":3858,"title":3859,"body":3860,"description":3867,"extension":329,"layout":330,"meta":3949,"navGroup":3950,"navOrder":77,"navTitle":3951,"navigation":187,"originalPath":3952,"path":3953,"redirect":330,"seo":3954,"stem":3955,"updated":337,"version":338,"__hash__":3956},"docs\u002Fdocs\u002Fapi\u002Findex.md","FlowFuse Platform API",{"type":7,"value":3861,"toc":3946},[3862,3865,3868,3879,3883,3886,3892,3895,3898,3901,3912,3915,3923,3933,3940],[10,3863,3859],{"id":3864},"flowfuse-platform-api",[14,3866,3867],{},"The platform provides a REST API that makes it possible to create integrations and\ncustom workflows.",[14,3869,3870,3871,3875,3876,273],{},"The API comes with an OpenAPI 3.0 Specification that can be viewed ",[41,3872,785],{"href":3873,"rel":3874},"https:\u002F\u002Fapp.flowfuse.com\u002Fapi\u002F",[831],",\nor on any FlowFuse instance on the path ",[18,3877,3878],{},"\u002Fapi\u002F",[104,3880,3882],{"id":3881},"accessing-the-api","Accessing the API",[14,3884,3885],{},"To make use of the API you will need a valid Access Token. Tokens can be generated\nfor a user under the Security section of the User Settings page.",[14,3887,3888],{},[638,3889],{"alt":3890,"src":3891},"Tokens Settings Page","\u002Fdocs\u002Fapi\u002Fimages\u002Ftokens.png",[14,3893,3894],{},"Tokens can be set to have a limited life or unlimited and can be revoked\nby deleting the token from the list. Tokens with an expiry date will be deleted once they reach that date.",[14,3896,3897],{},"Be aware that the token value will only be displayed once, at creation time,\nthere is no way to recover the token after this point.",[14,3899,3900],{},"Currently, all routes require a valid token to be included in the request.",[14,3902,3903,3904,3907,3908,3911],{},"The tokens are passed using the ",[18,3905,3906],{},"Authorization"," header as a ",[18,3909,3910],{},"Bearer"," token.",[14,3913,3914],{},"For example, the following will get a list of the token owner's teams:",[50,3916,3921],{"className":3917,"code":3919,"language":3920},[3918],"language-text","curl -H \"Authorization: Bearer ffpat_d4vZlLhCN8muyFUi6UsquLj47H2aTDkDpvxBUf5Ea\" \\\n     https:\u002F\u002Fapp.flowforge.com\u002Fapi\u002Fv1\u002Fuser\u002Fteams\n","text",[18,3922,3919],{"__ignoreMap":55},[14,3924,3925,3926,3929,3930,273],{},"When sending data to the API, requests must set the ",[18,3927,3928],{},"Content-Type"," header to ",[18,3931,3932],{},"application\u002Fjson",[14,3934,3935,3936,3939],{},"For example, the following will update the name of the team with an id of ",[18,3937,3938],{},"mNYLkklLAG",":",[50,3941,3944],{"className":3942,"code":3943,"language":3920},[3918],"curl -X PUT \\\n     -H \"Content-Type: application\u002Fjson\" \\\n     -H \"Authorization: Bearer ffpat_d4vZlLhCN8muyFUi6UsquLj47H2aTDkDpvxBUf5Ea\" \\\n     -d '{\"name\": \"My Development Team\"}' \\\n     https:\u002F\u002Fapp.flowforge.com\u002Fapi\u002Fv1\u002Fteams\u002FmNYLkklLAG\n",[18,3945,3943],{"__ignoreMap":55},{"title":55,"searchDepth":77,"depth":77,"links":3947},[3948],{"id":3881,"depth":88,"text":3882},{},"FlowFuse User Manuals","FlowFuse API","api\u002FREADME.md","\u002Fdocs\u002Fapi",{"title":3859,"description":3867},"docs\u002Fapi\u002Findex","VHL5smDQLhyEs_2HPgY5Tvo-3u8wDG0VI3so2OCjzws",{"id":3958,"title":3959,"body":3960,"description":55,"extension":329,"layout":330,"meta":4051,"navGroup":330,"navOrder":330,"navTitle":4052,"navigation":187,"originalPath":4053,"path":4054,"redirect":330,"seo":4055,"stem":4056,"updated":337,"version":338,"__hash__":4057},"docs\u002Fdocs\u002Fcloud\u002Fbilling.md","Billing",{"type":7,"value":3961,"toc":4041},[3962,3965,3969,3972,3976,3979,3983,3986,3989,3993,3996,4000,4003,4006,4010,4013,4017,4020,4024,4027,4030,4033],[10,3963,3959],{"id":3964},"billing",[23,3966,3968],{"id":3967},"payment-methods","Payment Methods",[14,3970,3971],{},"We will accept payments via credit or debit card only using Stripe as our payment\nprovider. All payments are processed in US Dollars.",[23,3973,3975],{"id":3974},"team-billing","Team Billing",[14,3977,3978],{},"Each team has its own billing subscription that includes charges for the Node-RED\ninstances and Devices owned by the team.",[23,3980,3982],{"id":3981},"billing-cycle","Billing Cycle",[14,3984,3985],{},"Starter plan teams are billed monthly on the anniversary of the team creation. You will receive one bill for each team.",[14,3987,3988],{},"For Team plan teams, Node-RED Instances and Devices are added as pro-rated charges on the current billing cycle and invoiced\nat the end of the cycle.",[23,3990,3992],{"id":3991},"removing-instances","Removing Instances",[14,3994,3995],{},"For Team plan teams, when a Node-RED instance is deleted your account will receive pro-rated credit for the time remaining in the billing cycle.",[23,3997,3999],{"id":3998},"suspended-instances","Suspended Instances",[14,4001,4002],{},"Suspended Node-RED instances have no running editor, nor a runtime. You are not charged for suspended instances.",[14,4004,4005],{},"For Team plan teams, when an instance is suspended your account will receive pro-rated credit for the time remaining in the billing cycle. When an\ninstance is restarted it will be charged for the remaining time in the billing cycle.",[23,4007,4009],{"id":4008},"managing-billing-details","Managing Billing Details",[14,4011,4012],{},"Click on \"Billing\" followed by \"Stripe Customer Portal\" to get a summary of the team's current subscription. You'll be redirected to\na Stripe customer portal where you can update customer details as: The credit card on file and the billing information.",[23,4014,4016],{"id":4015},"failed-payments","Failed Payments",[14,4018,4019],{},"If your payment fails for any reason you will receive a notification to the billing email address, you may need to login and update the card on file.\nStripe will retry the payment several times over a number of days. If the card repeatedly fails your Node-RED instances will be suspended and a banner will be displayed at the top of the page for all users. An admin will need to update your card details to be able to create new instances or restart them.",[23,4021,4023],{"id":4022},"cancelling-your-subscription","Cancelling your subscription",[14,4025,4026],{},"To cancel your subscription you can either delete or suspend your team; both options are available under the Team Settings page.",[14,4028,4029],{},"Deleting the team will remove all of the team's instances and devices - they cannot be recovered after being deleted.",[14,4031,4032],{},"Suspending the team will stop all of your team's instances and devices and cancel your subscription so no further charges are made. You will not be able to do anything more with the team whilst it is suspended. You can unsuspend the team in the future by setting up a new payment subscription.",[14,4034,4035,4036,4040],{},"When deleting the team, if you have outstanding credit you can request a refund via a ",[41,4037,4039],{"href":4038},"\u002Fsupport\u002F","support ticket",", include the Team ID in your email.",{"title":55,"searchDepth":77,"depth":77,"links":4042},[4043,4044,4045,4046,4047,4048,4049,4050],{"id":3967,"depth":77,"text":3968},{"id":3974,"depth":77,"text":3975},{"id":3981,"depth":77,"text":3982},{"id":3991,"depth":77,"text":3992},{"id":3998,"depth":77,"text":3999},{"id":4008,"depth":77,"text":4009},{"id":4015,"depth":77,"text":4016},{"id":4022,"depth":77,"text":4023},{},"FlowFuse Cloud Billing","cloud\u002Fbilling.md","\u002Fdocs\u002Fcloud\u002Fbilling",{"title":3959,"description":55},"docs\u002Fcloud\u002Fbilling","7QBpTC3fk-xvyMF4eMmyuffvZ9mQrORx5-Iz-ANukkw",{"id":4059,"title":55,"body":4060,"description":55,"extension":329,"layout":532,"meta":4064,"navGroup":4067,"navOrder":88,"navTitle":4067,"navigation":187,"originalPath":4068,"path":4069,"redirect":4070,"seo":4072,"stem":4073,"updated":337,"version":338,"__hash__":4074},"docs\u002Fdocs\u002Fcloud\u002Findex.md",{"type":7,"value":4061,"toc":4062},[],{"title":55,"searchDepth":77,"depth":77,"links":4063},[],{"tags":4065},[4066],"noDropdown","FlowFuse Cloud","cloud\u002FREADME.md","\u002Fdocs\u002Fcloud",{"to":4071},"\u002Fdocs\u002Fcloud\u002Fintroduction",{"description":55},"docs\u002Fcloud\u002Findex","_7RDxa_XlwxP2HQgFY9qKKQMaPl2PDkc6hYnWSPvNZ4",{"id":4076,"title":4067,"body":4077,"description":4657,"extension":329,"layout":330,"meta":4658,"navGroup":330,"navOrder":62,"navTitle":4659,"navigation":187,"originalPath":4660,"path":4071,"redirect":330,"seo":4661,"stem":4662,"updated":337,"version":338,"__hash__":4663},"docs\u002Fdocs\u002Fcloud\u002Fintroduction.md",{"type":7,"value":4078,"toc":4630},[4079,4082,4089,4093,4096,4099,4102,4105,4107,4114,4118,4131,4135,4138,4141,4144,4148,4151,4155,4158,4174,4177,4180,4183,4197,4200,4204,4207,4213,4216,4222,4226,4238,4246,4255,4259,4262,4265,4303,4306,4310,4313,4316,4351,4359,4363,4366,4369,4372,4405,4409,4413,4424,4431,4435,4438,4442,4445,4448,4451,4455,4458,4461,4468,4488,4499,4505,4508,4514,4517,4523,4529,4533,4539,4542,4545,4549,4552,4563,4566,4569,4577,4580,4583,4593,4596,4599,4602,4609,4616,4620,4623],[10,4080,4067],{"id":4081},"flowfuse-cloud",[14,4083,4084,4085,4088],{},"FlowFuse Cloud is a hosted service allowing users to sign-up and start creating Node-RED instances without having to install and manage their own instance of FlowFuse.\nThe ",[41,4086,4087],{"href":590},"Concepts"," remain the same, but we run the platform for you.",[23,4090,4092],{"id":4091},"_30-day-free-trial","30-day Free Trial",[14,4094,4095],{},"When users sign-up to FlowFuse Cloud they get a 30-day free trial of the platform.\nThis is a great way to start using FlowFuse and discover a lot of the value it provides.",[14,4097,4098],{},"Users can end their trial by heading to the Billing page of their team and\nsetting up their payment information. This includes the option to pick which\nplan you want to upgrade the team to.",[14,4100,4101],{},"Otherwise, at the end of the 30-day trial period, any instances created in the team\nwill be suspended. This means they will no longer be running and the\neditor will not be accessible. Users will need to add their Billing details at which\npoint they will be able to restart their suspended Node-RED instances.",[14,4103,4104],{},"We will email users about their trial when it is nearing the end to ensure\nthey know what is happening.",[23,4106,3959],{"id":3964},[14,4108,4109,4110,4113],{},"Customers are billed at the team level for each Node-RED instance they create. This is a recurring monthly charge.\nSee the ",[41,4111,3959],{"href":4112},"\u002Fdocs\u002Fcloud\u002Fbilling.md"," page for more detailed answers about billing.",[23,4115,4117],{"id":4116},"support","Support",[14,4119,4120,4121,4125,4126,273],{},"Premium customers can get support by ",[41,4122,4124],{"href":4123},"\u002Fsupport","filing a ticket",". We offer\nsupport for the FlowFuse application and your account, any issues relating to\nNode-RED such as your flows or a 3rd party node should be raised in the\n",[41,4127,4130],{"href":4128,"rel":4129},"https:\u002F\u002Fcommunity.FlowFuse.com",[831],"community forum",[104,4132,4134],{"id":4133},"requesting-a-new-verification-email","Requesting a new verification email",[14,4136,4137],{},"When a user signs up for FlowFuse Cloud an email will be sent to verify it.\nIf this email doesn't get delivered one can be resend by signing in to FlowFuse\nand click the button to resend it.",[23,4139,1100],{"id":4140},"team-types",[14,4142,4143],{},"FlowFuse Cloud has three different Team Type aimed at different sorts of users",[104,4145,4147],{"id":4146},"starter","Starter",[14,4149,4150],{},"Good for getting to know the platform, allows 2 small Instances, 2 Devices and 2 Team members",[104,4152,4154],{"id":4153},"team","Team",[14,4156,4157],{},"Has access to more features e.g.",[28,4159,4160,4163,4165,4168,4171],{},[31,4161,4162],{},"Shared Team Library",[31,4164,790],{},[31,4166,4167],{},"Team-based Dashboard\u002FAPI security",[31,4169,4170],{},"Email alerts for Instance crashes",[31,4172,4173],{},"Has access to larger Instance Types",[14,4175,4176],{},"Also includes 5 Instances in the base price",[104,4178,1884],{"id":4179},"enterprise",[14,4181,4182],{},"All features from the Team Level plus",[28,4184,4185,4188,4191,4194],{},[31,4186,4187],{},"HA for Instances",[31,4189,4190],{},"SSO",[31,4192,4193],{},"Better Support SLA",[31,4195,4196],{},"MQTT Broker",[14,4198,4199],{},"Includes 10 instances and 20 MQTT Clients in the base price",[104,4201,4203],{"id":4202},"changing-team-type","Changing Team Type",[14,4205,4206],{},"You can change Team Type by selecting the \"Team Settings\" option from the left hand\nmenu, then clicking on the \"Change Team Type\" button",[14,4208,4209],{},[638,4210],{"alt":4211,"src":4212},"Change Team Type","\u002Fdocs\u002Fcloud\u002Fimages\u002Fchange-team-type.png",[14,4214,4215],{},"From here you will be presented with a choice of Team Types. You will not be able\nto downgrade to a lower Team Type if you already have more resources than allowed at\nthat level. Please Suspend or Delete any no longer required Instances or Devices.",[14,4217,4218],{},[638,4219],{"alt":4220,"src":4221},"Available Team Types","\u002Fdocs\u002Fcloud\u002Fimages\u002Favailble-team-types.png",[23,4223,4225],{"id":4224},"node-red-on-flowfuse-cloud","Node-RED on FlowFuse Cloud",[14,4227,4228,4229,4233,4234,273],{},"FlowFuse currently offers Node-RED 4.x, 3.x and 2.x to customers. When creating a\nnew instance a ",[41,4230,4232],{"href":4231},"\u002Fdocs\u002Fuser\u002Fconcepts.md#stack","stack"," is chosen, which later\ncan be ",[41,4235,4237],{"href":4236},"\u002Fdocs\u002Fuser\u002Fchangestack.md","upgraded to a later version",[14,4239,4240,4241,273],{},"Each Node-RED can install custom modules as advertised in the ",[41,4242,4245],{"href":4243,"rel":4244},"https:\u002F\u002Fflows.nodered.org",[831],"Flow Library",[14,4247,4248,4249,4254],{},"Note that some modules have dependencies on system libraries or other components that are not available within the FlowFuse\ncontainer images we use. Those modules cannot be used within FlowFuse Cloud. If you have a particular requirement, please\ndo ",[41,4250,4253],{"href":4251,"rel":4252},"https:\u002F\u002Fflowfuse.com\u002Fcontact-us\u002F",[831],"contact us"," so we can discuss what options may be available.",[23,4256,4258],{"id":4257},"cloud-instance-sizes","Cloud Instance Sizes",[14,4260,4261],{},"The different sizes of Cloud Instances relate to how much memory and CPU is made\navailable to them.",[14,4263,4264],{},"Memory tends to be the main limiting factor for a Node-RED instance. The following\ntable shows what is currently allocated by instance size.",[2289,4266,4267,4277],{},[2292,4268,4269],{},[2295,4270,4271,4274],{},[2298,4272,4273],{},"Size",[2298,4275,4276],{},"Memory (RAM)",[2305,4278,4279,4287,4295],{},[2295,4280,4281,4284],{},[2310,4282,4283],{},"Small",[2310,4285,4286],{},"256MB",[2295,4288,4289,4292],{},[2310,4290,4291],{},"Medium",[2310,4293,4294],{},"768MB",[2295,4296,4297,4300],{},[2310,4298,4299],{},"Large",[2310,4301,4302],{},"3840MB",[14,4304,4305],{},"Medium and Large instance types require the Teams or Enterprise tier.",[23,4307,4309],{"id":4308},"use-of-the-file-system","Use of the File System",[14,4311,4312],{},"FlowFuse Cloud hosted instances have access to a persistent file-system that will\nretain the files stored on it across restarts of the instance.",[14,4314,4315],{},"A quota limit is applied to how much data can be stored, which varies based on\nthe Team type.",[2289,4317,4318,4328],{},[2292,4319,4320],{},[2295,4321,4322,4325],{},[2298,4323,4324],{},"Team Type",[2298,4326,4327],{},"File Storage Quota (per instance)",[2305,4329,4330,4337,4344],{},[2295,4331,4332,4334],{},[2310,4333,4147],{},[2310,4335,4336],{},"1GB",[2295,4338,4339,4341],{},[2310,4340,4154],{},[2310,4342,4343],{},"10GB",[2295,4345,4346,4348],{},[2310,4347,1884],{},[2310,4349,4350],{},"100GB",[14,4352,4353,4354,273],{},"Files can be manually uploaded to an instance using the ",[41,4355,4358],{"href":4356,"rel":4357},"https:\u002F\u002Fflowfuse.com\u002Fblog\u002F2024\u002F08\u002Fflowfuse-2-8-release\u002F#static-assets-service",[831],"Static Asset Service",[23,4360,4362],{"id":4361},"node-red-context","Node-RED Context",[14,4364,4365],{},"Node-RED Context can be used to store small pieces of application state within the\nruntime. By default, this is stored in memory only.",[14,4367,4368],{},"FlowFuse Cloud provides an optional context store that can be used to persist\nthe data.",[14,4370,4371],{},"The amount of data that can be stored in context is determined by the Team type.",[2289,4373,4374,4383],{},[2292,4375,4376],{},[2295,4377,4378,4380],{},[2298,4379,4324],{},[2298,4381,4382],{},"Context Store Quota (per instance)",[2305,4384,4385,4392,4399],{},[2295,4386,4387,4389],{},[2310,4388,4147],{},[2310,4390,4391],{},"10MB",[2295,4393,4394,4396],{},[2310,4395,4154],{},[2310,4397,4398],{},"100MB",[2295,4400,4401,4403],{},[2310,4402,1884],{},[2310,4404,4336],{},[23,4406,4408],{"id":4407},"network-connections","Network Connections",[104,4410,4412],{"id":4411},"https-websockets","HTTP(S) & Websockets",[14,4414,4415,4416,4419,4420,4423],{},"Node-RED exposes an HTTPS interface on port 443 with each instance having its own hostname (",[18,4417,4418],{},"example.flowfuse.cloud","). Plain HTTP requests to port 80 will receive a redirect to HTTPS on port 443.\nYou MUST connect using the hostname not the IP address to reach your Node-RED instance.\nWebsocket connections over SSL (",[18,4421,4422],{},"wss:",") are also supported.",[14,4425,4426,4427,4430],{},"The payload size per request is limited to 5MB, which is the Node-RED default.\nWhen a request exceeds this limit, the whole request is rejected with a ",[18,4428,4429],{},"413 Payload Too Large"," error.",[104,4432,4434],{"id":4433},"tcp-and-udp","TCP and UDP",[14,4436,4437],{},"The default TCP and UDP nodes have been removed from the Node-RED palette. This is\nbecause it is not possible to route these sorts of connections to the container running\nNode-RED inside the FlowFuse Cloud platform.",[104,4439,4441],{"id":4440},"mqtt","MQTT",[14,4443,4444],{},"MQTT Connections to an external broker using the standard MQTT nodes will work fine as the connection is initiated by Node-RED.",[14,4446,4447],{},"FlowFuse provides an MQTT broker for general use by Enterprise Team's Node-RED instances. See the following section.",[14,4449,4450],{},"Also the Project Nodes can be used to easily pass messages between Node-RED instances running in the\nplatform.",[768,4452,4454],{"id":4453},"enterprise-team-broker","Enterprise Team Broker",[14,4456,4457],{},"Both Team and Enterprise level teams come with their own MQTT broker. You can provision clients from the broker tab in the left hand menu.",[14,4459,4460],{},"Enterprise level Teams can register up to 20 and Teams level Teams can register up to 5 clients as part of their plan. The ability to purchase additional packs of clients will come in a near future release.",[14,4462,4463,4464,4467],{},"The broker is available on ",[18,4465,4466],{},"broker.flowfuse.cloud"," and supports the following connection types:",[28,4469,4470,4476,4482],{},[31,4471,4472,4473],{},"MQTT on port ",[18,4474,4475],{},"1883",[31,4477,4478,4479],{},"MQTT over TLS on port ",[18,4480,4481],{},"8883",[31,4483,4484,4485],{},"MQTT over secure WebSockets on port ",[18,4486,4487],{},"443",[14,4489,4490,4491,4494,4495,4498],{},"When creating clients you can specify a username, but it will prepended to the the Team's id e.g. ",[18,4492,4493],{},"alice"," will become ",[18,4496,4497],{},"alice@32E4NEO5pY",".\nClients must also use the username as the MQTT Client ID in order to connect.",[14,4500,4501],{},[638,4502],{"alt":4503,"src":4504},"Create Broker Client","\u002Fdocs\u002Fcloud\u002Fimages\u002Fcreate-broker-client.png",[14,4506,4507],{},"e.g.",[50,4509,4512],{"className":4510,"code":4511,"language":3920},[3918],"mosquitto_sub -u \"alice@32E4NEO5pY\" -i \"alice@32E4NEO5pY\" -P \"password\" -h broker.flowfuse.cloud -t \"#\"\n",[18,4513,4511],{"__ignoreMap":55},[14,4515,4516],{},"Or in Node-RED as follows",[14,4518,4519],{},[638,4520],{"alt":4521,"src":4522},"Node-RED MQTT Client Connection","\u002Fdocs\u002Fcloud\u002Fimages\u002Fnode-red-mqtt-connection.png",[14,4524,4525],{},[638,4526],{"alt":4527,"src":4528},"Node-RED MQTT Client Security","\u002Fdocs\u002Fcloud\u002Fimages\u002Fnode-red-mqtt-security.png",[104,4530,4532],{"id":4531},"ip-addresses","IP Addresses",[14,4534,4535,4536,273],{},"Outbound connections from FlowFuse will always come from the IP address ",[18,4537,4538],{},"63.33.85.112",[14,4540,4541],{},"This can make access to a remote database or corporate network possible where those systems are protected by IP address filtering firewalls.",[14,4543,4544],{},"All incoming connections MUST use the hostname and not an IP address.",[23,4546,4548],{"id":4547},"data-security","Data Security",[14,4550,4551],{},"FlowFuse Cloud is hosted on Amazon Web Services. The following statements apply to data handling within the platform:",[28,4553,4554,4557,4560],{},[31,4555,4556],{},"The underlying database use the industry standard AES-256 encryption algorithm to encrypt the stored data.",[31,4558,4559],{},"Persistent storage offered to Node-RED instances uses AES-256 encryption algorithm to encrypt data and metadata at rest.",[31,4561,4562],{},"The network load balancer uses the latest recommended AWS Network Security policy. This enforces TLS1.2 as a minimum.",[23,4564,603],{"id":4565},"single-sign-on",[14,4567,4568],{},"FlowFuse supports configuring both SAML and LDAP based Single Sign-On for particular email domains.",[14,4570,4571,4572,4576],{},"This can be configured on request for FlowFuse Cloud by submitting a support request\nvia our ",[41,4573,4575],{"href":4251,"rel":4574},[831],"Contact Us"," page.",[14,4578,4579],{},"For SAML providers, you must have the ability to configure a SAML endpoint on your Identity Provider,\nand have the authority to configure SSO for your email domain.",[14,4581,4582],{},"We have currently validated our SSO support with the following Identity Providers:",[28,4584,4585,4587,4589,4591],{},[31,4586,2250],{},[31,4588,2256],{},[31,4590,2262],{},[31,4592,2274],{},[14,4594,4595],{},"If you are using a different Identity Provider, please still get in touch, and we\ncan evaluate what will be required to enable it.",[23,4597,964],{"id":4598},"custom-hostnames",[14,4600,4601],{},"FlowFuse Cloud can support custom hostnames for instances in Enterprise teams.",[14,4603,4604,4605,4608],{},"This allows you to point your own subdomain, such as ",[18,4606,4607],{},"dashboard.example.com"," at\none of your instances.",[14,4610,4611,4612,4615],{},"See ",[41,4613,964],{"href":4614},"\u002Fdocs\u002Fuser\u002Fcustom-hostnames.md"," for more information.",[23,4617,4619],{"id":4618},"removing-your-account","Removing your account",[14,4621,4622],{},"Before you can delete your account, teams you own must either be deleted or have at least 1 other owner.\nOnce this is done, you can remove your account by going to the \"User Settings\" page and clicking the \"Delete Account\" button.",[14,4624,4625,4626,273],{},"See also: ",[41,4627,4629],{"href":4628},"\u002Fdocs\u002Fcloud\u002Fbilling.md#cancelling-your-subscription","cancelling your subscription",{"title":55,"searchDepth":77,"depth":77,"links":4631},[4632,4633,4634,4637,4643,4644,4645,4646,4647,4653,4654,4655,4656],{"id":4091,"depth":77,"text":4092},{"id":3964,"depth":77,"text":3959},{"id":4116,"depth":77,"text":4117,"children":4635},[4636],{"id":4133,"depth":88,"text":4134},{"id":4140,"depth":77,"text":1100,"children":4638},[4639,4640,4641,4642],{"id":4146,"depth":88,"text":4147},{"id":4153,"depth":88,"text":4154},{"id":4179,"depth":88,"text":1884},{"id":4202,"depth":88,"text":4203},{"id":4224,"depth":77,"text":4225},{"id":4257,"depth":77,"text":4258},{"id":4308,"depth":77,"text":4309},{"id":4361,"depth":77,"text":4362},{"id":4407,"depth":77,"text":4408,"children":4648},[4649,4650,4651,4652],{"id":4411,"depth":88,"text":4412},{"id":4433,"depth":88,"text":4434},{"id":4440,"depth":88,"text":4441},{"id":4531,"depth":88,"text":4532},{"id":4547,"depth":77,"text":4548},{"id":4565,"depth":77,"text":603},{"id":4598,"depth":77,"text":964},{"id":4618,"depth":77,"text":4619},"FlowFuse Cloud is a hosted service allowing users to sign-up and start creating Node-RED instances without having to install and manage their own instance of FlowFuse.\nThe Concepts remain the same, but we run the platform for you.",{},"Introduction","cloud\u002Fintroduction.md",{"title":4067,"description":4657},"docs\u002Fcloud\u002Fintroduction","GtxPNOJ8I8c0zikF3cntrDqROU8lUY_ixX0o5DUUWQU",{"id":4665,"title":4666,"body":4667,"description":55,"extension":329,"layout":532,"meta":4671,"navGroup":4117,"navOrder":62,"navTitle":4666,"navigation":187,"originalPath":4672,"path":4673,"redirect":4674,"seo":4676,"stem":4677,"updated":337,"version":338,"__hash__":4678},"docs\u002Fdocs\u002Fcommunity-support.md","Community Support",{"type":7,"value":4668,"toc":4669},[],{"title":55,"searchDepth":77,"depth":77,"links":4670},[],{},"community-support.md","\u002Fdocs\u002Fcommunity-support",{"to":4675},"https:\u002F\u002Fdiscourse.nodered.org\u002Fc\u002Fvendors\u002Fflowfuse\u002F24",{"description":55},"docs\u002Fcommunity-support","DD62F4delPTqEjOoPTtamgsJJwQ2JNrF-h3VQ_5LRbI",{"id":4680,"title":4681,"body":4682,"description":4689,"extension":329,"layout":330,"meta":4871,"navGroup":330,"navOrder":330,"navTitle":4681,"navigation":187,"originalPath":4872,"path":4873,"redirect":330,"seo":4874,"stem":4875,"updated":337,"version":338,"__hash__":4876},"docs\u002Fdocs\u002Fcontribute\u002Fadding-template-settings.md","Adding Template Settings",{"type":7,"value":4683,"toc":4861},[4684,4687,4690,4693,4696,4699,4702,4731,4735,4738,4746,4749,4753,4756,4785,4789,4792,4803,4809,4812,4819,4823,4827,4842,4846,4853],[10,4685,4681],{"id":4686},"adding-template-settings",[14,4688,4689],{},"Within FlowFuse, each Node-RED instance is created from a Template. The Template defines\na set of preconfigured options for the instance. This includes runtime settings -\nvalues that you would normally expect to set in your Node-RED settings.js file.",[14,4691,4692],{},"The Template also defines which of those options can be customised by individual instances.",[14,4694,4695],{},"This guide explains how to add a new Node-RED runtime option to the Template object\nso that it can be customised and passed through to the underlying Node-RED settings.js file.",[14,4697,4698],{},"This is reasonably straightforward for simple boolean\u002Fstring\u002Fnumeric types.\nFor other types (objects\u002Farrays) it gets more complicated and we don't currently\nhave good examples to follow.",[14,4700,4701],{},"For the 'simple' cases, the steps are:",[398,4703,4704,4715,4723],{},[31,4705,4706,4707],{},"Update the Frontend\n",[398,4708,4709,4712],{},[31,4710,4711],{},"Pick a name for the setting, add it to the list of known settings and any\nvalidation logic that is needed",[31,4713,4714],{},"Add it to the appropriate Template section",[31,4716,4717,4718],{},"Update the runtime\n",[398,4719,4720],{},[31,4721,4722],{},"Add it to the known list of settings and any additional validation logic",[31,4724,4725,4726],{},"Update the Launcher\n",[398,4727,4728],{},[31,4729,4730],{},"Update the template used to generate settings.js with the new property",[23,4732,4734],{"id":4733},"_1-updating-the-frontend","1. Updating the Frontend",[14,4736,4737],{},"There are a set of views in the frontend used to present and edit templates. They\nget used in two different ways:",[398,4739,4740,4743],{},[31,4741,4742],{},"When creating\u002Fediting a template.\nAll options are available and there is a dropdown that lets the user set the\npolicy on the setting (which controls whether an instance is allowed to override\nthe setting)",[31,4744,4745],{},"When editing instance settings.\nAll options are shown, but only lets the user modify those that the template\npolicy allows to be changed.",[14,4747,4748],{},"This reuse of the views saves a lot of code duplication, at the cost of some\ncomplication in implementing it.",[104,4750,4752],{"id":4751},"_11-pick-a-name-for-the-setting","1.1 - Pick a name for the setting",[14,4754,4755],{},"The name should try to match its corresponding property in the Node-RED settings.js\nfile. Some thought should be made as to organisation of the properties.",[398,4757,4758,4764,4778],{},[31,4759,4760,4761],{},"Edit ",[18,4762,4763],{},"frontend\u002Fsrc\u002Fpages\u002Fadmin\u002FTemplate\u002Futils.js",[31,4765,4766,4767,302,4770,4773,4774,4777],{},"Add the new property name to the ",[18,4768,4769],{},"templateFields",[18,4771,4772],{},"defaultTemplateValues"," objects.\nNotice the names are flat strings with ",[18,4775,4776],{},"_"," used as a hierarchy separator... that\nwill make more sense if you're looking at the file.",[31,4779,4780,4781,4784],{},"If the value needs any sort of validation, add it to ",[18,4782,4783],{},"templateValidators"," in the\nsame file.",[104,4786,4788],{"id":4787},"_12-add-it-to-the-appropriate-template-section","1.2 - Add it to the appropriate Template section",[14,4790,4791],{},"Currently there are:",[28,4793,4794,4797,4800],{},[31,4795,4796],{},"Editor",[31,4798,4799],{},"Palette",[31,4801,4802],{},"Environment - a special case that is unlikely to get other options added",[14,4804,4805,4806,273],{},"These each lives in their own file under ",[18,4807,4808],{},"frontend\u002Fsrc\u002Fpages\u002Fadmin\u002FTemplate\u002Fsections\u002F",[14,4810,4811],{},"Pick an existing setting that most closely matches the setting you want to add\n(ie checkbox or text input), and copy its entry to the appropriate place.",[14,4813,4814,4815,4818],{},"Make sure you update ",[1160,4816,4817],{},"all"," of the references of the copied property name to your\nnew property name.",[23,4820,4822],{"id":4821},"_2-updating-the-runtime","2. Updating the Runtime",[104,4824,4826],{"id":4825},"_21-adding-it-to-the-known-list","2.1 - Adding it to the known list",[398,4828,4829,4835],{},[31,4830,4760,4831,4834],{},[18,4832,4833],{},"forge\u002Fdb\u002Fcontrollers\u002FProjectTemplate.js"," to add the new property to the\nlist of known settings.",[31,4836,4837,4838,4841],{},"In that same file, update the ",[18,4839,4840],{},"validateSettings"," function with any additional\nvalidation or data cleansing needed.",[23,4843,4845],{"id":4844},"_3-update-nr-launcher","3. Update nr-launcher",[14,4847,4848,4849,4852],{},"In the ",[18,4850,4851],{},"flowforge-nr-launcher"," repo...",[398,4854,4855],{},[31,4856,4760,4857,4860],{},[18,4858,4859],{},"lib\u002FruntimeSettings.js"," to include the new setting in the generate settings.js\nfile. Note that you must handle the case where the new setting is not present -\neither by applying a sensible default, or omitting the value.",{"title":55,"searchDepth":77,"depth":77,"links":4862},[4863,4867,4870],{"id":4733,"depth":77,"text":4734,"children":4864},[4865,4866],{"id":4751,"depth":88,"text":4752},{"id":4787,"depth":88,"text":4788},{"id":4821,"depth":77,"text":4822,"children":4868},[4869],{"id":4825,"depth":88,"text":4826},{"id":4844,"depth":77,"text":4845},{},"contribute\u002Fadding-template-settings.md","\u002Fdocs\u002Fcontribute\u002Fadding-template-settings",{"title":4681,"description":4689},"docs\u002Fcontribute\u002Fadding-template-settings","aqMTIucy-dNSWVN__BHpcdeNLheG_4rp2yTm4MwX64A",{"id":4878,"title":4879,"body":4880,"description":55,"extension":329,"layout":330,"meta":6202,"navGroup":330,"navOrder":330,"navTitle":6203,"navigation":187,"originalPath":6204,"path":6205,"redirect":330,"seo":6206,"stem":6207,"updated":337,"version":338,"__hash__":6208},"docs\u002Fdocs\u002Fcontribute\u002Fapi-design.md","HTTP API of the FlowFuse platform",{"type":7,"value":4881,"toc":6181},[4882,4885,4889,4896,4907,4910,4932,4935,4951,4955,4962,4968,4981,4987,5042,5046,5050,5053,5064,5068,5071,5080,5084,5087,5096,5100,5107,5110,5117,5121,5132,5192,5196,5207,5212,5226,5230,5235,5244,5248,5261,5265,5271,5274,5281,5287,5357,5361,5364,5399,5404,5407,5424,5427,5434,5440,5444,5447,5451,5454,5482,5485,5539,5545,5561,5570,5583,5587,5590,5604,5614,5618,5621,5628,5634,5637,5640,5646,5650,5653,5659,5662,5714,5720,5731,5737,5770,5773,5789,5810,6178],[10,4883,4879],{"id":4884},"http-api-of-the-flowfuse-platform",[23,4886,4888],{"id":4887},"api-documentation","API documentation",[14,4890,4891,4892,4895],{},"All public API routes should include a ",[18,4893,4894],{},"schema"," as part of their definition. This\nserves a number of purposes:",[28,4897,4898,4901,4904],{},[31,4899,4900],{},"It will be included in the auto-generated OpenAPI 3.0 spec",[31,4902,4903],{},"Fastify will validate requests include any required properties",[31,4905,4906],{},"Fastify will ensure the response object matches the defined schema",[14,4908,4909],{},"Some general guidance:",[28,4911,4912,4915,4922,4929],{},[31,4913,4914],{},"Ensure the routes have an appropriate tag set - this determines where in the\nSwagger UI it gets displayed.",[31,4916,4917,4918,4921],{},"Ensure the tag is listed in ",[18,4919,4920],{},"forge\u002Froutes\u002Fapi-docs.js"," so it appears in the right place",[31,4923,4924,4925,4928],{},"We define view schemas under ",[18,4926,4927],{},"forge\u002Fdb\u002Fviews\u002F*"," alongside the code that generates the view.\nKeep the naming consistent with other views.",[31,4930,4931],{},"Learn from the existing schemas - be consistent in style.",[14,4933,4934],{},"References:",[28,4936,4937,4944],{},[31,4938,4939],{},[41,4940,4943],{"href":4941,"rel":4942},"https:\u002F\u002Ffastify.dev\u002Fdocs\u002Flatest\u002FReference\u002FValidation-and-Serialization",[831],"Fastify Validation and Serialization",[31,4945,4946],{},[41,4947,4950],{"href":4948,"rel":4949},"https:\u002F\u002Fswagger.io\u002Fspecification\u002F",[831],"OpenAPI 3.0 spec",[23,4952,4954],{"id":4953},"object-ids","Object Ids",[14,4956,4957,4958,4961],{},"Most database models have a primary key of an auto-incrementing integer stored in\nthe ",[18,4959,4960],{},"id"," column.",[14,4963,4964,4965,4967],{},"These ids are internal properties of the models and should ",[1160,4966,1230],{}," be exposed via the\nAPI. This is because they can be guessed and leak information about how many instances\nof any particular model exist.",[14,4969,4970,4971,4974,4975,4977,4978,4980],{},"All model instances have an auto-generated ",[18,4972,4973],{},"hashid"," property that is an encoded version\nof the ",[18,4976,4960],{}," property. This property should be used on the API but aliased as the\n",[18,4979,4960],{}," property.",[14,4982,4983,4984,4986],{},"Each database model has a pair of helper functions to encode and decode hashids\nto\u002Ffrom the true ",[18,4985,4960],{}," value:",[50,4988,4992],{"className":4989,"code":4990,"language":4991,"meta":55,"style":55},"language-js shiki shiki-themes github-light github-dark","const encodedHashid = app.db.models.User.encodeHashid(123);\nconst objectId = app.db.models.User.decodeHashid(\"edjEbo2K1w\")\n","js",[18,4993,4994,5020],{"__ignoreMap":55},[59,4995,4996,4999,5002,5005,5008,5011,5014,5017],{"class":61,"line":62},[59,4997,4998],{"class":1372},"const",[59,5000,5001],{"class":73}," encodedHashid",[59,5003,5004],{"class":1372}," =",[59,5006,5007],{"class":178}," app.db.models.User.",[59,5009,5010],{"class":65},"encodeHashid",[59,5012,5013],{"class":178},"(",[59,5015,5016],{"class":73},"123",[59,5018,5019],{"class":178},");\n",[59,5021,5022,5024,5027,5029,5031,5034,5036,5039],{"class":61,"line":77},[59,5023,4998],{"class":1372},[59,5025,5026],{"class":73}," objectId",[59,5028,5004],{"class":1372},[59,5030,5007],{"class":178},[59,5032,5033],{"class":65},"decodeHashid",[59,5035,5013],{"class":178},[59,5037,5038],{"class":69},"\"edjEbo2K1w\"",[59,5040,5041],{"class":178},")\n",[23,5043,5045],{"id":5044},"api-path-design","API Path design",[104,5047,5049],{"id":5048},"admin-routes","Admin routes",[14,5051,5052],{},"All admin-only routes exist under:",[50,5054,5058],{"className":5055,"code":5056,"language":5057,"meta":55,"style":55},"language-txt shiki shiki-themes github-light github-dark","\u002Fapi\u002Fv1\u002Fadmin\u002F...\n","txt",[18,5059,5060],{"__ignoreMap":55},[59,5061,5062],{"class":61,"line":62},[59,5063,5056],{},[104,5065,5067],{"id":5066},"logged-in-user-routes","Logged-in user routes",[14,5069,5070],{},"All routes relating to the logged-in user exist under:",[50,5072,5074],{"className":5055,"code":5073,"language":5057,"meta":55,"style":55},"\u002Fapi\u002Fv1\u002Fuser\u002F...\n",[18,5075,5076],{"__ignoreMap":55},[59,5077,5078],{"class":61,"line":62},[59,5079,5073],{},[104,5081,5083],{"id":5082},"object-collection-routes","Object collection routes",[14,5085,5086],{},"API routes that are related to objects in collections follow the pattern:",[50,5088,5090],{"className":5055,"code":5089,"language":5057,"meta":55,"style":55},"\u002Fapi\u002Fv1\u002F\u003Cplural-noun>\u002F\u003Cinstance-id>\u002F...\n",[18,5091,5092],{"__ignoreMap":55},[59,5093,5094],{"class":61,"line":62},[59,5095,5089],{},[23,5097,5099],{"id":5098},"implementing-routes","Implementing routes",[14,5101,5102,5103,5106],{},"All API routes exist under ",[18,5104,5105],{},"forge\u002Froutes\u002Fapi"," in the repository, grouped into\nfiles based on the entity\u002Ffunctionality the api is related to.",[14,5108,5109],{},"All requests will have already been validated to ensure they are being made with\na valid session for a logged in user, or an access token.",[14,5111,5112,5113,5116],{},"If there is a valid session, ",[18,5114,5115],{},"request.session.User"," will be the requesting user.",[104,5118,5120],{"id":5119},"opening-a-route-to-anonymous-users","Opening a route to anonymous users",[14,5122,5123,5124,5127,5128,5131],{},"In rare cases, a route needs to be accessible to anonymous users. To by-pass\nthe built-in preHandler, you can set ",[18,5125,5126],{},"allowAnonymous"," on the routes ",[18,5129,5130],{},"config"," object:",[50,5133,5135],{"className":4989,"code":5134,"language":4991,"meta":55,"style":55},"app.get('\u002F', { config: { allowAnonymous: true } }, async (request, reply) => {\n     ...\n})\n",[18,5136,5137,5182,5187],{"__ignoreMap":55},[59,5138,5139,5142,5145,5147,5150,5153,5155,5158,5161,5164,5168,5170,5173,5176,5179],{"class":61,"line":62},[59,5140,5141],{"class":178},"app.",[59,5143,5144],{"class":65},"get",[59,5146,5013],{"class":178},[59,5148,5149],{"class":69},"'\u002F'",[59,5151,5152],{"class":178},", { config: { allowAnonymous: ",[59,5154,3558],{"class":73},[59,5156,5157],{"class":178}," } }, ",[59,5159,5160],{"class":1372},"async",[59,5162,5163],{"class":178}," (",[59,5165,5167],{"class":5166},"s4XuR","request",[59,5169,3012],{"class":178},[59,5171,5172],{"class":5166},"reply",[59,5174,5175],{"class":178},") ",[59,5177,5178],{"class":1372},"=>",[59,5180,5181],{"class":178}," {\n",[59,5183,5184],{"class":61,"line":77},[59,5185,5186],{"class":1372},"     ...\n",[59,5188,5189],{"class":61,"line":88},[59,5190,5191],{"class":178},"})\n",[104,5193,5195],{"id":5194},"team-routes","Team routes",[14,5197,5198,5199,5202,5203,5206],{},"Routes under ",[18,5200,5201],{},"forge\u002Froutes\u002Fapi\u002Fteam.js"," that relate to a specific team,\nmust use ",[18,5204,5205],{},":teamId"," as the placeholder in the route. A request preHandler will\nuse that to ensure the requesting user has permission to access the instance.",[14,5208,5209,5210,3939],{},"The following properties will then be available on the ",[18,5211,5167],{},[28,5213,5214,5220],{},[31,5215,5216,5219],{},[18,5217,5218],{},"request.team"," - the team",[31,5221,5222,5225],{},[18,5223,5224],{},"request.teamMembership"," - the requesting user's role on the team",[104,5227,5229],{"id":5228},"project-routes","Project routes",[14,5231,5232,5234],{},[364,5233,1798],{}," In FlowForge 1.5 we started to replace the Project concept with that of Application\nand Instance. No changes have been made to the underlying APIs - that will be evaluated\nas part of 1.6.",[14,5236,5198,5237,5240,5241,5206],{},[18,5238,5239],{},"forge\u002Froutes\u002Fapi\u002Fproject.js"," that relate to a specific instance,\nmust use ",[18,5242,5243],{},":projectId",[14,5245,5209,5246,3939],{},[18,5247,5167],{},[28,5249,5250,5256],{},[31,5251,5252,5255],{},[18,5253,5254],{},"request.project"," - the instance (previously called \"project\")",[31,5257,5258,5260],{},[18,5259,5224],{}," - the requesting user's role on the team that owns the instance",[104,5262,5264],{"id":5263},"userrole-permissions","User\u002Frole permissions",[14,5266,5267,5268,273],{},"Permissions are defined in ",[18,5269,5270],{},"forge\u002Froutes\u002Fauth\u002Fpermissions.js",[14,5272,5273],{},"Each permission specifies the role a user must have to have that permission.",[14,5275,5276,5277,5280],{},"If a route requires a particular permission, it can use ",[18,5278,5279],{},"app.needsPermission"," to\ngenerate a preHandler function that will ensure any requesting user has that\npermission.",[14,5282,5283,5284,3939],{},"For example, to add a user to a team, the requesting user needs to have\n",[18,5285,5286],{},"\"team:user:add\"",[50,5288,5290],{"className":4989,"code":5289,"language":4991,"meta":55,"style":55},"app.post('\u002F', { preHandler: app.needsPermission(\"team:user:add\") }, async (request, reply) => {\n    \u002F\u002F This is defined under forge\u002Froutes\u002Fapi\u002FteamMembers.js - and is mounted\n    \u002F\u002F under the path `\u002Fapi\u002Fv1\u002Fteams\u002F:teamId\u002Fmembers\u002F`\n    \u002F\u002F Due to the team route preHandler, this means `request.teamMembership`\n    \u002F\u002F will be defined and will be used by the `needsPermission` handler\n});\n",[18,5291,5292,5332,5337,5342,5347,5352],{"__ignoreMap":55},[59,5293,5294,5296,5299,5301,5303,5306,5309,5311,5313,5316,5318,5320,5322,5324,5326,5328,5330],{"class":61,"line":62},[59,5295,5141],{"class":178},[59,5297,5298],{"class":65},"post",[59,5300,5013],{"class":178},[59,5302,5149],{"class":69},[59,5304,5305],{"class":178},", { preHandler: app.",[59,5307,5308],{"class":65},"needsPermission",[59,5310,5013],{"class":178},[59,5312,5286],{"class":69},[59,5314,5315],{"class":178},") }, ",[59,5317,5160],{"class":1372},[59,5319,5163],{"class":178},[59,5321,5167],{"class":5166},[59,5323,3012],{"class":178},[59,5325,5172],{"class":5166},[59,5327,5175],{"class":178},[59,5329,5178],{"class":1372},[59,5331,5181],{"class":178},[59,5333,5334],{"class":61,"line":77},[59,5335,5336],{"class":3773},"    \u002F\u002F This is defined under forge\u002Froutes\u002Fapi\u002FteamMembers.js - and is mounted\n",[59,5338,5339],{"class":61,"line":88},[59,5340,5341],{"class":3773},"    \u002F\u002F under the path `\u002Fapi\u002Fv1\u002Fteams\u002F:teamId\u002Fmembers\u002F`\n",[59,5343,5344],{"class":61,"line":99},[59,5345,5346],{"class":3773},"    \u002F\u002F Due to the team route preHandler, this means `request.teamMembership`\n",[59,5348,5349],{"class":61,"line":156},[59,5350,5351],{"class":3773},"    \u002F\u002F will be defined and will be used by the `needsPermission` handler\n",[59,5353,5354],{"class":61,"line":216},[59,5355,5356],{"class":178},"});\n",[23,5358,5360],{"id":5359},"error-formats","Error formats",[14,5362,5363],{},"If a route needs to return an error it should respond with a payload in the format:",[50,5365,5367],{"className":4989,"code":5366,"language":4991,"meta":55,"style":55},"{\n    code: 'error_code',\n    error: 'Human-readable message'\n}\n",[18,5368,5369,5373,5385,5395],{"__ignoreMap":55},[59,5370,5371],{"class":61,"line":62},[59,5372,2981],{"class":178},[59,5374,5375,5378,5380,5383],{"class":61,"line":77},[59,5376,5377],{"class":65},"    code",[59,5379,179],{"class":178},[59,5381,5382],{"class":69},"'error_code'",[59,5384,2665],{"class":178},[59,5386,5387,5390,5392],{"class":61,"line":88},[59,5388,5389],{"class":65},"    error",[59,5391,179],{"class":178},[59,5393,5394],{"class":69},"'Human-readable message'\n",[59,5396,5397],{"class":61,"line":99},[59,5398,3336],{"class":178},[14,5400,372,5401,5403],{},[18,5402,18],{}," property should be a well-defined string that can be used to programmatically\nidentify the error without relying on the human-readable message.",[14,5405,5406],{},"There is a set of predefined codes that should be used where appropriate:",[28,5408,5409,5414,5419],{},[31,5410,5411],{},[18,5412,5413],{},"unauthorized",[31,5415,5416],{},[18,5417,5418],{},"invalid_request",[31,5420,5421],{},[18,5422,5423],{},"unexpected_error",[14,5425,5426],{},"If the error is related to an invalid option\u002Fparameter\u002Fobject selection, then the code\nshould be:",[28,5428,5429],{},[31,5430,5431],{},[18,5432,5433],{},"invalid_\u003Cname of property>",[14,5435,5436,5437,273],{},"For example: ",[18,5438,5439],{},"invalid_project_name",[23,5441,5443],{"id":5442},"pagination-search","Pagination & Search",[14,5445,5446],{},"All routes that return collections of things must use pagination to allow for\nfuture growth, and provide a way to search the collection.",[104,5448,5450],{"id":5449},"pagination","Pagination",[14,5452,5453],{},"We use a cursor-based approach to our pagination. A cursor is a pointer to an entity\nin the collection and provides the starting point for what should be returned.",[28,5455,5456,5466,5476],{},[31,5457,5458,5459,302,5462,5465],{},"End-points accept ",[18,5460,5461],{},"cursor",[18,5463,5464],{},"limit"," parameters",[31,5467,5468,5469,5471,5472,5475],{},"If ",[18,5470,5461],{}," is not provided, it returns from the beginning of the collection.\nFor some end-points, this will mean returning the most recent entries -\nthe ",[18,5473,5474],{},"\u002Fapi\u002Fv1\u002Fprojects\u002F:id\u002Flogs"," for example.",[31,5477,5478,5479,5481],{},"Each end-point should have a sensible default for the ",[18,5480,5464],{}," parameter",[14,5483,5484],{},"The response object for paginated end-points should have the format:",[50,5486,5488],{"className":4989,"code":5487,"language":4991,"meta":55,"style":55},"{\n    \"meta\": {\n        \"next_cursor\": \"16416724188790000\",\n    },\n    \"\u003Cobject-type>\": [ ],\n    \"count\": 123\n}\n",[18,5489,5490,5494,5501,5513,5517,5525,5535],{"__ignoreMap":55},[59,5491,5492],{"class":61,"line":62},[59,5493,2981],{"class":178},[59,5495,5496,5499],{"class":61,"line":77},[59,5497,5498],{"class":69},"    \"meta\"",[59,5500,3068],{"class":178},[59,5502,5503,5506,5508,5511],{"class":61,"line":88},[59,5504,5505],{"class":69},"        \"next_cursor\"",[59,5507,179],{"class":178},[59,5509,5510],{"class":69},"\"16416724188790000\"",[59,5512,2665],{"class":178},[59,5514,5515],{"class":61,"line":99},[59,5516,3190],{"class":178},[59,5518,5519,5522],{"class":61,"line":156},[59,5520,5521],{"class":69},"    \"\u003Cobject-type>\"",[59,5523,5524],{"class":178},": [ ],\n",[59,5526,5527,5530,5532],{"class":61,"line":216},[59,5528,5529],{"class":69},"    \"count\"",[59,5531,179],{"class":178},[59,5533,5534],{"class":73},"123\n",[59,5536,5537],{"class":61,"line":224},[59,5538,3336],{"class":178},[14,5540,372,5541,5544],{},[18,5542,5543],{},"meta"," property contains information to help the client navigate the collection.",[28,5546,5547,5553],{},[31,5548,5549,5552],{},[18,5550,5551],{},"next_cursor"," - if set, provides the cursor to use to get the next page of results",[31,5554,5555,5558,5559,273],{},[18,5556,5557],{},"previous_cursor"," - if set, provides the cursor to use to get the previous page of results. Note\nthat not all end-points need to be navigable in both directions so may never return\n",[18,5560,5557],{},[14,5562,372,5563,5566,5567,273],{},[18,5564,5565],{},"\u003Cobject-type>"," property should be called the appropriate plural form of what is being returned,\nsuch as ",[18,5568,5569],{},"projects",[14,5571,372,5572,5575,5576,5579,5580,5582],{},[18,5573,5574],{},"count"," property is optional and indicates the total number of objects in the collection.\nEnd-points ",[1160,5577,5578],{},"should"," include it if the number is known and is of material use. It is ",[1160,5581,1230],{},"\nreturned by end-points used to query logs etc as the total count is a constantly changing\nvalue.",[768,5584,5586],{"id":5585},"cursor-design","Cursor design",[14,5588,5589],{},"A cursor should be able to point directly at an entity in the collection. It could\nbe the entity hashid, or a timestamp if it is a time-series collection such as the\nlogs.",[14,5591,5592,5593,5595,5596,5598,5599,5601,5602,273],{},"The cursor should ",[1160,5594,1230],{}," be the internal ",[18,5597,4960],{}," of the entity in the collection - as we\ndo not expose those ids on the api, use the ",[18,5600,4973],{}," value as the external ",[18,5603,4960],{},[14,5605,5606,5607,5609,5610,5613],{},"If an end-point supports navigating the collection in reverse (by returning ",[18,5608,5557],{},"),\nthe cursor should be prefixed with ",[18,5611,5612],{},"-"," to indicate the query should be in the opposite\ndirection to the collection's natural order.",[104,5615,5617],{"id":5616},"search-filtering","Search & Filtering",[14,5619,5620],{},"Search is done as simple case-insensitive text-based queries against string columns\nin the database model. This is a crude but effective way to implement search but\nmay need a more comprehensive approach in the future.",[14,5622,5623,5624,5627],{},"The search value is provided via the ",[18,5625,5626],{},"query"," query parameter.",[50,5629,5632],{"className":5630,"code":5631,"language":3920},[3918],"\u002Fapi\u002Fv1\u002Fexample?query=something\n",[18,5633,5631],{"__ignoreMap":55},[14,5635,5636],{},"Filtering can be used to limit the results based on the values of specific columns.",[14,5638,5639],{},"The Filter value is provided via additional query parameters.",[50,5641,5644],{"className":5642,"code":5643,"language":3920},[3918],"\u002Fapi\u002Fv1\u002Fexample?eventName=foo\n",[18,5645,5643],{"__ignoreMap":55},[104,5647,5649],{"id":5648},"implementing-pagination-search","Implementing pagination & search",[14,5651,5652],{},"Two utility functions are provided to help implement pagination and search.",[768,5654,5656],{"id":5655},"appgetpaginationoptions",[18,5657,5658],{},"app.getPaginationOptions",[14,5660,5661],{},"This returns an object of pagination options for a given request, with any default\nvalues automatically applied:",[50,5663,5665],{"className":4989,"code":5664,"language":4991,"meta":55,"style":55},"const paginationOptions = app.getPaginationOptions(request, {limit: 1000})\n\n\u002F\u002F paginationOptions.limit = how many results to return\n\u002F\u002F paginationOptions.cursor = starting cursor\n\u002F\u002F paginationOptions.query = search string to use\n\u002F\u002F paginationOptions.* = any remaining query parameters\n",[18,5666,5667,5690,5694,5699,5704,5709],{"__ignoreMap":55},[59,5668,5669,5671,5674,5676,5679,5682,5685,5688],{"class":61,"line":62},[59,5670,4998],{"class":1372},[59,5672,5673],{"class":73}," paginationOptions",[59,5675,5004],{"class":1372},[59,5677,5678],{"class":178}," app.",[59,5680,5681],{"class":65},"getPaginationOptions",[59,5683,5684],{"class":178},"(request, {limit: ",[59,5686,5687],{"class":73},"1000",[59,5689,5191],{"class":178},[59,5691,5692],{"class":61,"line":77},[59,5693,188],{"emptyLinePlaceholder":187},[59,5695,5696],{"class":61,"line":88},[59,5697,5698],{"class":3773},"\u002F\u002F paginationOptions.limit = how many results to return\n",[59,5700,5701],{"class":61,"line":99},[59,5702,5703],{"class":3773},"\u002F\u002F paginationOptions.cursor = starting cursor\n",[59,5705,5706],{"class":61,"line":156},[59,5707,5708],{"class":3773},"\u002F\u002F paginationOptions.query = search string to use\n",[59,5710,5711],{"class":61,"line":216},[59,5712,5713],{"class":3773},"\u002F\u002F paginationOptions.* = any remaining query parameters\n",[768,5715,5717],{"id":5716},"buildpaginationsearchclause",[18,5718,5719],{},"buildPaginationSearchClause",[14,5721,5722,5723,5726,5727,5730],{},"This takes the pagination options along with model-specific configuration options\nto build a suitable ",[18,5724,5725],{},"where"," clause that can be passed to the database model's ",[18,5728,5729],{},"getAll","\nfunction.",[50,5732,5735],{"className":5733,"code":5734,"language":3920},[3918],"const where = buildPaginationSearchClause(params, whereClause, searchColumns, filterMap)\n",[18,5736,5734],{"__ignoreMap":55},[28,5738,5739,5748,5754,5764],{},[31,5740,5741,5744,5745,5747],{},[18,5742,5743],{},"params"," - the pagination object returned by ",[18,5746,5658],{}," for a given request",[31,5749,5750,5753],{},[18,5751,5752],{},"whereClause"," - (optional) an object containing any additional query clauses that should be applied",[31,5755,5756,5759,5760,5763],{},[18,5757,5758],{},"searchColumns"," - (optional) an array of fully-qualified column names that should be searched when ",[18,5761,5762],{},"query=\u003Cxyz>"," is included in query",[31,5765,5766,5769],{},[18,5767,5768],{},"filterMap"," - (option) a map of filter name to fully-qualified column name that are valid filter parameters",[14,5771,5772],{},"With the filtering option, if a particular filter parameter is specified more than\nonce in the query string, the generated query will apply an Or between the values.",[14,5774,5775,5776,5779,5780,3012,5783,302,5785,5788],{},"For example, lets consider the imaginary ",[18,5777,5778],{},"Thing"," model has a ",[18,5781,5782],{},"name",[18,5784,440],{},[18,5786,5787],{},"type",".\nThe following will mean:",[28,5790,5791,5802],{},[31,5792,5793,5794,5796,5797,302,5799,5801],{},"any ",[18,5795,5762],{}," parameter will be searched for in the ",[18,5798,5782],{},[18,5800,440],{}," columns",[31,5803,5793,5804,5807,5808,4961],{},[18,5805,5806],{},"type=\u003Cabc>"," parameter will filter on the ",[18,5809,5787],{},[50,5811,5813],{"className":4989,"code":5812,"language":4991,"meta":55,"style":55},"const { buildPaginationSearchClause } = require('..\u002Futils')\n\n...\n\ngetAll: async (pagination = {}, where = {}) => {\n    \u002F\u002F Ensure a sensible default limit for this particular type of thing\n    const limit = parseInt(pagination.limit) || 1000\n\n    \u002F\u002F Decode the cursor from hashid to database id\n    if (pagination.cursor) {\n        pagination.cursor = M.Thing.decodeHashid(pagination.cursor)\n    }\n\n    \u002F\u002F Build the full where query using the buildPaginationSearchClause utility.\n    \u002F\u002F We pass in the list of columns that should be searched against\n    where = buildPaginationSearchClause(\n        pagination,\n        where,\n        ['Thing.name', 'Thing.description'],\n        {\n            type: 'Thing.type'\n        }\n    )\n\n    \u002F\u002F Run the query\n    const { count, rows } = await this.findAndCountAll({\n        where,\n        order: [['id', 'ASC']],\n        limit\n    })\n\n    \u002F\u002F Return the results with the additional metadata\n    return {\n        meta: {\n            next_cursor: rows.length === limit ? rows[rows.length - 1].hashid : undefined\n        },\n        count,\n        things: rows\n    }\n}\n",[18,5814,5815,5839,5843,5848,5852,5880,5885,5907,5911,5916,5924,5942,5946,5950,5955,5960,5973,5978,5983,5999,6004,6012,6017,6022,6026,6031,6062,6066,6082,6087,6092,6096,6101,6108,6113,6150,6156,6162,6168,6173],{"__ignoreMap":55},[59,5816,5817,5819,5822,5824,5827,5829,5832,5834,5837],{"class":61,"line":62},[59,5818,4998],{"class":1372},[59,5820,5821],{"class":178}," { ",[59,5823,5719],{"class":73},[59,5825,5826],{"class":178}," } ",[59,5828,1373],{"class":1372},[59,5830,5831],{"class":65}," require",[59,5833,5013],{"class":178},[59,5835,5836],{"class":69},"'..\u002Futils'",[59,5838,5041],{"class":178},[59,5840,5841],{"class":61,"line":77},[59,5842,188],{"emptyLinePlaceholder":187},[59,5844,5845],{"class":61,"line":88},[59,5846,5847],{"class":1372},"...\n",[59,5849,5850],{"class":61,"line":99},[59,5851,188],{"emptyLinePlaceholder":187},[59,5853,5854,5856,5858,5860,5862,5864,5866,5869,5871,5873,5876,5878],{"class":61,"line":156},[59,5855,5729],{"class":65},[59,5857,179],{"class":178},[59,5859,5160],{"class":1372},[59,5861,5163],{"class":178},[59,5863,5449],{"class":5166},[59,5865,5004],{"class":1372},[59,5867,5868],{"class":178}," {}, ",[59,5870,5725],{"class":5166},[59,5872,5004],{"class":1372},[59,5874,5875],{"class":178}," {}) ",[59,5877,5178],{"class":1372},[59,5879,5181],{"class":178},[59,5881,5882],{"class":61,"line":216},[59,5883,5884],{"class":3773},"    \u002F\u002F Ensure a sensible default limit for this particular type of thing\n",[59,5886,5887,5890,5893,5895,5898,5901,5904],{"class":61,"line":224},[59,5888,5889],{"class":1372},"    const",[59,5891,5892],{"class":73}," limit",[59,5894,5004],{"class":1372},[59,5896,5897],{"class":65}," parseInt",[59,5899,5900],{"class":178},"(pagination.limit) ",[59,5902,5903],{"class":1372},"||",[59,5905,5906],{"class":73}," 1000\n",[59,5908,5909],{"class":61,"line":233},[59,5910,188],{"emptyLinePlaceholder":187},[59,5912,5913],{"class":61,"line":241},[59,5914,5915],{"class":3773},"    \u002F\u002F Decode the cursor from hashid to database id\n",[59,5917,5918,5921],{"class":61,"line":249},[59,5919,5920],{"class":1372},"    if",[59,5922,5923],{"class":178}," (pagination.cursor) {\n",[59,5925,5926,5929,5931,5934,5937,5939],{"class":61,"line":257},[59,5927,5928],{"class":178},"        pagination.cursor ",[59,5930,1373],{"class":1372},[59,5932,5933],{"class":73}," M",[59,5935,5936],{"class":178},".Thing.",[59,5938,5033],{"class":65},[59,5940,5941],{"class":178},"(pagination.cursor)\n",[59,5943,5944],{"class":61,"line":3137},[59,5945,3324],{"class":178},[59,5947,5948],{"class":61,"line":3150},[59,5949,188],{"emptyLinePlaceholder":187},[59,5951,5952],{"class":61,"line":3163},[59,5953,5954],{"class":3773},"    \u002F\u002F Build the full where query using the buildPaginationSearchClause utility.\n",[59,5956,5957],{"class":61,"line":3176},[59,5958,5959],{"class":3773},"    \u002F\u002F We pass in the list of columns that should be searched against\n",[59,5961,5962,5965,5967,5970],{"class":61,"line":3187},[59,5963,5964],{"class":178},"    where ",[59,5966,1373],{"class":1372},[59,5968,5969],{"class":65}," buildPaginationSearchClause",[59,5971,5972],{"class":178},"(\n",[59,5974,5975],{"class":61,"line":3193},[59,5976,5977],{"class":178},"        pagination,\n",[59,5979,5980],{"class":61,"line":3201},[59,5981,5982],{"class":178},"        where,\n",[59,5984,5985,5988,5991,5993,5996],{"class":61,"line":3214},[59,5986,5987],{"class":178},"        [",[59,5989,5990],{"class":69},"'Thing.name'",[59,5992,3012],{"class":178},[59,5994,5995],{"class":69},"'Thing.description'",[59,5997,5998],{"class":178},"],\n",[59,6000,6001],{"class":61,"line":3222},[59,6002,6003],{"class":178},"        {\n",[59,6005,6006,6009],{"class":61,"line":3233},[59,6007,6008],{"class":178},"            type: ",[59,6010,6011],{"class":69},"'Thing.type'\n",[59,6013,6014],{"class":61,"line":3239},[59,6015,6016],{"class":178},"        }\n",[59,6018,6019],{"class":61,"line":3247},[59,6020,6021],{"class":178},"    )\n",[59,6023,6024],{"class":61,"line":3256},[59,6025,188],{"emptyLinePlaceholder":187},[59,6027,6028],{"class":61,"line":3261},[59,6029,6030],{"class":3773},"    \u002F\u002F Run the query\n",[59,6032,6033,6035,6037,6039,6041,6044,6046,6048,6051,6054,6056,6059],{"class":61,"line":3269},[59,6034,5889],{"class":1372},[59,6036,5821],{"class":178},[59,6038,5574],{"class":73},[59,6040,3012],{"class":178},[59,6042,6043],{"class":73},"rows",[59,6045,5826],{"class":178},[59,6047,1373],{"class":1372},[59,6049,6050],{"class":1372}," await",[59,6052,6053],{"class":73}," this",[59,6055,273],{"class":178},[59,6057,6058],{"class":65},"findAndCountAll",[59,6060,6061],{"class":178},"({\n",[59,6063,6064],{"class":61,"line":3278},[59,6065,5982],{"class":178},[59,6067,6068,6071,6074,6076,6079],{"class":61,"line":3284},[59,6069,6070],{"class":178},"        order: [[",[59,6072,6073],{"class":69},"'id'",[59,6075,3012],{"class":178},[59,6077,6078],{"class":69},"'ASC'",[59,6080,6081],{"class":178},"]],\n",[59,6083,6084],{"class":61,"line":3289},[59,6085,6086],{"class":178},"        limit\n",[59,6088,6089],{"class":61,"line":3297},[59,6090,6091],{"class":178},"    })\n",[59,6093,6094],{"class":61,"line":3310},[59,6095,188],{"emptyLinePlaceholder":187},[59,6097,6098],{"class":61,"line":3321},[59,6099,6100],{"class":3773},"    \u002F\u002F Return the results with the additional metadata\n",[59,6102,6103,6106],{"class":61,"line":3327},[59,6104,6105],{"class":1372},"    return",[59,6107,5181],{"class":178},[59,6109,6110],{"class":61,"line":3333},[59,6111,6112],{"class":178},"        meta: {\n",[59,6114,6116,6119,6122,6125,6128,6131,6134,6136,6139,6142,6145,6147],{"class":61,"line":6115},35,[59,6117,6118],{"class":178},"            next_cursor: rows.",[59,6120,6121],{"class":73},"length",[59,6123,6124],{"class":1372}," ===",[59,6126,6127],{"class":178}," limit ",[59,6129,6130],{"class":1372},"?",[59,6132,6133],{"class":178}," rows[rows.",[59,6135,6121],{"class":73},[59,6137,6138],{"class":1372}," -",[59,6140,6141],{"class":73}," 1",[59,6143,6144],{"class":178},"].hashid ",[59,6146,3939],{"class":1372},[59,6148,6149],{"class":73}," undefined\n",[59,6151,6153],{"class":61,"line":6152},36,[59,6154,6155],{"class":178},"        },\n",[59,6157,6159],{"class":61,"line":6158},37,[59,6160,6161],{"class":178},"        count,\n",[59,6163,6165],{"class":61,"line":6164},38,[59,6166,6167],{"class":178},"        things: rows\n",[59,6169,6171],{"class":61,"line":6170},39,[59,6172,3324],{"class":178},[59,6174,6176],{"class":61,"line":6175},40,[59,6177,3336],{"class":178},[316,6179,6180],{},"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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":55,"searchDepth":77,"depth":77,"links":6182},[6183,6184,6185,6190,6196,6197],{"id":4887,"depth":77,"text":4888},{"id":4953,"depth":77,"text":4954},{"id":5044,"depth":77,"text":5045,"children":6186},[6187,6188,6189],{"id":5048,"depth":88,"text":5049},{"id":5066,"depth":88,"text":5067},{"id":5082,"depth":88,"text":5083},{"id":5098,"depth":77,"text":5099,"children":6191},[6192,6193,6194,6195],{"id":5119,"depth":88,"text":5120},{"id":5194,"depth":88,"text":5195},{"id":5228,"depth":88,"text":5229},{"id":5263,"depth":88,"text":5264},{"id":5359,"depth":77,"text":5360},{"id":5442,"depth":77,"text":5443,"children":6198},[6199,6200,6201],{"id":5449,"depth":88,"text":5450},{"id":5616,"depth":88,"text":5617},{"id":5648,"depth":88,"text":5649},{},"API Design","contribute\u002Fapi-design.md","\u002Fdocs\u002Fcontribute\u002Fapi-design",{"title":4879,"description":55},"docs\u002Fcontribute\u002Fapi-design","9L_UtvSL1gBfuRXhcQ3wms526-AprdI1587YyNcBJ-8",{"id":6210,"title":6211,"body":6212,"description":6219,"extension":329,"layout":330,"meta":6641,"navGroup":330,"navOrder":330,"navTitle":6211,"navigation":187,"originalPath":6642,"path":6643,"redirect":330,"seo":6644,"stem":6645,"updated":337,"version":338,"__hash__":6646},"docs\u002Fdocs\u002Fcontribute\u002Farchitecture.md","FlowFuse Architecture",{"type":7,"value":6213,"toc":6632},[6214,6217,6220,6228,6231,6251,6255,6258,6261,6265,6271,6275,6282,6285,6288,6292,6295,6298,6304,6313,6316,6319,6321,6323,6330,6339,6343,6346,6354,6360,6364,6371,6374,6381,6384,6387,6390,6393,6396,6404,6407,6410,6417,6421,6629],[10,6215,6211],{"id":6216},"flowfuse-architecture",[14,6218,6219],{},"A FlowFuse install is made up of 2 main components",[28,6221,6222,6225],{},[31,6223,6224],{},"The Management Application",[31,6226,6227],{},"The Node-RED instances",[14,6229,6230],{},"These can be deployed in one of 2 ways",[28,6232,6233,6242],{},[31,6234,6235,6236,6238],{},"On a single machine",[662,6237],{},[638,6239],{"alt":6240,"src":6241},"LocalFS Architecture","\u002Fdocs\u002Fcontribute\u002Fimages\u002Fff-localfs.png",[31,6243,6244,6245,6247],{},"Using a Container Orchestration platform (Kubernetes\u002FDocker Compose)",[662,6246],{},[638,6248],{"alt":6249,"src":6250},"Container Architecture","\u002Fdocs\u002Fcontribute\u002Fimages\u002Fff-containers.png",[23,6252,6254],{"id":6253},"flowfuse-management-application","FlowFuse Management Application",[14,6256,6257],{},"This provides the interface for managing the objects in the platform. It also\nprovides a collection of APIs to support the Node-RED instances once started.",[14,6259,6260],{},"A key component is the Container API driver, this is the part that actually creates\u002Fdestroys\nNode-RED instances and keeps track of what should be running and restarts if needed.",[104,6262,6264],{"id":6263},"container-drivers","Container Drivers",[14,6266,6267,6268,1497],{},"Node-RED instances are started by the FlowFuse Management Application via one of the following Container Drivers. Documentation for the Container Driver API will be available in the ",[41,6269,6270],{"href":3953},"API",[768,6272,6274],{"id":6273},"localfs","Localfs",[14,6276,6277,6278,6281],{},"This driver runs Node-RED as separate processes on the same machine as the FlowFuse Management Application. Each instance gets its own ",[18,6279,6280],{},"userDir"," and a dedicated TCP\u002FIP port to listen to.",[14,6283,6284],{},"State is stored in a local SQLite database",[14,6286,6287],{},"There is no automatic Ingres automation provided by this driver.",[768,6289,6291],{"id":6290},"kubernetes","Kubernetes",[14,6293,6294],{},"This driver runs Node-RED in separate containers and each instance is accessed by a dedicated hostname via an HTTP Ingres proxy.",[14,6296,6297],{},"The state is stored in a provided PostgreSQL database.",[14,6299,6300,6301,660],{},"Node-RED containers are segregated into their own namespace (currently hardcoded to ",[18,6302,6303],{},"flowforge",[14,6305,6306,6307,6312],{},"The driver uses the ",[41,6308,6311],{"href":6309,"rel":6310},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@kubernetes\u002Fclient-node",[831],"@kubernetes\u002Fclient-node"," to interact with the cluster.",[14,6314,6315],{},"The driver will create the required Service and Ingres Kubernetes resources to expose each instance via whatever Ingress Controller the underlying Kubernetes cluster provides.",[768,6317,6318],{"id":161},"Docker-Compose",[14,6320,6294],{},[14,6322,6297],{},[14,6324,6306,6325,6312],{},[41,6326,6329],{"href":6327,"rel":6328},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fdockerode",[831],"dockerode",[14,6331,6332,6333,6338],{},"The driver will add the required Environment variables to each Node-RED container to work with the ",[41,6334,6337],{"href":6335,"rel":6336},"https:\u002F\u002Fhub.docker.com\u002Fr\u002Fjwilder\u002Fnginx-proxy",[831],"jwilder\u002Fnginx-proxy"," NGINX proxy.",[23,6340,6342],{"id":6341},"flowfuse-instances","FlowFuse Instances",[14,6344,6345],{},"A FlowFuse Node-RED Instance is made up of 2 processes",[28,6347,6348,6351],{},[31,6349,6350],{},"The FlowFuse Launcher",[31,6352,6353],{},"A Node-RED instance",[14,6355,6356],{},[638,6357],{"alt":6358,"src":6359},"Project Architecture","\u002Fdocs\u002Fcontribute\u002Fimages\u002Fff-project-arch.png",[104,6361,6363],{"id":6362},"flowfuse-launcher","FlowFuse Launcher",[14,6365,6366,6367,6370],{},"This is a small application that handles downloading the Instance specific settings, building a ",[18,6368,6369],{},"settings.js"," from those settings and then starting the Node-RED instance.",[14,6372,6373],{},"The launcher presents an HTTP API (it defaults to the Node-RED port + 1000) that allows the FlowFuse Management Application to start\u002Fstop\u002Frestart the Node-RED instance as well as query its current state and retrieve the console logs.",[14,6375,6376,6377],{},"The launcher can be found ",[41,6378,785],{"href":6379,"rel":6380},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnr-launcher",[831],[14,6382,6383],{},"Within the launcher are some custom plugins that are loaded by Node-RED:",[768,6385,6386],{"id":6386},"nr-storage",[14,6388,6389],{},"This plugin is used to save flows, settings, sessions, and library entries back to the FlowFuse Management Application.",[768,6391,6392],{"id":6392},"nr-auth",[14,6394,6395],{},"This plugin is used to authenticate users trying to access the Node-RED Editor, it refers back to the FlowFuse Management Application to ensure only members of the team that owns the instance can log in.",[14,6397,6398,6399],{},"This plugin uses the Node-RED ",[41,6400,6403],{"href":6401,"rel":6402},"https:\u002F\u002Fnodered.org\u002Fdocs\u002Fuser-guide\u002Fruntime\u002Fsecuring-node-red#custom-user-authentication",[831],"Authentication API",[768,6405,6406],{"id":6406},"nr-audit-logger",[14,6408,6409],{},"This plugin sends Node-RED Audit events (e.g. user log in and flow deployment events) back to the to the FlowFuse Management Application to allow a reliable audit of what actions have taken place in the instance.",[14,6411,6398,6412],{},[41,6413,6416],{"href":6414,"rel":6415},"https:\u002F\u002Fnodered.org\u002Fdocs\u002Fuser-guide\u002Fruntime\u002Flogging",[831],"Logging API",[23,6418,6420],{"id":6419},"component-overview","Component Overview",[50,6422,6426],{"className":6423,"code":6424,"language":6425,"meta":55,"style":55},"language-mermaid shiki shiki-themes github-light github-dark","erDiagram\n    USER ||--o{ NGINX : Requests\n    NGINX {\n        Protocol HTTP-TLS\n        Port default-443\n    }\n    FORGE-APP {\n        Protocol HTTP-TLS\n        Port default-3000\n    }\n    POSTGRESQL {\n        Protocol tcp-tls\n        Port default-5432\n    }\n    MOSQUITTO {\n        Protocol HTTP-TLS-WSS-MQTT\n        Port default-1883\n        Port websocket-1884\n    }\n    FLOWFORGE-FILE-SERVER {\n        Protocol HTTP-TLS\n        Port default-3001\n    }\n    NGINX }o--o| NODE-RED : routes\n    NGINX }o--o{ FORGE-APP: routes\n    NGINX ||--|| MOSQUITTO: mqtt-ws\n    FORGE-APP ||--|{ POSTGRESQL: query\n    FORGE-APP ||--|| NODE-RED: \"flow update\"\n    NODE-RED }o--o{ FLOWFORGE-FILE-SERVER: \"Blob store\"\n    FLOWFORGE-FILE-SERVER ||--|| FORGE-APP: \"Authenticate\"\n    NODE-RED {\n        Protocol HTTP-TLS\n        Port default-1880\n    }\n    NODE-RED }o--|| MOSQUITTO: mqtt\n    FORGE-APP }o--|| MOSQUITTO: mqtt\n    NODE-RED-DEVICES {\n        Port default-1880\n        Protocol User-Defined\n    }\n    NODE-RED-DEVICES }o--|| NGINX: mqtt-ws\n    USER ||--|| NODE-RED-DEVICES: Requests\n","mermaid",[18,6427,6428,6433,6438,6443,6448,6453,6457,6462,6466,6471,6475,6480,6485,6490,6494,6499,6504,6509,6514,6518,6523,6527,6532,6536,6541,6546,6551,6556,6561,6566,6571,6576,6580,6585,6589,6594,6599,6604,6608,6613,6617,6623],{"__ignoreMap":55},[59,6429,6430],{"class":61,"line":62},[59,6431,6432],{},"erDiagram\n",[59,6434,6435],{"class":61,"line":77},[59,6436,6437],{},"    USER ||--o{ NGINX : Requests\n",[59,6439,6440],{"class":61,"line":88},[59,6441,6442],{},"    NGINX {\n",[59,6444,6445],{"class":61,"line":99},[59,6446,6447],{},"        Protocol HTTP-TLS\n",[59,6449,6450],{"class":61,"line":156},[59,6451,6452],{},"        Port default-443\n",[59,6454,6455],{"class":61,"line":216},[59,6456,3324],{},[59,6458,6459],{"class":61,"line":224},[59,6460,6461],{},"    FORGE-APP {\n",[59,6463,6464],{"class":61,"line":233},[59,6465,6447],{},[59,6467,6468],{"class":61,"line":241},[59,6469,6470],{},"        Port default-3000\n",[59,6472,6473],{"class":61,"line":249},[59,6474,3324],{},[59,6476,6477],{"class":61,"line":257},[59,6478,6479],{},"    POSTGRESQL {\n",[59,6481,6482],{"class":61,"line":3137},[59,6483,6484],{},"        Protocol tcp-tls\n",[59,6486,6487],{"class":61,"line":3150},[59,6488,6489],{},"        Port default-5432\n",[59,6491,6492],{"class":61,"line":3163},[59,6493,3324],{},[59,6495,6496],{"class":61,"line":3176},[59,6497,6498],{},"    MOSQUITTO {\n",[59,6500,6501],{"class":61,"line":3187},[59,6502,6503],{},"        Protocol HTTP-TLS-WSS-MQTT\n",[59,6505,6506],{"class":61,"line":3193},[59,6507,6508],{},"        Port default-1883\n",[59,6510,6511],{"class":61,"line":3201},[59,6512,6513],{},"        Port websocket-1884\n",[59,6515,6516],{"class":61,"line":3214},[59,6517,3324],{},[59,6519,6520],{"class":61,"line":3222},[59,6521,6522],{},"    FLOWFORGE-FILE-SERVER {\n",[59,6524,6525],{"class":61,"line":3233},[59,6526,6447],{},[59,6528,6529],{"class":61,"line":3239},[59,6530,6531],{},"        Port default-3001\n",[59,6533,6534],{"class":61,"line":3247},[59,6535,3324],{},[59,6537,6538],{"class":61,"line":3256},[59,6539,6540],{},"    NGINX }o--o| NODE-RED : routes\n",[59,6542,6543],{"class":61,"line":3261},[59,6544,6545],{},"    NGINX }o--o{ FORGE-APP: routes\n",[59,6547,6548],{"class":61,"line":3269},[59,6549,6550],{},"    NGINX ||--|| MOSQUITTO: mqtt-ws\n",[59,6552,6553],{"class":61,"line":3278},[59,6554,6555],{},"    FORGE-APP ||--|{ POSTGRESQL: query\n",[59,6557,6558],{"class":61,"line":3284},[59,6559,6560],{},"    FORGE-APP ||--|| NODE-RED: \"flow update\"\n",[59,6562,6563],{"class":61,"line":3289},[59,6564,6565],{},"    NODE-RED }o--o{ FLOWFORGE-FILE-SERVER: \"Blob store\"\n",[59,6567,6568],{"class":61,"line":3297},[59,6569,6570],{},"    FLOWFORGE-FILE-SERVER ||--|| FORGE-APP: \"Authenticate\"\n",[59,6572,6573],{"class":61,"line":3310},[59,6574,6575],{},"    NODE-RED {\n",[59,6577,6578],{"class":61,"line":3321},[59,6579,6447],{},[59,6581,6582],{"class":61,"line":3327},[59,6583,6584],{},"        Port default-1880\n",[59,6586,6587],{"class":61,"line":3333},[59,6588,3324],{},[59,6590,6591],{"class":61,"line":6115},[59,6592,6593],{},"    NODE-RED }o--|| MOSQUITTO: mqtt\n",[59,6595,6596],{"class":61,"line":6152},[59,6597,6598],{},"    FORGE-APP }o--|| MOSQUITTO: mqtt\n",[59,6600,6601],{"class":61,"line":6158},[59,6602,6603],{},"    NODE-RED-DEVICES {\n",[59,6605,6606],{"class":61,"line":6164},[59,6607,6584],{},[59,6609,6610],{"class":61,"line":6170},[59,6611,6612],{},"        Protocol User-Defined\n",[59,6614,6615],{"class":61,"line":6175},[59,6616,3324],{},[59,6618,6620],{"class":61,"line":6619},41,[59,6621,6622],{},"    NODE-RED-DEVICES }o--|| NGINX: mqtt-ws\n",[59,6624,6626],{"class":61,"line":6625},42,[59,6627,6628],{},"    USER ||--|| NODE-RED-DEVICES: Requests\n",[316,6630,6631],{},"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":55,"searchDepth":77,"depth":77,"links":6633},[6634,6637,6640],{"id":6253,"depth":77,"text":6254,"children":6635},[6636],{"id":6263,"depth":88,"text":6264},{"id":6341,"depth":77,"text":6342,"children":6638},[6639],{"id":6362,"depth":88,"text":6363},{"id":6419,"depth":77,"text":6420},{},"contribute\u002Farchitecture.md","\u002Fdocs\u002Fcontribute\u002Farchitecture",{"title":6211,"description":6219},"docs\u002Fcontribute\u002Farchitecture","ehgfsgpF92k0QSb_4qLYsP5ezk5PDost6T-Cv2yuAQ0",{"id":6648,"title":6649,"body":6650,"description":6654,"extension":329,"layout":330,"meta":6829,"navGroup":330,"navOrder":330,"navTitle":6830,"navigation":187,"originalPath":6831,"path":6832,"redirect":330,"seo":6833,"stem":6834,"updated":337,"version":338,"__hash__":6835},"docs\u002Fdocs\u002Fcontribute\u002Fcreating-debug-stack-containers.md","Creating Debug Stack Containers",{"type":7,"value":6651,"toc":6823},[6652,6655,6662,6665,6668,6675,6679,6690,6699,6702,6708,6711,6717,6724,6727,6747,6754,6765,6771,6774,6780,6784,6793,6796,6800,6803,6809,6816,6820],[14,6653,6654],{},"Sometimes we want to be able to run some debug code within a stack running in our\nstaging test environment.",[14,6656,6657,6658,6661],{},"For example, changes to the ",[18,6659,6660],{},"nr-launcher"," component or any of the other components\nthat run within the stack.",[14,6663,6664],{},"This guide shows a simple way to do that without having to rebuild the container\nfrom scratch each time.",[14,6666,6667],{},"This will require:",[398,6669,6670,6672],{},[31,6671,1209],{},[31,6673,6674],{},"A container registry you can push images to. For example, a free DockerHub account where you can push images",[104,6676,6678],{"id":6677},"creating-a-debug-container","Creating a debug container",[14,6680,6681,6682,6685,6686,6689],{},"Pick an existing container image to use as your starting point. For example, ",[18,6683,6684],{},"flowfuse\u002Fnode-red:2.5.0-4.0.x","\ncontains ",[18,6687,6688],{},"nr-launcher@2.5.0"," and the latest Node-RED 4.x release.",[14,6691,6692,6694,6695,6698],{},[364,6693,1760],{},": our staging environments requires ",[18,6696,6697],{},"arm64"," based containers. The following instructions work for\nMacs (with M1\u002FM2) - additional steps may be needed for other OS; please contribute them if you know them.",[14,6700,6701],{},"Use the following command to open a shell into the container:",[50,6703,6706],{"className":6704,"code":6705,"language":3920},[3918],"docker run -it --entrypoint \u002Fbin\u002Fbash flowfuse\u002Fnode-red:2.5.0-4.0.x\n",[18,6707,6705],{"__ignoreMap":55},[14,6709,6710],{},"The prompt will then look like this:",[50,6712,6715],{"className":6713,"code":6714,"language":3920},[3918],"e8dcd669ea4c:\u002Fusr\u002Fsrc\u002Fflowforge-nr-launcher$\n",[18,6716,6714],{"__ignoreMap":55},[14,6718,6719,6720,6723],{},"Take a note of the ",[18,6721,6722],{},"e8dcd669ea4c"," - this is the id of the container instance you have created.",[14,6725,6726],{},"The following directories are probably of interest:",[28,6728,6729,6738],{},[31,6730,6731,6734,6735,6737],{},[18,6732,6733],{},"\u002Fusr\u002Fsrc\u002Fflowforge-nr-launcher"," - contains the ",[18,6736,6660],{}," code and its dependencies",[31,6739,6740,6734,6743,6746],{},[18,6741,6742],{},"\u002Fusr\u002Fsrc\u002Fnode-red",[18,6744,6745],{},"node-red"," code",[14,6748,6749,6750,6753],{},"Using ",[18,6751,6752],{},"vi",", you can edit the files to make the changes you want and when you're done, exit the shell.",[14,6755,6756,6757,6760,6761,6764],{},"Having made the changes, use ",[18,6758,6759],{},"docker commit"," to create a new container image. The command requires\nthe id of the container you've just edited and the tag for the container. It also needs to restore\nthe ",[18,6762,6763],{},"entrypoint"," configuration back to the default.",[50,6766,6769],{"className":6767,"code":6768,"language":3920},[3918],"docker commit \\\n  --change='ENTRYPOINT [\".\u002Fnode_modules\u002F.bin\u002Fflowfuse-node-red\", \"-p\", \"2880\", \"-n\", \"\u002Fusr\u002Fsrc\u002Fnode-red\"]' \\\n  e8dcd669ea4c \\\n  knolleary\u002Fff-debug:debug-1\n",[18,6770,6768],{"__ignoreMap":55},[14,6772,6773],{},"Finally, you can push the new container to your container registry.",[50,6775,6778],{"className":6776,"code":6777,"language":3920},[3918],"docker push knolleary\u002Fff-debug:debug-1\n",[18,6779,6777],{"__ignoreMap":55},[104,6781,6783],{"id":6782},"using-the-container-in-flowfuse","Using the container in FlowFuse",[14,6785,6786,6787,6789,6790,6792],{},"Debug containers should ",[1160,6788,1355],{}," be used in pre-staging\u002Fstaging environments. Do ",[1160,6791,1230],{}," add to production.",[14,6794,6795],{},"Once pushed, you can create a custom stack in the FlowFuse admin section and give it the location\nof your container.",[104,6797,6799],{"id":6798},"iterating","Iterating",[14,6801,6802],{},"If you find you want to add some more debug, repeat the process, however use your existing image\nas the starting point:",[50,6804,6807],{"className":6805,"code":6806,"language":3920},[3918],"docker run -it --entrypoint \u002Fbin\u002Fbash knolleary\u002Fff-debug:debug-1\n",[18,6808,6806],{"__ignoreMap":55},[14,6810,6811,6812,6815],{},"Be sure to increment the number in the image name (",[18,6813,6814],{},"debug-2",") when you commit and push the new container.",[104,6817,6819],{"id":6818},"tidying-up","Tidying up",[14,6821,6822],{},"Remember to delete your instances\u002Fstacks once you're done, as well as all of the local\ndocker images and containers that have been created along the way.",{"title":55,"searchDepth":77,"depth":77,"links":6824},[6825,6826,6827,6828],{"id":6677,"depth":88,"text":6678},{"id":6782,"depth":88,"text":6783},{"id":6798,"depth":88,"text":6799},{"id":6818,"depth":88,"text":6819},{},"Creating debug stack containers","contribute\u002Fcreating-debug-stack-containers.md","\u002Fdocs\u002Fcontribute\u002Fcreating-debug-stack-containers",{"description":6654},"docs\u002Fcontribute\u002Fcreating-debug-stack-containers","C9zXR2m7s-YTLsYC2_tfJnXo2G7twLnA8D_9XIPrRTM",{"id":6837,"title":6838,"body":6839,"description":6846,"extension":329,"layout":330,"meta":7055,"navGroup":330,"navOrder":330,"navTitle":7056,"navigation":187,"originalPath":7057,"path":7058,"redirect":330,"seo":7059,"stem":7060,"updated":337,"version":338,"__hash__":7061},"docs\u002Fdocs\u002Fcontribute\u002Fdb-migrations.md","Database Migrations",{"type":7,"value":6840,"toc":7045},[6841,6844,6847,6850,6854,6858,6865,6871,6890,6896,6902,6906,6909,6989,6995,7001,7012,7016,7019,7023,7026,7029,7033,7036,7039,7042],[10,6842,6838],{"id":6843},"database-migrations",[14,6845,6846],{},"Any changes made to the database models must include migrations\nthat can modify the database state from one state to another.",[14,6848,6849],{},"Whilst we use Sequelize as our ORM layer, we do not use the migration tooling\nit provides - we have our own.",[23,6851,6853],{"id":6852},"creating-migrations","Creating migrations",[104,6855,6857],{"id":6856},"filename","Filename",[14,6859,6860,6861,6864],{},"A migration is provided as JavaScript in the directory ",[18,6862,6863],{},"forge\u002Fdb\u002Fmigrations",".\nIts file name must follow the pattern:",[50,6866,6869],{"className":6867,"code":6868,"language":3920},[3918],"YYYYMMDD-nn-description.js\n",[18,6870,6868],{"__ignoreMap":55},[28,6872,6873,6879,6885],{},[31,6874,6875,6878],{},[18,6876,6877],{},"YYYYMMDD"," - the date the migration is added",[31,6880,6881,6884],{},[18,6882,6883],{},"nn"," - a two digit number",[31,6886,6887,6889],{},[18,6888,440],{}," - a name for the migration",[14,6891,6892,6893,273],{},"For example ",[18,6894,6895],{},"20220204-01-add-billing.js",[14,6897,6898,6899,6901],{},"This ensures the migrations have a natural order to be applied. The ",[18,6900,6883],{}," part of\nthe name allows multiple migrations to be added on the same day but is kept in the\nright order.",[104,6903,6905],{"id":6904},"structure","Structure",[14,6907,6908],{},"The migration code should use the following layout:",[50,6910,6912],{"className":4989,"code":6911,"language":4991,"meta":55,"style":55},"module.exports = {\n    up: async (context) => {\n        \u002F\u002F Apply the migration\n    },\n    down: async (context) => {\n        \u002F\u002F Remove the migration\n    }\n}\n",[18,6913,6914,6928,6948,6953,6957,6976,6981,6985],{"__ignoreMap":55},[59,6915,6916,6919,6921,6924,6926],{"class":61,"line":62},[59,6917,6918],{"class":73},"module",[59,6920,273],{"class":178},[59,6922,6923],{"class":73},"exports",[59,6925,5004],{"class":1372},[59,6927,5181],{"class":178},[59,6929,6930,6933,6935,6937,6939,6942,6944,6946],{"class":61,"line":77},[59,6931,6932],{"class":65},"    up",[59,6934,179],{"class":178},[59,6936,5160],{"class":1372},[59,6938,5163],{"class":178},[59,6940,6941],{"class":5166},"context",[59,6943,5175],{"class":178},[59,6945,5178],{"class":1372},[59,6947,5181],{"class":178},[59,6949,6950],{"class":61,"line":88},[59,6951,6952],{"class":3773},"        \u002F\u002F Apply the migration\n",[59,6954,6955],{"class":61,"line":99},[59,6956,3190],{"class":178},[59,6958,6959,6962,6964,6966,6968,6970,6972,6974],{"class":61,"line":156},[59,6960,6961],{"class":65},"    down",[59,6963,179],{"class":178},[59,6965,5160],{"class":1372},[59,6967,5163],{"class":178},[59,6969,6941],{"class":5166},[59,6971,5175],{"class":178},[59,6973,5178],{"class":1372},[59,6975,5181],{"class":178},[59,6977,6978],{"class":61,"line":216},[59,6979,6980],{"class":3773},"        \u002F\u002F Remove the migration\n",[59,6982,6983],{"class":61,"line":224},[59,6984,3324],{"class":178},[59,6986,6987],{"class":61,"line":233},[59,6988,3336],{"class":178},[14,6990,372,6991,6994],{},[18,6992,6993],{},"up"," function applies to the migration. This can be to create new tables, add columns\nto existing ones - whatever is needed.",[14,6996,372,6997,7000],{},[18,6998,6999],{},"down"," function reverses the migration. It should restore the database back to\nhow it was prior to the migration.",[14,7002,372,7003,7005,7006,7011],{},[18,7004,6941],{}," argument is an instance of ",[41,7007,7010],{"href":7008,"rel":7009},"https:\u002F\u002Fsequelize.org\u002Fdocs\u002Fv6\u002Fother-topics\u002Fquery-interface\u002F",[831],"Sequelize.QueryInterface"," that can be used to perform\noperations on the database.",[23,7013,7015],{"id":7014},"applying-migrations","Applying migrations",[14,7017,7018],{},"Migrations are applied automatically at the start of the FlowFuse application. Down\nmigrations are not yet supported.",[23,7020,7022],{"id":7021},"considerations-when-writing-migrations","Considerations when writing migrations",[14,7024,7025],{},"Whilst migrations give us the ability to make changes to the database, they must\nbe used with great care. A failing migration will prevent the platform from starting\nand may require manual intervention. Everything should be done to avoid that from\nhappening.",[14,7027,7028],{},"Certain types of migration need particular guidance and care over.",[104,7030,7032],{"id":7031},"adding-constraints","Adding constraints",[14,7034,7035],{},"If a migration is adding a new constraint to an existing table, you need to consider\nvery carefully what impact that could have on an existing system with real data.",[14,7037,7038],{},"For example, adding a new 'unique' constraint where you cannot guarantee that\nconstraint hasn't already been broken. What strategy will you use to guard against\nthat or to help recover from it? What additional testing is needed for the migration\nto verify its behavior in those situations?",[14,7040,7041],{},"The preferred method to add a new unique constraint is by adding a new index to the\ndatabase. This is because SQLite doesn't provide a way to alter columns that doesn't\ninvolve dropping the whole table and triggering any cascade triggers.",[316,7043,7044],{},"html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}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":55,"searchDepth":77,"depth":77,"links":7046},[7047,7051,7052],{"id":6852,"depth":77,"text":6853,"children":7048},[7049,7050],{"id":6856,"depth":88,"text":6857},{"id":6904,"depth":88,"text":6905},{"id":7014,"depth":77,"text":7015},{"id":7021,"depth":77,"text":7022,"children":7053},[7054],{"id":7031,"depth":88,"text":7032},{},"Database migrations","contribute\u002Fdb-migrations.md","\u002Fdocs\u002Fcontribute\u002Fdb-migrations",{"title":6838,"description":6846},"docs\u002Fcontribute\u002Fdb-migrations","IUmzH2NjzBAtqgYGdgvW0pjhjlrP7dR47oBklb2xXhI",{"id":7063,"title":7064,"body":7065,"description":7069,"extension":329,"layout":330,"meta":7323,"navGroup":330,"navOrder":330,"navTitle":7324,"navigation":187,"originalPath":7325,"path":7326,"redirect":330,"seo":7327,"stem":7328,"updated":337,"version":338,"__hash__":7329},"docs\u002Fdocs\u002Fcontribute\u002Ffeature-flags.md","Feature Flags",{"type":7,"value":7066,"toc":7316},[7067,7070,7079,7085,7091,7095,7098,7104,7112,7116,7126,7146,7150,7153,7159,7172,7178,7183,7190,7194,7205,7211,7218,7224,7227,7230,7241,7245,7267,7282,7300,7306],[14,7068,7069],{},"When adding features to the platform it is sometimes a requirement to be able to\nrestrict the feature to licensed platforms, and furthermore to certain types of team\non the platform.",[14,7071,7072,7073,7075,7076,7078],{},"Most typically this will be a feature that should only be available to the ",[18,7074,4154],{},"\nor ",[18,7077,1884],{}," tiers on FlowFuse Cloud.",[14,7080,7081,7082,273],{},"This is a quick guide for how to add a feature flag - both at the platform-wide\nlevel and against individual ",[18,7083,7084],{},"TeamTypes",[14,7086,7087,7088,273],{},"Feature flag names should use ",[18,7089,7090],{},"camelCase",[104,7092,7094],{"id":7093},"add-a-platform-wide-feature-flag","Add a platform-wide feature flag",[14,7096,7097],{},"All licensable features should set a platform-wide feature flag to indicate\nthe feature is available.",[50,7099,7102],{"className":7100,"code":7101,"language":3920},[3918],"app.config.features.register('featureFlagName', true)\n",[18,7103,7101],{"__ignoreMap":55},[14,7105,7106,7111],{},[41,7107,7110],{"href":7108,"rel":7109},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Fblob\u002F0335c9056019ff9987d97f3ad3f18675de1c2422\u002Fforge\u002Fee\u002Flib\u002Fha\u002Findex.js#L6",[831],"Here"," is\nan example of how the HA feature sets its platform-wide flag.",[104,7113,7115],{"id":7114},"add-a-team-type-feature-flag","Add a team type feature flag",[14,7117,7118,7119,273],{},"The feature flag is set by an admin user via ",[41,7120,7123],{"href":7121,"rel":7122},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Fblob\u002F0335c9056019ff9987d97f3ad3f18675de1c2422\u002Ffrontend\u002Fsrc\u002Fpages\u002Fadmin\u002FTeamTypes\u002Fdialogs\u002FTeamTypeEditDialog.vue",[831],[18,7124,7125],{},"TeamTypeEditDialog.vue",[398,7127,7128,7135],{},[31,7129,7130,7131,273],{},"Add an entry to the existing list of feature flags ",[41,7132,785],{"href":7133,"rel":7134},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Fblob\u002F0335c9056019ff9987d97f3ad3f18675de1c2422\u002Ffrontend\u002Fsrc\u002Fpages\u002Fadmin\u002FTeamTypes\u002Fdialogs\u002FTeamTypeEditDialog.vue#L73-L85",[831],[31,7136,7137,7138,7142,7143,7145],{},"Add a check to ensure the right default value is applied ",[41,7139,785],{"href":7140,"rel":7141},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Fblob\u002F0335c9056019ff9987d97f3ad3f18675de1c2422\u002Ffrontend\u002Fsrc\u002Fpages\u002Fadmin\u002FTeamTypes\u002Fdialogs\u002FTeamTypeEditDialog.vue#L172-L174",[831],". Any new feature should default to ",[18,7144,659],{}," so it can then be selectively enabled.",[104,7147,7149],{"id":7148},"using-the-feature-flags-runtime-side","Using the feature flags - runtime side",[14,7151,7152],{},"Platform-wide feature flags can be checked using:",[50,7154,7157],{"className":7155,"code":7156,"language":3920},[3918],"const isFeatureEnabledOnPlatform = app.config.features.enabled('featureFlagName')\n\n",[18,7158,7156],{"__ignoreMap":55},[14,7160,7161,7164,7165,7168,7169,7171],{},[18,7162,7163],{},"TeamType"," feature flags can be checked using the ",[18,7166,7167],{},"getFeatureProperty"," function\nof the ",[18,7170,7163],{}," model:",[50,7173,7176],{"className":7174,"code":7175,"language":3920},[3918],"\u002F\u002F myTeamType is an instance of `TeamType`\nconst isFeatureEnabledForTeamType = myTeamType.getFeatureProperty('featureFlagName', false)\n",[18,7177,7175],{"__ignoreMap":55},[14,7179,7180,7181,273],{},"The first arg is the name of the feature flag, the second arg is the default value\nif the feature flag is otherwise unset. As mentioned above, any new feature should\ndefault to ",[18,7182,659],{},[14,7184,7185,7189],{},[41,7186,7110],{"href":7187,"rel":7188},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Fblob\u002F0335c9056019ff9987d97f3ad3f18675de1c2422\u002Fforge\u002Fee\u002Froutes\u002FsharedLibrary\u002Findex.js#L22",[831]," is an example of this in action.",[104,7191,7193],{"id":7192},"using-the-feature-flag-frontend","Using the feature flag - frontend",[14,7195,7196,7197,7200,7201,7204],{},"In the frontend, platform-wide feature flags can be checked against\nthe ",[18,7198,7199],{},"features"," property of the ",[18,7202,7203],{},"account"," store.",[14,7206,7207,7208,273],{},"TeamType feature flags can be checked against ",[18,7209,7210],{},"team.type.properties.features.featureFlagName",[14,7212,7213,7217],{},[41,7214,7110],{"href":7215,"rel":7216},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Fblob\u002F0335c9056019ff9987d97f3ad3f18675de1c2422\u002Ffrontend\u002Fsrc\u002Fpages\u002Fapplication\u002FDeviceGroups.vue#L135-L146",[831]," is an example of how we combine these two things:",[50,7219,7222],{"className":7220,"code":7221,"language":3920},[3918],"    computed: {\n        ...mapState('account', ['features']),\n        featureEnabledForTeam () {\n            return !!this.team.type.properties.features?.deviceGroups\n        },\n        featureEnabledForPlatform () {\n            return this.features.deviceGroups\n        },\n        featureEnabled () {\n            return this.featureEnabledForTeam && this.featureEnabledForPlatform\n        }\n    },\n",[18,7223,7221],{"__ignoreMap":55},[14,7225,7226],{},"This allows the UI to distinguish between a feature being unavailable because the platform\nis not licensed for it, and a feature being unavailable for the current team type.",[14,7228,7229],{},"This allows different messages to be displayed with the most appropriate call to action.",[14,7231,372,7232,7235,7236,7240],{},[18,7233,7234],{},"EmptyState"," component has support for this - see ",[41,7237,785],{"href":7238,"rel":7239},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Fblob\u002F0335c9056019ff9987d97f3ad3f18675de1c2422\u002Ffrontend\u002Fsrc\u002Fpages\u002Fapplication\u002FDeviceGroups.vue#L29",[831],"\nfor how that is applied.",[104,7242,7244],{"id":7243},"using-the-feature_configs-composable","Using the FEATURE_CONFIGS composable",[14,7246,7247,7248,7251,7252,7255,7256,7259,7260,7263,7264,7204],{},"The recommended approach for frontend feature checks is to use the ",[18,7249,7250],{},"FEATURE_CONFIGS"," array\nin ",[18,7253,7254],{},"frontend\u002Fsrc\u002Fcomposables\u002FFeatureChecks.ts",". Each entry defines a feature with platform\nand\u002For team level checks, and the ",[18,7257,7258],{},"buildFeatureChecks"," function produces computed properties\nthat are available via the ",[18,7261,7262],{},"featuresCheck"," getter on the ",[18,7265,7266],{},"account-settings",[14,7268,7269,7270,7273,7274,7277,7278,7281],{},"Features can be ",[364,7271,7272],{},"opt-in"," (disabled by default, must be explicitly enabled per team type) or\n",[364,7275,7276],{},"opt-out"," (enabled by default, must be explicitly disabled). Opt-out features use ",[18,7279,7280],{},"optOut: true","\nin their config entry.",[14,7283,7284,7285,3012,7288,7291,7292,7295,7296,7299],{},"Features can also declare dependencies on other features using ",[18,7286,7287],{},"dependsOn",[18,7289,7290],{},"dependsOnPlatform",",\nand ",[18,7293,7294],{},"dependsOnTeam",". For example, all AI sub-features depend on the ",[18,7297,7298],{},"ai"," flag:",[50,7301,7304],{"className":7302,"code":7303,"language":3920},[3918],"{ output: 'isExpertAssistantFeatureEnabled', platformKey: 'expertAssistant', teamKey: 'expertAssistant', optOut: true, dependsOnPlatform: 'ai', dependsOnTeam: 'ai', dependsOnTeamOptOut: true }\n",[18,7305,7303],{"__ignoreMap":55},[14,7307,7308,7309,7311,7312,7315],{},"See the JSDoc on the ",[18,7310,7250],{}," array in ",[18,7313,7314],{},"FeatureChecks.ts"," for full documentation\nof all available options.",{"title":55,"searchDepth":77,"depth":77,"links":7317},[7318,7319,7320,7321,7322],{"id":7093,"depth":88,"text":7094},{"id":7114,"depth":88,"text":7115},{"id":7148,"depth":88,"text":7149},{"id":7192,"depth":88,"text":7193},{"id":7243,"depth":88,"text":7244},{},"Working with Feature Flags","contribute\u002Ffeature-flags.md","\u002Fdocs\u002Fcontribute\u002Ffeature-flags",{"description":7069},"docs\u002Fcontribute\u002Ffeature-flags","UahZ5MHkG66qFCUiF8YxLY00SGbaDxTuYMzwPHH-egw",{"id":7331,"title":55,"body":7332,"description":55,"extension":329,"layout":532,"meta":7336,"navGroup":7337,"navOrder":330,"navTitle":7338,"navigation":187,"originalPath":7339,"path":7340,"redirect":7341,"seo":7343,"stem":7344,"updated":337,"version":338,"__hash__":7345},"docs\u002Fdocs\u002Fcontribute\u002Findex.md",{"type":7,"value":7333,"toc":7334},[],{"title":55,"searchDepth":77,"depth":77,"links":7335},[],{},"Contributing","Contributing to FlowFuse","contribute\u002FREADME.md","\u002Fdocs\u002Fcontribute",{"to":7342},"\u002Fdocs\u002Fcontribute\u002Fintroduction",{"description":55},"docs\u002Fcontribute\u002Findex","UID18P_4R53R8Ysp2Pw684lZnlowK2RVI3zM6i6eWeQ",{"id":7347,"title":7338,"body":7348,"description":7355,"extension":329,"layout":330,"meta":9033,"navGroup":330,"navOrder":62,"navTitle":4659,"navigation":187,"originalPath":9034,"path":7342,"redirect":330,"seo":9035,"stem":9036,"updated":337,"version":338,"__hash__":9037},"docs\u002Fdocs\u002Fcontribute\u002Fintroduction.md",{"type":7,"value":7349,"toc":9017},[7350,7353,7356,7359,7363,7366,7399,7403,7412,7495,7499,7502,7511,7514,7555,7558,7565,7640,7647,7651,7657,7797,7801,7844,7847,7850,7857,7874,7880,7884,7887,7901,7904,7916,7919,7935,7938,7960,7966,7969,7982,7996,7999,8007,8015,8031,8034,8045,8048,8051,8070,8074,8077,8087,8090,8278,8291,8316,8322,8326,8329,8336,8339,8342,8345,8360,8363,8370,8373,8377,8380,8412,8416,8419,8548,8552,8562,8600,8603,8606,8630,8633,8641,8643,8988,9014],[10,7351,7338],{"id":7352},"contributing-to-flowfuse",[14,7354,7355],{},"This guide will help you get setup to contribute to the FlowFuse project.",[14,7357,7358],{},"The core of the FlowFuse platform is available under the Apache-2.0 license and\nwe welcome contributions from the community.",[104,7360,7362],{"id":7361},"software-requirements","Software Requirements",[14,7364,7365],{},"This guide assumes you have a working development environment including:",[28,7367,7368,7371,7396],{},[31,7369,7370],{},"Node.js 18\u002F20",[31,7372,7373,7374],{},"Platform build tools\n",[28,7375,7376,7382,7388],{},[31,7377,7378,7379],{},"Linux: ",[18,7380,7381],{},"apt-get install build-essential",[31,7383,7384,7385],{},"MacOS: ",[18,7386,7387],{},"xcode-select --install",[31,7389,7390,7391],{},"Windows: installed as part of the official node.js installer\n",[28,7392,7393],{},[31,7394,7395],{},"☑️ Automatically install the necessary tools must be checked",[31,7397,7398],{},"Git",[104,7400,7402],{"id":7401},"project-repositories","Project Repositories",[14,7404,7405,7406,7411],{},"There are a number of repositories under the ",[41,7407,7410],{"href":7408,"rel":7409},"https:\u002F\u002Fgithub.com\u002FFlowFuse",[831],"FlowFuse GitHub organisation","\nthat make up the platform.",[2289,7413,7414,7423],{},[2292,7415,7416],{},[2295,7417,7418,7421],{},[2298,7419,7420],{},"Repository",[2298,7422,3348],{},[2305,7424,7425,7437,7449,7461,7473,7485],{},[2295,7426,7427,7434],{},[2310,7428,7429],{},[41,7430,7433],{"href":7431,"rel":7432},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse",[831],"FlowFuse",[2310,7435,7436],{},"This is the core of the platform.",[2295,7438,7439,7446],{},[2310,7440,7441],{},[41,7442,7445],{"href":7443,"rel":7444},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Finstaller",[831],"installer",[2310,7447,7448],{},"The installer for the platform",[2295,7450,7451,7458],{},[2310,7452,7453],{},[41,7454,7457],{"href":7455,"rel":7456},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdriver-localfs",[831],"driver-localfs",[2310,7459,7460],{},"The LocalFS driver. This deploys instances to the local system.",[2295,7462,7463,7470],{},[2310,7464,7465],{},[41,7466,7469],{"href":7467,"rel":7468},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdriver-docker",[831],"driver-docker",[2310,7471,7472],{},"The Docker driver. This deploys instances as containers in a Docker-managed environment.",[2295,7474,7475,7482],{},[2310,7476,7477],{},[41,7478,7481],{"href":7479,"rel":7480},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdriver-k8s",[831],"driver-k8s",[2310,7483,7484],{},"The Kubernetes driver. This deploys instances as containers in a Kubernetes-managed environment.",[2295,7486,7487,7492],{},[2310,7488,7489],{},[41,7490,6660],{"href":6379,"rel":7491},[831],[2310,7493,7494],{},"The launcher application is used to start and monitor an individual instance of Node-RED in the FlowFuse platform. This includes a number of Node-RED plugins used to integrate with the FlowFuse platform.",[104,7496,7498],{"id":7497},"setting-up-a-development-environment","Setting Up A Development Environment",[14,7500,7501],{},"With the project split across multiple repositories, setting up a development\nenvironment manually takes quite a lot of steps to ensure everything is checked\nout and configured properly.",[14,7503,7504,7505,7510],{},"To make it easier, you can use the ",[41,7506,7509],{"href":7507,"rel":7508},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdev-env",[831],"FlowFuse Development Environment"," project to get set up.",[14,7512,7513],{},"The following steps will get your development environment setup in no time:",[50,7515,7517],{"className":52,"code":7516,"language":54,"meta":55,"style":55},"git clone https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdev-env.git\ncd dev-env\nnpm install\nnpm run init\n",[18,7518,7519,7530,7538,7546],{"__ignoreMap":55},[59,7520,7521,7524,7527],{"class":61,"line":62},[59,7522,7523],{"class":65},"git",[59,7525,7526],{"class":69}," clone",[59,7528,7529],{"class":69}," https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdev-env.git\n",[59,7531,7532,7535],{"class":61,"line":77},[59,7533,7534],{"class":73},"cd",[59,7536,7537],{"class":69}," dev-env\n",[59,7539,7540,7543],{"class":61,"line":88},[59,7541,7542],{"class":65},"npm",[59,7544,7545],{"class":69}," install\n",[59,7547,7548,7550,7552],{"class":61,"line":99},[59,7549,7542],{"class":65},[59,7551,70],{"class":69},[59,7553,7554],{"class":69}," init\n",[14,7556,7557],{},"This clones all of the main project repositories, installs their dependencies and builds\nthe repositories that need it.",[14,7559,7560,7561,7564],{},"All of the repositories are cloned under the ",[18,7562,7563],{},"packages"," directory:",[50,7566,7568],{"className":5055,"code":7567,"language":5057,"meta":55,"style":55},"dev-env\n└── packages\n    ├── device-agent\n    ├── docker-compose\n    ├── driver-docker\n    ├── driver-k8s\n    ├── driver-localfs\n    ├── file-server\n    ├── flowfuse\n    ├── helm\n    ├── installer\n    ├── nr-file-nodes\n    ├── nr-launcher\n    └── nr-project-nodes\n",[18,7569,7570,7575,7580,7585,7590,7595,7600,7605,7610,7615,7620,7625,7630,7635],{"__ignoreMap":55},[59,7571,7572],{"class":61,"line":62},[59,7573,7574],{},"dev-env\n",[59,7576,7577],{"class":61,"line":77},[59,7578,7579],{},"└── packages\n",[59,7581,7582],{"class":61,"line":88},[59,7583,7584],{},"    ├── device-agent\n",[59,7586,7587],{"class":61,"line":99},[59,7588,7589],{},"    ├── docker-compose\n",[59,7591,7592],{"class":61,"line":156},[59,7593,7594],{},"    ├── driver-docker\n",[59,7596,7597],{"class":61,"line":216},[59,7598,7599],{},"    ├── driver-k8s\n",[59,7601,7602],{"class":61,"line":224},[59,7603,7604],{},"    ├── driver-localfs\n",[59,7606,7607],{"class":61,"line":233},[59,7608,7609],{},"    ├── file-server\n",[59,7611,7612],{"class":61,"line":241},[59,7613,7614],{},"    ├── flowfuse\n",[59,7616,7617],{"class":61,"line":249},[59,7618,7619],{},"    ├── helm\n",[59,7621,7622],{"class":61,"line":257},[59,7623,7624],{},"    ├── installer\n",[59,7626,7627],{"class":61,"line":3137},[59,7628,7629],{},"    ├── nr-file-nodes\n",[59,7631,7632],{"class":61,"line":3150},[59,7633,7634],{},"    ├── nr-launcher\n",[59,7636,7637],{"class":61,"line":3163},[59,7638,7639],{},"    └── nr-project-nodes\n",[14,7641,7642,7643,273],{},"More details on using the FlowForge Development Environment are available in its\n",[41,7644,7646],{"href":7507,"rel":7645},[831],"documentation",[104,7648,7650],{"id":7649},"flowfuse-code-structure","FlowFuse Code Structure",[14,7652,372,7653,7656],{},[18,7654,7655],{},"FlowFuse\u002Fflowfuse"," repository is the core of the platform and where you'll\nlikely want to begin.",[50,7658,7660],{"className":5055,"code":7659,"language":5057,"meta":55,"style":55},".\n├── bin\n├── config               - build config files\n├── docs\n├── etc                  - FlowFuse platform configuration files\n├── forge                - Platform core code\n│   ├── config\n│   ├── containers\n│   ├── db\n│   ├── ee\n│   ├── lib\n│   ├── licensing\n│   ├── monitor\n│   ├── postoffice\n│   ├── routes\n│   └── settings\n├── frontend             - Frontend code\n│   ├── dist             - build output - created by `npm run build`\n│   ├── public           - static assets\n│   └── src              - vue src\n│       ├── api\n│       ├── components\n│       ├── pages\n│       ├── routes\n│       └── store\n├── test                 - tests for FlowFuse\n└── var                  - where the database and localfs project directories are created\n",[18,7661,7662,7667,7672,7677,7682,7687,7692,7697,7702,7707,7712,7717,7722,7727,7732,7737,7742,7747,7752,7757,7762,7767,7772,7777,7782,7787,7792],{"__ignoreMap":55},[59,7663,7664],{"class":61,"line":62},[59,7665,7666],{},".\n",[59,7668,7669],{"class":61,"line":77},[59,7670,7671],{},"├── bin\n",[59,7673,7674],{"class":61,"line":88},[59,7675,7676],{},"├── config               - build config files\n",[59,7678,7679],{"class":61,"line":99},[59,7680,7681],{},"├── docs\n",[59,7683,7684],{"class":61,"line":156},[59,7685,7686],{},"├── etc                  - FlowFuse platform configuration files\n",[59,7688,7689],{"class":61,"line":216},[59,7690,7691],{},"├── forge                - Platform core code\n",[59,7693,7694],{"class":61,"line":224},[59,7695,7696],{},"│   ├── config\n",[59,7698,7699],{"class":61,"line":233},[59,7700,7701],{},"│   ├── containers\n",[59,7703,7704],{"class":61,"line":241},[59,7705,7706],{},"│   ├── db\n",[59,7708,7709],{"class":61,"line":249},[59,7710,7711],{},"│   ├── ee\n",[59,7713,7714],{"class":61,"line":257},[59,7715,7716],{},"│   ├── lib\n",[59,7718,7719],{"class":61,"line":3137},[59,7720,7721],{},"│   ├── licensing\n",[59,7723,7724],{"class":61,"line":3150},[59,7725,7726],{},"│   ├── monitor\n",[59,7728,7729],{"class":61,"line":3163},[59,7730,7731],{},"│   ├── postoffice\n",[59,7733,7734],{"class":61,"line":3176},[59,7735,7736],{},"│   ├── routes\n",[59,7738,7739],{"class":61,"line":3187},[59,7740,7741],{},"│   └── settings\n",[59,7743,7744],{"class":61,"line":3193},[59,7745,7746],{},"├── frontend             - Frontend code\n",[59,7748,7749],{"class":61,"line":3201},[59,7750,7751],{},"│   ├── dist             - build output - created by `npm run build`\n",[59,7753,7754],{"class":61,"line":3214},[59,7755,7756],{},"│   ├── public           - static assets\n",[59,7758,7759],{"class":61,"line":3222},[59,7760,7761],{},"│   └── src              - vue src\n",[59,7763,7764],{"class":61,"line":3233},[59,7765,7766],{},"│       ├── api\n",[59,7768,7769],{"class":61,"line":3239},[59,7770,7771],{},"│       ├── components\n",[59,7773,7774],{"class":61,"line":3247},[59,7775,7776],{},"│       ├── pages\n",[59,7778,7779],{"class":61,"line":3256},[59,7780,7781],{},"│       ├── routes\n",[59,7783,7784],{"class":61,"line":3261},[59,7785,7786],{},"│       └── store\n",[59,7788,7789],{"class":61,"line":3269},[59,7790,7791],{},"├── test                 - tests for FlowFuse\n",[59,7793,7794],{"class":61,"line":3278},[59,7795,7796],{},"└── var                  - where the database and localfs project directories are created\n",[23,7798,7800],{"id":7799},"development-setup","Development Setup",[398,7802,7803,7809,7815,7821,7827,7833,7839],{},[31,7804,7805],{},[41,7806,7808],{"href":7807},"#create-a-stack","Create a Stack",[31,7810,7811],{},[41,7812,7814],{"href":7813},"#running-flowfuse","Running FlowFuse",[31,7816,7817],{},[41,7818,7820],{"href":7819},"#configuring-flowfuse","Configuring FlowFuse",[31,7822,7823],{},[41,7824,7826],{"href":7825},"#mocking-email","Mocking email",[31,7828,7829],{},[41,7830,7832],{"href":7831},"#testing","Testing",[31,7834,7835],{},[41,7836,7838],{"href":7837},"#vscode-tips","VSCode Tips",[31,7840,7841],{},[41,7842,1008],{"href":7843},"\u002Fdocs\u002Fcontribute\u002Fteam-broker",[104,7845,7808],{"id":7846},"create-a-stack",[14,7848,7849],{},"You will need to setup the version(s) of Node-RED you want to use in your stacks.",[14,7851,7852,7853,7856],{},"From the ",[18,7854,7855],{},"flowfuse"," directory run",[50,7858,7860],{"className":52,"code":7859,"language":54,"meta":55,"style":55},"npm run install-stack --vers=3.1.9\n",[18,7861,7862],{"__ignoreMap":55},[59,7863,7864,7866,7868,7871],{"class":61,"line":62},[59,7865,7542],{"class":65},[59,7867,70],{"class":69},[59,7869,7870],{"class":69}," install-stack",[59,7872,7873],{"class":73}," --vers=3.1.9\n",[14,7875,7876,7877,7879],{},"Where ",[18,7878,1196],{}," is the version of Node-RED you want to use in the stack.",[768,7881,7883],{"id":7882},"working-with-local-nodes","Working with Local Nodes",[14,7885,7886],{},"If you want to test local, in-development nodes in FlowFuse, you can create a dedicated stack for this purpose.",[50,7888,7889],{"className":52,"code":7859,"language":54,"meta":55,"style":55},[18,7890,7891],{"__ignoreMap":55},[59,7892,7893,7895,7897,7899],{"class":61,"line":62},[59,7894,7542],{"class":65},[59,7896,70],{"class":69},[59,7898,7870],{"class":69},[59,7900,7873],{"class":73},[14,7902,7903],{},"Navigate to the stacks directory:",[50,7905,7907],{"className":52,"code":7906,"language":54,"meta":55,"style":55},"cd \u002Fvar\u002Fstacks\n",[18,7908,7909],{"__ignoreMap":55},[59,7910,7911,7913],{"class":61,"line":62},[59,7912,7534],{"class":73},[59,7914,7915],{"class":69}," \u002Fvar\u002Fstacks\n",[14,7917,7918],{},"Rename the directory to something more appropriate:",[50,7920,7922],{"className":52,"code":7921,"language":54,"meta":55,"style":55},"mv \"3.1.9\" \"3.1.9-local\"\n",[18,7923,7924],{"__ignoreMap":55},[59,7925,7926,7929,7932],{"class":61,"line":62},[59,7927,7928],{"class":65},"mv",[59,7930,7931],{"class":69}," \"3.1.9\"",[59,7933,7934],{"class":69}," \"3.1.9-local\"\n",[14,7936,7937],{},"Install your local repository directly into the stack:",[50,7939,7941],{"className":52,"code":7940,"language":54,"meta":55,"style":55},"cd 3.1.9-local\nnpm install \u002Fpath\u002Fto\u002Fyour\u002Fnodes-repo\n",[18,7942,7943,7950],{"__ignoreMap":55},[59,7944,7945,7947],{"class":61,"line":62},[59,7946,7534],{"class":73},[59,7948,7949],{"class":69}," 3.1.9-local\n",[59,7951,7952,7954,7957],{"class":61,"line":77},[59,7953,7542],{"class":65},[59,7955,7956],{"class":69}," install",[59,7958,7959],{"class":69}," \u002Fpath\u002Fto\u002Fyour\u002Fnodes-repo\n",[14,7961,7962,7963,273],{},"With your stack created from the terminal, you can now add it via the FlowFuse Admin UI - see ",[41,7964,1108],{"href":7965},"\u002Fdocs\u002Fadmin\u002Fintroduction\u002F#create-new-stack",[104,7967,7814],{"id":7968},"running-flowfuse",[14,7970,7971,7972,7974,7975,7978,7979,7981],{},"A number of ",[18,7973,7542],{}," tasks are defined in the ",[18,7976,7977],{},"package.json"," file of this repository.\nTo get started from the ",[18,7980,7855],{}," directory use:",[50,7983,7985],{"className":52,"code":7984,"language":54,"meta":55,"style":55},"npm run serve\n",[18,7986,7987],{"__ignoreMap":55},[59,7988,7989,7991,7993],{"class":61,"line":62},[59,7990,7542],{"class":65},[59,7992,70],{"class":69},[59,7994,7995],{"class":69}," serve\n",[14,7997,7998],{},"This does a couple of things in parallel:",[28,8000,8001,8004],{},[31,8002,8003],{},"Starts the core FlowFuse application and watches the source code for any\nchanges - triggering a restart if needed.",[31,8005,8006],{},"Builds the frontend application using WebPack and watches for any changes - triggering a rebuild as needed.",[14,8008,8009,8010,8012,8013,273],{},"When running like this, the ",[18,8011,3755],{}," environment variable gets set to ",[18,8014,1731],{},[14,8016,8017,8019,8020,8023,8024,8027,8028,273],{},[1160,8018,1760],{},": if you have not used the ",[41,8021,7509],{"href":8022},"#setting-up-a-development-environment",", then you will need to run ",[18,8025,8026],{},"npm run build","\nto build the platform before you can use ",[18,8029,8030],{},"npm run serve",[104,8032,7820],{"id":8033},"configuring-flowfuse",[14,8035,8036,8037,8040,8041,8044],{},"When running in development mode, the core app will use ",[18,8038,8039],{},"etc\u002Fflowforge.yml"," for its configuration.\nAs you may want to have a local configuration that you don't want to commit back to git,\nyou can create a file called ",[18,8042,8043],{},"etc\u002Fflowforge.local.yml"," and it will use that instead.\nThat filename is set to be ignored by git so it won't be accidentally committed.",[104,8046,7826],{"id":8047},"mocking-email",[14,8049,8050],{},"If you are developing locally and need to enable external email sending, you can either:",[28,8052,8053,8060],{},[31,8054,8055,8056],{},"Setup a local test SMTP server. For example, the Nodemailer project provides a\nuseful app that does the job: ",[41,8057,8058],{"href":8058,"rel":8059},"https:\u002F\u002Fgithub.com\u002Fnodemailer\u002Fnodemailer-app\u002Freleases",[831],[31,8061,8062,8063,8066,8067,8069],{},"Alternatively, set the ",[18,8064,8065],{},"email.debug"," option to ",[18,8068,3558],{}," in your configuration file\nand the app will print all emails to its log.",[104,8071,8073],{"id":8072},"configuring-billing","Configuring billing",[14,8075,8076],{},"If you need to develop features covered by the Billing EE feature, you will need\nto configure the platform with a set of valid Stripe API keys and an EE license.",[14,8078,372,8079,8082,8083,8086],{},[1160,8080,8081],{},"development-only"," EE licence is provided in ",[18,8084,8085],{},"flowfuse\u002Fforge\u002Flicensing\u002Findex.js",". This\nlicence is not valid for production usage.",[14,8088,8089],{},"For FlowForge Inc. employees the configuration is provided in 1Password as 'Stripe Testing Configuration'.",[50,8091,8093],{"className":165,"code":8092,"language":167,"meta":55,"style":55},"license: ***\n\nbilling:\n  stripe:\n    key: ***\n    wh_secret: ***\n    team_price: ***\n    team_product: ***\n    project_price: ***\n    project_product: ***\n    device_price: ***\n    device_product: ***\n    deviceCost: 10\n    new_customer_free_credit: 1000\n    teams:\n      starter:\n        price: ***\n        product: ***\n        userCost: 0\n",[18,8094,8095,8107,8111,8117,8124,8135,8146,8157,8168,8179,8190,8201,8212,8222,8232,8239,8246,8257,8268],{"__ignoreMap":55},[59,8096,8097,8100,8102,8104],{"class":61,"line":62},[59,8098,8099],{"class":174},"license",[59,8101,179],{"class":178},[59,8103,1795],{"class":1372},[59,8105,8106],{"class":178},"**\n",[59,8108,8109],{"class":61,"line":77},[59,8110,188],{"emptyLinePlaceholder":187},[59,8112,8113,8115],{"class":61,"line":88},[59,8114,3964],{"class":174},[59,8116,196],{"class":178},[59,8118,8119,8122],{"class":61,"line":99},[59,8120,8121],{"class":174},"  stripe",[59,8123,196],{"class":178},[59,8125,8126,8129,8131,8133],{"class":61,"line":156},[59,8127,8128],{"class":174},"    key",[59,8130,179],{"class":178},[59,8132,1795],{"class":1372},[59,8134,8106],{"class":178},[59,8136,8137,8140,8142,8144],{"class":61,"line":216},[59,8138,8139],{"class":174},"    wh_secret",[59,8141,179],{"class":178},[59,8143,1795],{"class":1372},[59,8145,8106],{"class":178},[59,8147,8148,8151,8153,8155],{"class":61,"line":224},[59,8149,8150],{"class":174},"    team_price",[59,8152,179],{"class":178},[59,8154,1795],{"class":1372},[59,8156,8106],{"class":178},[59,8158,8159,8162,8164,8166],{"class":61,"line":233},[59,8160,8161],{"class":174},"    team_product",[59,8163,179],{"class":178},[59,8165,1795],{"class":1372},[59,8167,8106],{"class":178},[59,8169,8170,8173,8175,8177],{"class":61,"line":241},[59,8171,8172],{"class":174},"    project_price",[59,8174,179],{"class":178},[59,8176,1795],{"class":1372},[59,8178,8106],{"class":178},[59,8180,8181,8184,8186,8188],{"class":61,"line":249},[59,8182,8183],{"class":174},"    project_product",[59,8185,179],{"class":178},[59,8187,1795],{"class":1372},[59,8189,8106],{"class":178},[59,8191,8192,8195,8197,8199],{"class":61,"line":257},[59,8193,8194],{"class":174},"    device_price",[59,8196,179],{"class":178},[59,8198,1795],{"class":1372},[59,8200,8106],{"class":178},[59,8202,8203,8206,8208,8210],{"class":61,"line":3137},[59,8204,8205],{"class":174},"    device_product",[59,8207,179],{"class":178},[59,8209,1795],{"class":1372},[59,8211,8106],{"class":178},[59,8213,8214,8217,8219],{"class":61,"line":3150},[59,8215,8216],{"class":174},"    deviceCost",[59,8218,179],{"class":178},[59,8220,8221],{"class":73},"10\n",[59,8223,8224,8227,8229],{"class":61,"line":3163},[59,8225,8226],{"class":174},"    new_customer_free_credit",[59,8228,179],{"class":178},[59,8230,8231],{"class":73},"1000\n",[59,8233,8234,8237],{"class":61,"line":3176},[59,8235,8236],{"class":174},"    teams",[59,8238,196],{"class":178},[59,8240,8241,8244],{"class":61,"line":3187},[59,8242,8243],{"class":174},"      starter",[59,8245,196],{"class":178},[59,8247,8248,8251,8253,8255],{"class":61,"line":3193},[59,8249,8250],{"class":174},"        price",[59,8252,179],{"class":178},[59,8254,1795],{"class":1372},[59,8256,8106],{"class":178},[59,8258,8259,8262,8264,8266],{"class":61,"line":3201},[59,8260,8261],{"class":174},"        product",[59,8263,179],{"class":178},[59,8265,1795],{"class":1372},[59,8267,8106],{"class":178},[59,8269,8270,8273,8275],{"class":61,"line":3214},[59,8271,8272],{"class":174},"        userCost",[59,8274,179],{"class":178},[59,8276,8277],{"class":73},"0\n",[14,8279,8280,8281,8286,8287,8290],{},"You will also need to install the ",[41,8282,8285],{"href":8283,"rel":8284},"https:\u002F\u002Fstripe.com\u002Fdocs\u002Fcli\u002F",[831],"Stripe CLI"," to handle webhook callbacks properly. Install the CLI following their documentation, then\nrun the following command, with the API key using the value of ",[18,8288,8289],{},"billing.stripe.key"," from\nabove.",[50,8292,8294],{"className":52,"code":8293,"language":54,"meta":55,"style":55},"stripe listen --forward-to localhost:3000\u002Fee\u002Fbilling\u002Fcallback --api-key ***\n",[18,8295,8296],{"__ignoreMap":55},[59,8297,8298,8301,8304,8307,8310,8313],{"class":61,"line":62},[59,8299,8300],{"class":65},"stripe",[59,8302,8303],{"class":69}," listen",[59,8305,8306],{"class":73}," --forward-to",[59,8308,8309],{"class":69}," localhost:3000\u002Fee\u002Fbilling\u002Fcallback",[59,8311,8312],{"class":73}," --api-key",[59,8314,8315],{"class":73}," ***\n",[14,8317,8318,8319,8321],{},"Note that due to the way Stripe works, you will receive events for ",[1160,8320,4817],{}," activity\nin the configured Stripe account. That means if someone else is actively developing\nwith billing enabled on the same account, you will see their events arrive.",[768,8323,8325],{"id":8324},"free-trials","Free Trials",[14,8327,8328],{},"Free trials are implemented as a Stripe Credit that is applied when a FlowFuse user\ncreates their first team and completes billing sign up.",[14,8330,8331,8332,8335],{},"To enable trials, set the ",[18,8333,8334],{},"billing.stripe.new_customer_free_credit"," value to a credit amount in cents.\nFor a totally free trial, this amount should match the cost of the Stripe product for the project type to be trialed to be trialed.",[14,8337,8338],{},"The Stripe webhook forwarder must be running as the credit is handled as part of the webhook handling.",[104,8340,7832],{"id":8341},"testing",[14,8343,8344],{},"Our testing philosophy follows the principle of:",[1110,8346,8347],{},[14,8348,8349,8350],{},"Write tests. Not too many. Mostly integration ",[8351,8352,8353],"sup",{},[41,8354,8359],{"href":8355,"ariaDescribedBy":8356,"dataFootnoteRef":55,"id":8358},"#user-content-fn-1",[8357],"footnote-label","user-content-fnref-1","1",[14,8361,8362],{},"We create both unit tests and system level tests. The former is suitable for\nwell-contained components that need to provide a stable api and behavior to\nthe rest of the code base. The latter is for testing the external behavior\nof the platform as a whole with as little internal mocking as possible.",[14,8364,8365,8366,8369],{},"We use code coverage reporting as ",[1160,8367,8368],{},"one"," aspect of assessing our testing coverage.\nWe do not treat 100% coverage as an imperative goal - that can often lead to\nbusy work writing tests that don't provide any real value in understanding the\noverall quality of the system.",[14,8371,8372],{},"Unit tests should provide sufficient coverage to give us confidence that a\ncomponent's behavior does not unexpectedly change.",[768,8374,8376],{"id":8375},"running-tests","Running tests",[14,8378,8379],{},"To run the tests for the project, you can use the following npm tasks:",[28,8381,8382,8388,8394,8400,8406],{},[31,8383,8384,8387],{},[18,8385,8386],{},"npm run test"," - runs the whole test suite, covering code linting, unit and systems tests.",[31,8389,8390,8393],{},[18,8391,8392],{},"npm run lint"," - runs the linting tests",[31,8395,8396,8399],{},[18,8397,8398],{},"npm run test:unit"," - runs the unit tests",[31,8401,8402,8405],{},[18,8403,8404],{},"npm run test:system"," - runs the system tests",[31,8407,8408,8411],{},[18,8409,8410],{},"npm run test:docs"," - checks the validity of links in the documentation",[1146,8413,8415],{"id":8414},"testing-against-postgresql","Testing against PostgreSQL",[14,8417,8418],{},"By default, the tests use an in-memory SQLite database to test against. This is\nthe most self-contained way of testing the platform. But it is also necessary to\ntest against PostgreSQL. To enable the use of PostgreSQL in the tests:",[398,8420,8421,8460],{},[31,8422,8423,8424],{},"Ensure you have an instance of PostgreSQL running locally. For example, via docker:",[50,8425,8427],{"className":52,"code":8426,"language":54,"meta":55,"style":55},"   docker run -it -p 5432:5432 --name ff-postgres -e POSTGRES_PASSWORD=secret postgres:14\n",[18,8428,8429],{"__ignoreMap":55},[59,8430,8431,8434,8436,8439,8442,8445,8448,8451,8454,8457],{"class":61,"line":62},[59,8432,8433],{"class":65},"   docker",[59,8435,70],{"class":69},[59,8437,8438],{"class":73}," -it",[59,8440,8441],{"class":73}," -p",[59,8443,8444],{"class":69}," 5432:5432",[59,8446,8447],{"class":73}," --name",[59,8449,8450],{"class":69}," ff-postgres",[59,8452,8453],{"class":73}," -e",[59,8455,8456],{"class":69}," POSTGRES_PASSWORD=secret",[59,8458,8459],{"class":69}," postgres:14\n",[31,8461,8462,8463,8480,8482,8483],{},"Enable PostgreSQL mode by setting the following environment variable:",[50,8464,8466],{"className":52,"code":8465,"language":54,"meta":55,"style":55},"   export FF_TEST_DB_POSTGRES=true\n",[18,8467,8468],{"__ignoreMap":55},[59,8469,8470,8473,8476,8478],{"class":61,"line":62},[59,8471,8472],{"class":1372},"   export",[59,8474,8475],{"class":178}," FF_TEST_DB_POSTGRES",[59,8477,1373],{"class":1372},[59,8479,3230],{"class":178},[662,8481],{},"The database connection can be set using the following env vars (default values shown)",[50,8484,8486],{"className":52,"code":8485,"language":54,"meta":55,"style":55},"   export FF_TEST_DB_POSTGRES_HOST=localhost\n   export FF_TEST_DB_POSTGRES_PORT=5432\n   export FF_TEST_DB_POSTGRES_USER=postgres\n   export FF_TEST_DB_POSTGRES_PASSWORD=secret\n   export FF_TEST_DB_POSTGRES_DATABASE=flowforge_test\n",[18,8487,8488,8500,8512,8524,8536],{"__ignoreMap":55},[59,8489,8490,8492,8495,8497],{"class":61,"line":62},[59,8491,8472],{"class":1372},[59,8493,8494],{"class":178}," FF_TEST_DB_POSTGRES_HOST",[59,8496,1373],{"class":1372},[59,8498,8499],{"class":178},"localhost\n",[59,8501,8502,8504,8507,8509],{"class":61,"line":77},[59,8503,8472],{"class":1372},[59,8505,8506],{"class":178}," FF_TEST_DB_POSTGRES_PORT",[59,8508,1373],{"class":1372},[59,8510,8511],{"class":73},"5432\n",[59,8513,8514,8516,8519,8521],{"class":61,"line":88},[59,8515,8472],{"class":1372},[59,8517,8518],{"class":178}," FF_TEST_DB_POSTGRES_USER",[59,8520,1373],{"class":1372},[59,8522,8523],{"class":178},"postgres\n",[59,8525,8526,8528,8531,8533],{"class":61,"line":99},[59,8527,8472],{"class":1372},[59,8529,8530],{"class":178}," FF_TEST_DB_POSTGRES_PASSWORD",[59,8532,1373],{"class":1372},[59,8534,8535],{"class":178},"secret\n",[59,8537,8538,8540,8543,8545],{"class":61,"line":156},[59,8539,8472],{"class":1372},[59,8541,8542],{"class":178}," FF_TEST_DB_POSTGRES_DATABASE",[59,8544,1373],{"class":1372},[59,8546,8547],{"class":178},"flowforge_test\n",[768,8549,8551],{"id":8550},"reporting-code-coverage","Reporting code coverage",[14,8553,372,8554,8557,8558,8561],{},[18,8555,8556],{},"test:*"," tasks have corresponding code coverage tasks. These tasks run the\ntests using ",[18,8559,8560],{},"nyc"," to generate code coverage information.",[28,8563,8564,8574,8583,8591],{},[31,8565,8566,8569,8570,8573],{},[18,8567,8568],{},"npm run cover"," - runs the whole test suite (excluding linting) with code\ncoverage enabled and generates a report (via the ",[18,8571,8572],{},"cover:report"," task)",[31,8575,8576,8579,8580,8582],{},[18,8577,8578],{},"npm run cover:unit"," - runs the unit tests with code coverage enabled. It\ndoes ",[1160,8581,1230],{}," generate the report.",[31,8584,8585,8588,8589,8582],{},[18,8586,8587],{},"npm run cover:system"," - runs the system tests with code coverage enabled. It\ndoes ",[1160,8590,1230],{},[31,8592,8593,8596,8597],{},[18,8594,8595],{},"npm run cover:report"," - generates a report of the code coverage. This is\nprinted to the console and generates a browsable HTML copy under ",[18,8598,8599],{},"coverage\u002Findex.html",[104,8601,7838],{"id":8602},"vscode-tips",[14,8604,8605],{},"To step debug in VSCode",[398,8607,8608,8615,8622],{},[31,8609,8610,8611,8614],{},"Open ",[18,8612,8613],{},"launch.json"," config and enter the JavaScript below",[31,8616,8617,8618,8621],{},"Choose ",[18,8619,8620],{},"Start-Watch"," from the \"Run and Debug\" menu",[31,8623,8624,8625,8629],{},"Press ▶️ or ",[8626,8627,8628],"kbd",{},"F5"," to start debugging",[14,8631,8632],{},"There are 2 other \"Run and Debug\" entries in the menu...",[28,8634,8635,8638],{},[31,8636,8637],{},"\"Attach by Process ID\" - this will allow you to attach to a launched driver",[31,8639,8640],{},"\"Debug Current Test\" - this will enable you to step debug a test (starts debugging the currently open test file)",[768,8642],{"id":55},[50,8644,8646],{"className":2972,"code":8645,"language":2974,"meta":55,"style":55},"{\n    \u002F\u002F Use IntelliSense to learn about possible attributes.\n    \u002F\u002F Hover to view descriptions of existing attributes.\n    \u002F\u002F For more information, visit: https:\u002F\u002Fgo.microsoft.com\u002Ffwlink\u002F?linkid=830387\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n            \"command\": \"npm run start-watch\",\n            \"name\": \"Start-Watch\",\n            \"request\": \"launch\",\n            \"type\": \"node-terminal\",\n            \"env\": {\n                \"NODE_ENV\": \"development\"\n            }\n        },\n        {\n            \"name\": \"Attach by Process ID\",\n            \"processId\": \"${command:PickProcess}\",\n            \"request\": \"attach\",\n            \"skipFiles\": [\n                \"\u003Cnode_internals>\u002F**\"\n            ],\n            \"type\": \"node\"\n        },\n        {\n            \"type\": \"node\",\n            \"request\": \"launch\",\n            \"name\": \"Debug Current Test\",\n            \"program\": \"${workspaceFolder}\u002Fnode_modules\u002Fmocha\u002Fbin\u002F_mocha\",\n            \"args\": [\n              \"--no-warnings\",\n              \"-u\",\n              \"bdd\",\u002F\u002F set to bdd, not tdd\n              \"--timeout\",\n              \"999999\",\n              \"--colors\",\n              \"${file}\"\n            ],\n            \"env\": {\n                \"NODE_ENV\": \"development\"\n            },\n            \"internalConsoleOptions\": \"openOnSessionStart\"\n        }\n    ]\n}\n",[18,8647,8648,8652,8657,8662,8667,8679,8687,8691,8703,8715,8727,8739,8746,8756,8761,8765,8769,8780,8792,8803,8810,8815,8820,8829,8833,8837,8848,8858,8869,8881,8888,8895,8902,8913,8920,8927,8934,8939,8943,8949,8957,8962,8972,8977,8983],{"__ignoreMap":55},[59,8649,8650],{"class":61,"line":62},[59,8651,2981],{"class":178},[59,8653,8654],{"class":61,"line":77},[59,8655,8656],{"class":3773},"    \u002F\u002F Use IntelliSense to learn about possible attributes.\n",[59,8658,8659],{"class":61,"line":88},[59,8660,8661],{"class":3773},"    \u002F\u002F Hover to view descriptions of existing attributes.\n",[59,8663,8664],{"class":61,"line":99},[59,8665,8666],{"class":3773},"    \u002F\u002F For more information, visit: https:\u002F\u002Fgo.microsoft.com\u002Ffwlink\u002F?linkid=830387\n",[59,8668,8669,8672,8674,8677],{"class":61,"line":156},[59,8670,8671],{"class":73},"    \"version\"",[59,8673,179],{"class":178},[59,8675,8676],{"class":69},"\"0.2.0\"",[59,8678,2665],{"class":178},[59,8680,8681,8684],{"class":61,"line":216},[59,8682,8683],{"class":73},"    \"configurations\"",[59,8685,8686],{"class":178},": [\n",[59,8688,8689],{"class":61,"line":224},[59,8690,6003],{"class":178},[59,8692,8693,8696,8698,8701],{"class":61,"line":233},[59,8694,8695],{"class":73},"            \"command\"",[59,8697,179],{"class":178},[59,8699,8700],{"class":69},"\"npm run start-watch\"",[59,8702,2665],{"class":178},[59,8704,8705,8708,8710,8713],{"class":61,"line":241},[59,8706,8707],{"class":73},"            \"name\"",[59,8709,179],{"class":178},[59,8711,8712],{"class":69},"\"Start-Watch\"",[59,8714,2665],{"class":178},[59,8716,8717,8720,8722,8725],{"class":61,"line":249},[59,8718,8719],{"class":73},"            \"request\"",[59,8721,179],{"class":178},[59,8723,8724],{"class":69},"\"launch\"",[59,8726,2665],{"class":178},[59,8728,8729,8732,8734,8737],{"class":61,"line":257},[59,8730,8731],{"class":73},"            \"type\"",[59,8733,179],{"class":178},[59,8735,8736],{"class":69},"\"node-terminal\"",[59,8738,2665],{"class":178},[59,8740,8741,8744],{"class":61,"line":3137},[59,8742,8743],{"class":73},"            \"env\"",[59,8745,3068],{"class":178},[59,8747,8748,8751,8753],{"class":61,"line":3150},[59,8749,8750],{"class":73},"                \"NODE_ENV\"",[59,8752,179],{"class":178},[59,8754,8755],{"class":69},"\"development\"\n",[59,8757,8758],{"class":61,"line":3163},[59,8759,8760],{"class":178},"            }\n",[59,8762,8763],{"class":61,"line":3176},[59,8764,6155],{"class":178},[59,8766,8767],{"class":61,"line":3187},[59,8768,6003],{"class":178},[59,8770,8771,8773,8775,8778],{"class":61,"line":3193},[59,8772,8707],{"class":73},[59,8774,179],{"class":178},[59,8776,8777],{"class":69},"\"Attach by Process ID\"",[59,8779,2665],{"class":178},[59,8781,8782,8785,8787,8790],{"class":61,"line":3201},[59,8783,8784],{"class":73},"            \"processId\"",[59,8786,179],{"class":178},[59,8788,8789],{"class":69},"\"${command:PickProcess}\"",[59,8791,2665],{"class":178},[59,8793,8794,8796,8798,8801],{"class":61,"line":3214},[59,8795,8719],{"class":73},[59,8797,179],{"class":178},[59,8799,8800],{"class":69},"\"attach\"",[59,8802,2665],{"class":178},[59,8804,8805,8808],{"class":61,"line":3222},[59,8806,8807],{"class":73},"            \"skipFiles\"",[59,8809,8686],{"class":178},[59,8811,8812],{"class":61,"line":3233},[59,8813,8814],{"class":69},"                \"\u003Cnode_internals>\u002F**\"\n",[59,8816,8817],{"class":61,"line":3239},[59,8818,8819],{"class":178},"            ],\n",[59,8821,8822,8824,8826],{"class":61,"line":3247},[59,8823,8731],{"class":73},[59,8825,179],{"class":178},[59,8827,8828],{"class":69},"\"node\"\n",[59,8830,8831],{"class":61,"line":3256},[59,8832,6155],{"class":178},[59,8834,8835],{"class":61,"line":3261},[59,8836,6003],{"class":178},[59,8838,8839,8841,8843,8846],{"class":61,"line":3269},[59,8840,8731],{"class":73},[59,8842,179],{"class":178},[59,8844,8845],{"class":69},"\"node\"",[59,8847,2665],{"class":178},[59,8849,8850,8852,8854,8856],{"class":61,"line":3278},[59,8851,8719],{"class":73},[59,8853,179],{"class":178},[59,8855,8724],{"class":69},[59,8857,2665],{"class":178},[59,8859,8860,8862,8864,8867],{"class":61,"line":3284},[59,8861,8707],{"class":73},[59,8863,179],{"class":178},[59,8865,8866],{"class":69},"\"Debug Current Test\"",[59,8868,2665],{"class":178},[59,8870,8871,8874,8876,8879],{"class":61,"line":3289},[59,8872,8873],{"class":73},"            \"program\"",[59,8875,179],{"class":178},[59,8877,8878],{"class":69},"\"${workspaceFolder}\u002Fnode_modules\u002Fmocha\u002Fbin\u002F_mocha\"",[59,8880,2665],{"class":178},[59,8882,8883,8886],{"class":61,"line":3297},[59,8884,8885],{"class":73},"            \"args\"",[59,8887,8686],{"class":178},[59,8889,8890,8893],{"class":61,"line":3310},[59,8891,8892],{"class":69},"              \"--no-warnings\"",[59,8894,2665],{"class":178},[59,8896,8897,8900],{"class":61,"line":3321},[59,8898,8899],{"class":69},"              \"-u\"",[59,8901,2665],{"class":178},[59,8903,8904,8907,8910],{"class":61,"line":3327},[59,8905,8906],{"class":69},"              \"bdd\"",[59,8908,8909],{"class":178},",",[59,8911,8912],{"class":3773},"\u002F\u002F set to bdd, not tdd\n",[59,8914,8915,8918],{"class":61,"line":3333},[59,8916,8917],{"class":69},"              \"--timeout\"",[59,8919,2665],{"class":178},[59,8921,8922,8925],{"class":61,"line":6115},[59,8923,8924],{"class":69},"              \"999999\"",[59,8926,2665],{"class":178},[59,8928,8929,8932],{"class":61,"line":6152},[59,8930,8931],{"class":69},"              \"--colors\"",[59,8933,2665],{"class":178},[59,8935,8936],{"class":61,"line":6158},[59,8937,8938],{"class":69},"              \"${file}\"\n",[59,8940,8941],{"class":61,"line":6164},[59,8942,8819],{"class":178},[59,8944,8945,8947],{"class":61,"line":6170},[59,8946,8743],{"class":73},[59,8948,3068],{"class":178},[59,8950,8951,8953,8955],{"class":61,"line":6175},[59,8952,8750],{"class":73},[59,8954,179],{"class":178},[59,8956,8755],{"class":69},[59,8958,8959],{"class":61,"line":6619},[59,8960,8961],{"class":178},"            },\n",[59,8963,8964,8967,8969],{"class":61,"line":6625},[59,8965,8966],{"class":73},"            \"internalConsoleOptions\"",[59,8968,179],{"class":178},[59,8970,8971],{"class":69},"\"openOnSessionStart\"\n",[59,8973,8975],{"class":61,"line":8974},43,[59,8976,6016],{"class":178},[59,8978,8980],{"class":61,"line":8979},44,[59,8981,8982],{"class":178},"    ]\n",[59,8984,8986],{"class":61,"line":8985},45,[59,8987,3336],{"class":178},[8989,8990,8993,8998],"section",{"className":8991,"dataFootnotes":55},[8992],"footnotes",[23,8994,8997],{"className":8995,"id":8357},[8996],"sr-only","Footnotes",[398,8999,9000],{},[31,9001,9003,3497,9007],{"id":9002},"user-content-fn-1",[41,9004,9005],{"href":9005,"rel":9006},"https:\u002F\u002Fkentcdodds.com\u002Fblog\u002Fwrite-tests",[831],[41,9008,9013],{"href":9009,"ariaLabel":9010,"className":9011,"dataFootnoteBackref":55},"#user-content-fnref-1","Back to reference 1",[9012],"data-footnote-backref","↩",[316,9015,9016],{},"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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":55,"searchDepth":77,"depth":77,"links":9018},[9019,9020,9021,9022,9023,9032],{"id":7361,"depth":88,"text":7362},{"id":7401,"depth":88,"text":7402},{"id":7497,"depth":88,"text":7498},{"id":7649,"depth":88,"text":7650},{"id":7799,"depth":77,"text":7800,"children":9024},[9025,9026,9027,9028,9029,9030,9031],{"id":7846,"depth":88,"text":7808},{"id":7968,"depth":88,"text":7814},{"id":8033,"depth":88,"text":7820},{"id":8047,"depth":88,"text":7826},{"id":8072,"depth":88,"text":8073},{"id":8341,"depth":88,"text":7832},{"id":8602,"depth":88,"text":7838},{"id":8357,"depth":77,"text":8997},{},"contribute\u002Fintroduction.md",{"title":7338,"description":7355},"docs\u002Fcontribute\u002Fintroduction","vNEkYlYSZG1JuO1Sola_iUCgae2Qv1AhukiOLFkW8lU",{"id":9039,"title":9040,"body":9041,"description":9048,"extension":329,"layout":330,"meta":10029,"navGroup":330,"navOrder":330,"navTitle":9040,"navigation":187,"originalPath":10030,"path":10031,"redirect":330,"seo":10032,"stem":10033,"updated":337,"version":338,"__hash__":10034},"docs\u002Fdocs\u002Fcontribute\u002Flocal\u002Findex.md","Local Install",{"type":7,"value":9042,"toc":10009},[9043,9046,9049,9054,9056,9060,9063,9091,9095,9103,9107,9110,9113,9117,9120,9127,9130,9161,9165,9394,9398,9401,9407,9409,9414,9428,9431,9446,9457,9459,9462,9488,9491,9511,9514,9517,9522,9526,9535,9542,9548,9555,9559,9567,9576,9584,9587,9674,9676,9679,9783,9787,9790,9807,9813,9853,9857,9860,9867,9942,9951,9955,9964,10006],[10,9044,9040],{"id":9045},"local-install",[14,9047,9048],{},"This guide is for setting up FlowFuse on a single machine, ideal for smaller deployments, evaluations, or for contributors who want to gain a basic understanding of the FlowFuse platform and its features.",[14,9050,9051],{},[364,9052,9053],{},"Note: Local installation does not support HTTPS",[23,9055,26],{"id":25},[104,9057,9059],{"id":9058},"operating-system","Operating System",[14,9061,9062],{},"The install script has been tested against the following operating systems:",[28,9064,9065,9073,9076,9079,9082,9085,9088],{},[31,9066,9067,9068],{},"Raspbian\u002FRaspberry Pi OS versions Buster\u002FBullseye ",[8351,9069,9070],{},[41,9071,8359],{"href":8355,"ariaDescribedBy":9072,"dataFootnoteRef":55,"id":8358},[8357],[31,9074,9075],{},"Debian Buster\u002FBullseye",[31,9077,9078],{},"Fedora 35",[31,9080,9081],{},"Ubuntu 20.04",[31,9083,9084],{},"CentOS 8\u002FRHEL 8\u002FAmazon Linux 2",[31,9086,9087],{},"MacOS Big Sur & Monterey on Intel & Apple M processors",[31,9089,9090],{},"Windows 10 & 11",[104,9092,9094],{"id":9093},"nodejs","Node.js",[14,9096,9097,9098,273],{},"FlowFuse requires ",[1160,9099,9100],{},[364,9101,9102],{},"Node.js v20",[768,9104,9106],{"id":9105},"linux","Linux",[14,9108,9109],{},"The install script will check to see if it can find a suitable version of Node.js.\nIf not, it will offer to install it for you.",[14,9111,9112],{},"It will also ensure you have the appropriate build tools installed that are often\nneeded by Node.js modules to build native components.",[768,9114,9116],{"id":9115},"windowsmacos","Windows\u002FMacOS",[14,9118,9119],{},"If the install script cannot find a suitable version of Node.js, it will exit.",[14,9121,9122,9123],{},"You will need to manually install it before proceeding. Information about\nhow to do this can be found on the Node.js website here: ",[41,9124,9125],{"href":9125,"rel":9126},"https:\u002F\u002Fnodejs.org\u002Fen\u002Fdownload",[831],[14,9128,9129],{},"You will also need to install the appropriate build tools.",[28,9131,9132,9138],{},[31,9133,9134,9137],{},[364,9135,9136],{},"Windows",": the standard Node.js installer will offer to do that for you.",[31,9139,9140,9143,9144,9147,9148],{},[364,9141,9142],{},"MacOS",": you will need the ",[18,9145,9146],{},"XCode Command Line Tools"," to be installed.\nThis can be done by running the following command:\n",[50,9149,9151],{"className":52,"code":9150,"language":54,"meta":55,"style":55},"xcode-select --install\n",[18,9152,9153],{"__ignoreMap":55},[59,9154,9155,9158],{"class":61,"line":62},[59,9156,9157],{"class":65},"xcode-select",[59,9159,9160],{"class":73}," --install\n",[23,9162,9164],{"id":9163},"installing-flowfuse","Installing FlowFuse",[398,9166,9167,9230,9239,9351],{},[31,9168,9169,9170,1706,9173,9176,9178,9179,9208,9210,9211],{},"Create a directory to be the base of your FlowFuse install. For example: ",[18,9171,9172],{},"\u002Fopt\u002Fflowforge",[18,9174,9175],{},"c:\\flowforge",[662,9177],{},"For Linux\u002FMacOS:",[50,9180,9182],{"className":52,"code":9181,"language":54,"meta":55,"style":55},"sudo mkdir \u002Fopt\u002Fflowforge\nsudo chown $USER \u002Fopt\u002Fflowforge\n",[18,9183,9184,9195],{"__ignoreMap":55},[59,9185,9186,9189,9192],{"class":61,"line":62},[59,9187,9188],{"class":65},"sudo",[59,9190,9191],{"class":69}," mkdir",[59,9193,9194],{"class":69}," \u002Fopt\u002Fflowforge\n",[59,9196,9197,9199,9202,9205],{"class":61,"line":77},[59,9198,9188],{"class":65},[59,9200,9201],{"class":69}," chown",[59,9203,9204],{"class":178}," $USER ",[59,9206,9207],{"class":69},"\u002Fopt\u002Fflowforge\n",[662,9209],{},"For Windows:",[50,9212,9214],{"className":52,"code":9213,"language":54,"meta":55,"style":55},"mkdir c:\\flowforge\n",[18,9215,9216],{"__ignoreMap":55},[59,9217,9218,9221,9224,9227],{"class":61,"line":62},[59,9219,9220],{"class":65},"mkdir",[59,9222,9223],{"class":69}," c:",[59,9225,9226],{"class":73},"\\f",[59,9228,9229],{"class":69},"lowforge\n",[31,9231,9232,9233,9238],{},"Download the latest ",[41,9234,9237],{"href":9235,"rel":9236},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Finstaller\u002Freleases\u002Flatest\u002Fdownload\u002Fflowforge-installer.zip",[831],"Installer zip file"," into a temporary location.",[31,9240,9241,9242,9245,9247,9257,9292,9295,9297,9304],{},"Unzip the downloaded zip file and copy its contents to\nthe FlowForge directory",[104,9243,9178],{"id":9244},"for-linuxmacos",[662,9246],{},[1160,9248,9249,9250,9253,9254],{},"Assumes ",[18,9251,9252],{},"\u002Ftmp\u002F"," is the directory where you downloaded ",[18,9255,9256],{},"flowforge-installer.zip",[50,9258,9260],{"className":52,"code":9259,"language":54,"meta":55,"style":55},"cd \u002Ftmp\u002F\nunzip flowforge-installer.zip\ncp -R flowforge-installer\u002F* \u002Fopt\u002Fflowforge\n",[18,9261,9262,9269,9277],{"__ignoreMap":55},[59,9263,9264,9266],{"class":61,"line":62},[59,9265,7534],{"class":73},[59,9267,9268],{"class":69}," \u002Ftmp\u002F\n",[59,9270,9271,9274],{"class":61,"line":77},[59,9272,9273],{"class":65},"unzip",[59,9275,9276],{"class":69}," flowforge-installer.zip\n",[59,9278,9279,9282,9285,9288,9290],{"class":61,"line":88},[59,9280,9281],{"class":65},"cp",[59,9283,9284],{"class":73}," -R",[59,9286,9287],{"class":69}," flowforge-installer\u002F",[59,9289,1795],{"class":73},[59,9291,9194],{"class":69},[104,9293,9210],{"id":9294},"for-windows",[662,9296],{},[1160,9298,9249,9299,9253,9302],{},[18,9300,9301],{},"c:\\temp",[18,9303,9256],{},[50,9305,9307],{"className":52,"code":9306,"language":54,"meta":55,"style":55},"cd c:\\temp\ntar -xf flowforge-installer.zip\nxcopy \u002FE \u002FI flowforge-installer c:\\flowforge\n",[18,9308,9309,9321,9331],{"__ignoreMap":55},[59,9310,9311,9313,9315,9318],{"class":61,"line":62},[59,9312,7534],{"class":73},[59,9314,9223],{"class":69},[59,9316,9317],{"class":73},"\\t",[59,9319,9320],{"class":69},"emp\n",[59,9322,9323,9326,9329],{"class":61,"line":77},[59,9324,9325],{"class":65},"tar",[59,9327,9328],{"class":73}," -xf",[59,9330,9276],{"class":69},[59,9332,9333,9336,9339,9342,9345,9347,9349],{"class":61,"line":88},[59,9334,9335],{"class":65},"xcopy",[59,9337,9338],{"class":69}," \u002FE",[59,9340,9341],{"class":69}," \u002FI",[59,9343,9344],{"class":69}," flowforge-installer",[59,9346,9223],{"class":69},[59,9348,9226],{"class":73},[59,9350,9229],{"class":69},[31,9352,9353,9354,9178,9356,9372,9210,9374],{},"Run the installer and follow the prompts",[662,9355],{},[50,9357,9359],{"className":52,"code":9358,"language":54,"meta":55,"style":55},"cd \u002Fopt\u002Fflowforge\n.\u002Finstall.sh\n",[18,9360,9361,9367],{"__ignoreMap":55},[59,9362,9363,9365],{"class":61,"line":62},[59,9364,7534],{"class":73},[59,9366,9194],{"class":69},[59,9368,9369],{"class":61,"line":77},[59,9370,9371],{"class":65},".\u002Finstall.sh\n",[662,9373],{},[50,9375,9377],{"className":52,"code":9376,"language":54,"meta":55,"style":55},"cd c:\\flowforge\ninstall.bat\n",[18,9378,9379,9389],{"__ignoreMap":55},[59,9380,9381,9383,9385,9387],{"class":61,"line":62},[59,9382,7534],{"class":73},[59,9384,9223],{"class":69},[59,9386,9226],{"class":73},[59,9388,9229],{"class":69},[59,9390,9391],{"class":61,"line":77},[59,9392,9393],{"class":65},"install.bat\n",[104,9395,9397],{"id":9396},"installing-as-a-service-optional","Installing as a service (optional)",[14,9399,9400],{},"On Linux, the installer will ask if you want to run FlowFuse as a service.\nThis will mean it starts automatically whenever you restart your device.",[14,9402,9403,9404,9406],{},"If you select this option, it will ask if you want to run the service as the\ncurrent user, or create a new ",[18,9405,6303],{}," user. If you choose to create the\nuser, it will also change the ownership of the FlowFuse directory to that user.",[23,9408,7820],{"id":8033},[14,9410,9411,9412],{},"The default FlowFuse configuration is provided in the file ",[18,9413,576],{},[28,9415,9416,9422],{},[31,9417,9418,9419],{},"Linux\u002FMacOS: ",[18,9420,9421],{},"\u002Fopt\u002Fflowforge\u002Fetc\u002Fflowforge.yml",[31,9423,9424,9425],{},"Windows: ",[18,9426,9427],{},"c:\\flowforge\\etc\\flowforge.yml",[14,9429,9430],{},"The default configuration file already contains everything you need to get started with FlowFuse.",[14,9432,9433,9434,9437,9438,9441,9442,9445],{},"It will allow you to access FlowFuse and the Node-RED instances you create, from the same server running the platform.\nIf you want to allow access from other devices on the network, you must edit the configuration file and\nchange the ",[18,9435,9436],{},"host"," setting to ",[18,9439,9440],{},"0.0.0.0"," and change ",[18,9443,9444],{},"base_url"," to contain the IP address of the server.",[14,9447,9448,9449,302,9451,9453,9454,273],{},"NOTE: We do not support changing the ",[18,9450,9436],{},[18,9452,9444],{}," values once you have created an instance.\nFor more information on all of the options available, see the ",[41,9455,9456],{"href":573},"configuration guide",[23,9458,7814],{"id":7968},[14,9460,9461],{},"To run it manually, you can use:",[28,9463,9464,9476],{},[31,9465,9466,9467],{},"Linux\u002FMacOS:",[50,9468,9470],{"className":52,"code":9469,"language":54,"meta":55,"style":55},"\u002Fopt\u002Fflowforge\u002Fbin\u002Fflowforge.sh\n",[18,9471,9472],{"__ignoreMap":55},[59,9473,9474],{"class":61,"line":62},[59,9475,9469],{"class":65},[31,9477,9478,9479],{},"Windows:",[50,9480,9482],{"className":52,"code":9481,"language":54,"meta":55,"style":55},"c:\\flowforge\\bin\\flowforge.bat\n",[18,9483,9484],{"__ignoreMap":55},[59,9485,9486],{"class":61,"line":62},[59,9487,9481],{"class":65},[14,9489,9490],{},"Or to run as a service:",[28,9492,9493],{},[31,9494,9106,9495],{},[50,9496,9498],{"className":52,"code":9497,"language":54,"meta":55,"style":55},"service flowforge start\n",[18,9499,9500],{"__ignoreMap":55},[59,9501,9502,9505,9508],{"class":61,"line":62},[59,9503,9504],{"class":65},"service",[59,9506,9507],{"class":69}," flowforge",[59,9509,9510],{"class":69}," start\n",[23,9512,584],{"id":9513},"first-run-setup",[14,9515,9516],{},"Once FlowFuse is started, you will be ready to perform the first run setup.",[14,9518,2582,9519,9521],{},[41,9520,2587],{"href":583}," to continue.",[23,9523,9525],{"id":9524},"setting-up-mosquitto-optional","Setting up Mosquitto (optional)",[14,9527,9528,9529,9534],{},"The platform depends on the ",[41,9530,9533],{"href":9531,"rel":9532},"https:\u002F\u002Fmosquitto.org\u002F",[831],"Mosquitto MQTT Broker"," to\nprovide real-time messaging between devices and the platform.",[14,9536,9537,9538,9541],{},"This is currently an ",[1160,9539,9540],{},"optional"," component - the platform will work without the\nbroker, but some features will not be available (e.g Access to Remote Instance Editor\nand Remote Instance logs requires the MQTT Broker).",[14,9543,9544,9545,9547],{},"We do ",[364,9546,1230],{}," support sharing a broker with other non-FlowFuse applications. If you\nalready have mosquitto installed and running, you will need to run a second instance\ndedicated to FlowFuse.",[14,9549,9550,9551,273],{},"You can either follow the manual install steps, which involve building the authentication\nplugin from scratch, or make use of the ",[41,9552,9554],{"href":9553},"#docker-install","Docker install",[104,9556,9558],{"id":9557},"manual-install","Manual install",[14,9560,9561,9563,9564,9566],{},[364,9562,1760],{},": if you are running on Windows, you will need to follow the ",[41,9565,9554],{"href":9553},"\ninstructions below due to a limitation of the authentication plugin we use.",[14,9568,9569,9570,9575],{},"Follow the appropriate ",[41,9571,9574],{"href":9572,"rel":9573},"https:\u002F\u002Fmosquitto.org\u002Fdownload\u002F",[831],"install instructions"," for\nyour operating system.",[14,9577,9578,9579,9583],{},"Once installed, you can download pre-built binaries for Linux platforms from\n",[41,9580,785],{"href":9581,"rel":9582},"https:\u002F\u002Fgithub.com\u002Fiegomez\u002Fmosquitto-go-auth\u002Freleases\u002Flatest",[831]," and then jump\nto step 4 below.",[14,9585,9586],{},"On MacOS you will need to build and install the authentication plugin.",[398,9588,9589,9606,9614,9621],{},[31,9590,9591,9592],{},"Clone the plugin repository",[50,9593,9595],{"className":52,"code":9594,"language":54,"meta":55,"style":55},"git clone https:\u002F\u002Fgithub.com\u002Fiegomez\u002Fmosquitto-go-auth.git\n",[18,9596,9597],{"__ignoreMap":55},[59,9598,9599,9601,9603],{"class":61,"line":62},[59,9600,7523],{"class":65},[59,9602,7526],{"class":69},[59,9604,9605],{"class":69}," https:\u002F\u002Fgithub.com\u002Fiegomez\u002Fmosquitto-go-auth.git\n",[31,9607,9608,9609],{},"Follow the instructions on ",[41,9610,9613],{"href":9611,"rel":9612},"https:\u002F\u002Fgithub.com\u002Fiegomez\u002Fmosquitto-go-auth#building-the-plugin",[831],"building the plugin",[31,9615,9616,9617,9620],{},"This should result in a file called ",[18,9618,9619],{},"go-auth.so"," being generated",[31,9622,9623,9624,9627,9628,9630,9631,9658],{},"Run mosquitto with the configuration file found in the ",[18,9625,9626],{},"broker"," directory",[662,9629],{},"You will need to customise the values to match your local configuration:",[28,9632,9633,9642,9648],{},[31,9634,9635,9638,9639,9641],{},[18,9636,9637],{},"auth_plugin"," - set to the path of the ",[18,9640,9619],{}," file built in the previous step",[31,9643,9644,9647],{},[18,9645,9646],{},"listener 1883\u002F1884"," - if you already have mosquitto running locally, you'll need to\nchange these ports to something else.",[31,9649,9650,9653,9654,9657],{},[18,9651,9652],{},"auth_opt_http_host"," \u002F ",[18,9655,9656],{},"auth_opt_http_port"," - if you plan to run the platform\non a different port, change these settings to match.",[50,9659,9661],{"className":52,"code":9660,"language":54,"meta":55,"style":55},"mosquitto -c broker\u002Fmosquitto.conf\n",[18,9662,9663],{"__ignoreMap":55},[59,9664,9665,9668,9671],{"class":61,"line":62},[59,9666,9667],{"class":65},"mosquitto",[59,9669,9670],{"class":73}," -c",[59,9672,9673],{"class":69}," broker\u002Fmosquitto.conf\n",[104,9675,5],{"id":12},[14,9677,9678],{},"Instead of installing and building mosquitto and the authentication plugin from source,\nyou can use a pre-built docker image that provides everything needed.",[398,9680,9681,9699,9727],{},[31,9682,9683,9684],{},"First pull the latest version of the pre-built container",[50,9685,9687],{"className":52,"code":9686,"language":54,"meta":55,"style":55},"docker pull iegomez\u002Fmosquitto-go-auth\n",[18,9688,9689],{"__ignoreMap":55},[59,9690,9691,9693,9696],{"class":61,"line":62},[59,9692,66],{"class":65},[59,9694,9695],{"class":69}," pull",[59,9697,9698],{"class":69}," iegomez\u002Fmosquitto-go-auth\n",[31,9700,9701,9702,9704,9705,9630,9707],{},"A default mosquitto.conf file can be found in the ",[18,9703,9626],{}," directory.",[662,9706],{},[28,9708,9709,9714,9719],{},[31,9710,9711,9713],{},[18,9712,9652],{}," value to match the IP address of either the docker0 interface or the external IP address of the machine running the FlowFuse platform",[31,9715,9716,9718],{},[18,9717,9656],{}," if you have changed the port the FlowFuse platform is running on",[31,9720,9721,9723,9724],{},[18,9722,9637],{}," should be changed to ",[18,9725,9726],{},"auth_plugin \u002Fmosquitto\u002Fgo-auth.so",[31,9728,9729,9730,9767,9769,9770,3601,9772,9775,9776,9779,9780,273],{},"Start the container with the following command",[50,9731,9733],{"className":52,"code":9732,"language":54,"meta":55,"style":55},"docker run -d -v \u002Fopt\u002Fflowforge\u002Fbroker\u002Fmosquitto.conf:\u002Fetc\u002Fmosquitto\u002Fmosquitto.conf -p 1883:1883 -p 1884:1884 --name flowforge-broker iegomez\u002Fmosquitto-go-auth\n",[18,9734,9735],{"__ignoreMap":55},[59,9736,9737,9739,9741,9744,9747,9750,9752,9755,9757,9760,9762,9765],{"class":61,"line":62},[59,9738,66],{"class":65},[59,9740,70],{"class":69},[59,9742,9743],{"class":73}," -d",[59,9745,9746],{"class":73}," -v",[59,9748,9749],{"class":69}," \u002Fopt\u002Fflowforge\u002Fbroker\u002Fmosquitto.conf:\u002Fetc\u002Fmosquitto\u002Fmosquitto.conf",[59,9751,8441],{"class":73},[59,9753,9754],{"class":69}," 1883:1883",[59,9756,8441],{"class":73},[59,9758,9759],{"class":69}," 1884:1884",[59,9761,8447],{"class":73},[59,9763,9764],{"class":69}," flowforge-broker",[59,9766,9698],{"class":69},[662,9768],{},"This will map the ",[18,9771,4475],{},[18,9773,9774],{},"1884"," ports to the host machine so they can be accessed outside of the container. If you already have an MQTT broker running on port 1883, then you'll need to modify the ",[18,9777,9778],{},"-p"," options to use a different set of ports. For example: ",[18,9781,9782],{},"-p 9883:1883 -p 9884:1884",[23,9784,9786],{"id":9785},"file-server","File Server",[14,9788,9789],{},"By default the FlowFuse File Server component is disabled as it is a licensed feature. If you provide a license you can start the File Server with the following command:",[50,9791,9793],{"className":52,"code":9792,"language":54,"meta":55,"style":55},"sudo service flowforge-file start\n",[18,9794,9795],{"__ignoreMap":55},[59,9796,9797,9799,9802,9805],{"class":61,"line":62},[59,9798,9188],{"class":65},[59,9800,9801],{"class":69}," service",[59,9803,9804],{"class":69}," flowforge-file",[59,9806,9510],{"class":69},[14,9808,9809,9810,9812],{},"You can then uncomment the following section in the ",[18,9811,9421],{}," file",[50,9814,9816],{"className":165,"code":9815,"language":167,"meta":55,"style":55},"#################################################\n# File Server config                            #\n#################################################\n\nfileStore:\n  url: http:\u002F\u002Flocalhost:3001\n\n",[18,9817,9818,9823,9828,9832,9836,9843],{"__ignoreMap":55},[59,9819,9820],{"class":61,"line":62},[59,9821,9822],{"class":3773},"#################################################\n",[59,9824,9825],{"class":61,"line":77},[59,9826,9827],{"class":3773},"# File Server config                            #\n",[59,9829,9830],{"class":61,"line":88},[59,9831,9822],{"class":3773},[59,9833,9834],{"class":61,"line":99},[59,9835,188],{"emptyLinePlaceholder":187},[59,9837,9838,9841],{"class":61,"line":156},[59,9839,9840],{"class":174},"fileStore",[59,9842,196],{"class":178},[59,9844,9845,9848,9850],{"class":61,"line":216},[59,9846,9847],{"class":174},"  url",[59,9849,179],{"class":178},[59,9851,9852],{"class":69},"http:\u002F\u002Flocalhost:3001\n",[23,9854,9856],{"id":9855},"upgrade","Upgrade",[14,9858,9859],{},"If upgrading from 1.x.y to 2.x.y then you may need to upgrade from NodeJS v16 to NodeJS v18.\nPlease ensure you do this before the following steps.",[14,9861,9862,9863,9866],{},"To upgrade to the latest release you can follow these steps. Replace ",[18,9864,9865],{},"x.y.z"," with the\nversion you are upgrading to.",[398,9868,9869,9882,9903,9927,9930],{},[31,9870,9871,9872,3497,9875],{},"Stop FlowFuse ",[18,9873,9874],{},"sudo service flowfuse stop",[8351,9876,9877],{},[41,9878,3158],{"href":9879,"ariaDescribedBy":9880,"dataFootnoteRef":55,"id":9881},"#user-content-fn-2",[8357],"user-content-fnref-2",[31,9883,9884,9885,9888,9889],{},"Change into the ",[18,9886,9887],{},"app"," directory\n",[28,9890,9891,9897],{},[31,9892,9893,9896],{},[18,9894,9895],{},"cd \u002Fopt\u002Fflowforge\u002Fapp"," (Linux\u002FMacOS)",[31,9898,9899,9902],{},[18,9900,9901],{},"cd c:\\flowforge\\app"," (Windows)",[31,9904,9905,9906],{},"NPM install the desired version\n",[28,9907,9908,9922],{},[31,9909,9910,9913,9914],{},[18,9911,9912],{},"sudo -u flowforge npm install @flowfuse\u002Fflowfuse@x.y.z"," (Linux\u002FMacOS) ",[8351,9915,9916],{},[41,9917,9921],{"href":9918,"ariaDescribedBy":9919,"dataFootnoteRef":55,"id":9920},"#user-content-fn-3",[8357],"user-content-fnref-3","3",[31,9923,9924,9902],{},[18,9925,9926],{},"npm install @flowfuse\u002Fflowfuse@x.y.z",[31,9928,9929],{},"Check the release notes for any additional steps needed to upgrade the particular version",[31,9931,9932,9933,3497,9936],{},"Restart FlowFuse ",[18,9934,9935],{},"sudo service flowfuse start",[8351,9937,9938],{},[41,9939,3158],{"href":9879,"ariaDescribedBy":9940,"dataFootnoteRef":55,"id":9941},[8357],"user-content-fnref-2-2",[14,9943,9944,9945,9948,9949],{},"If you are running as your normal user you can drop the ",[18,9946,9947],{},"sudo -u flowfuse"," and just run ",[18,9950,9926],{},[23,9952,9954],{"id":9953},"uninstall","Uninstall",[14,9956,9957,9958,1706,9961,9704],{},"To uninstall, stop all the running processes and then delete the ",[18,9959,9960],{},"\u002Fopt\u002Fflowfuse",[18,9962,9963],{},"c:\\flowfuse\\",[8989,9965,9967,9970],{"className":9966,"dataFootnotes":55},[8992],[23,9968,8997],{"className":9969,"id":8357},[8996],[398,9971,9972,9978,9994],{},[31,9973,9974,9975],{"id":9002},"Arm6 devices, such as the original Raspberry Pi Zero and Zero W are not supported. ",[41,9976,9013],{"href":9009,"ariaLabel":9010,"className":9977,"dataFootnoteBackref":55},[9012],[31,9979,9981,9982,3497,9987],{"id":9980},"user-content-fn-2","Assumes you are running FlowFuse as a Linux service. ",[41,9983,9013],{"href":9984,"ariaLabel":9985,"className":9986,"dataFootnoteBackref":55},"#user-content-fnref-2","Back to reference 2",[9012],[41,9988,9013,9992],{"href":9989,"ariaLabel":9990,"className":9991,"dataFootnoteBackref":55},"#user-content-fnref-2-2","Back to reference 2-2",[9012],[8351,9993,3158],{},[31,9995,9997,9998,10000,10001],{"id":9996},"user-content-fn-3","Assumes you are running FlowFuse as the ",[18,9999,7855],{}," user as created by the installer ",[41,10002,9013],{"href":10003,"ariaLabel":10004,"className":10005,"dataFootnoteBackref":55},"#user-content-fnref-3","Back to reference 3",[9012],[316,10007,10008],{},"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 .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 .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":55,"searchDepth":77,"depth":77,"links":10010},[10011,10015,10018,10019,10020,10021,10025,10026,10027,10028],{"id":25,"depth":77,"text":26,"children":10012},[10013,10014],{"id":9058,"depth":88,"text":9059},{"id":9093,"depth":88,"text":9094},{"id":9163,"depth":77,"text":9164,"children":10016},[10017],{"id":9396,"depth":88,"text":9397},{"id":8033,"depth":77,"text":7820},{"id":7968,"depth":77,"text":7814},{"id":9513,"depth":77,"text":584},{"id":9524,"depth":77,"text":9525,"children":10022},[10023,10024],{"id":9557,"depth":88,"text":9558},{"id":12,"depth":88,"text":5},{"id":9785,"depth":77,"text":9786},{"id":9855,"depth":77,"text":9856},{"id":9953,"depth":77,"text":9954},{"id":8357,"depth":77,"text":8997},{},"contribute\u002Flocal\u002FREADME.md","\u002Fdocs\u002Fcontribute\u002Flocal",{"title":9040,"description":9048},"docs\u002Fcontribute\u002Flocal\u002Findex","mFbvgHQHGzD9mLl0Iukb-dG9nT4LYz4SPDQE-jrff9c",{"id":10036,"title":1128,"body":10037,"description":10044,"extension":329,"layout":330,"meta":10188,"navGroup":330,"navOrder":330,"navTitle":1128,"navigation":187,"originalPath":10189,"path":10190,"redirect":330,"seo":10191,"stem":10192,"updated":337,"version":338,"__hash__":10193},"docs\u002Fdocs\u002Fcontribute\u002Flocal\u002Fstacks.md",{"type":7,"value":10038,"toc":10184},[10039,10042,10045,10048,10069,10072,10082,10086,10089,10092,10159,10162,10165,10181],[10,10040,1128],{"id":10041},"local-stacks",[14,10043,10044],{},"A Stack defines a set of platform configuration options that will get\napplied to each Node-RED instance when it is created.",[14,10046,10047],{},"For the Local deployment model, this covers two things:",[28,10049,10050,10063],{},[31,10051,10052,10055,10056,10059,10060,273],{},[18,10053,10054],{},"memory"," - the value to apply (in MB) to the Node.js ",[18,10057,10058],{},"max-old-space-size"," option.\nThis defines the point where Node.js will start freeing unused memory. It is\nnot a hard limit - Node-RED's memory usage will not be capped - but this\nis useful when running on a memory constrained device such as a Raspberry Pi. Recommended minimum ",[18,10061,10062],{},"256",[31,10064,10065,10068],{},[18,10066,10067],{},"nodered"," - the version number of Node-RED to use. This should match the value used in the steps following.",[14,10070,10071],{},"The FlowFuse installer will create a default stack using the latest stable\nrelease of Node-RED.",[14,10073,10074,10075,1706,10078,10081],{},"The stacks are stored under ",[18,10076,10077],{},"\u002Fopt\u002Fflowforge\u002Fvar\u002Fstacks",[18,10079,10080],{},"c:\\flowforge\\var\\stacks"," on Windows.",[104,10083,10085],{"id":10084},"creating-a-stack","Creating a Stack",[14,10087,10088],{},"When a new version of Node-RED is released, it can be added to your FlowFuse\nplatform by creating a new stack.",[14,10090,10091],{},"For a local install there are two steps required:",[398,10093,10094,10153],{},[31,10095,10096,10097,10099,10100,10103,10104,10106,10107,10109,10110,10129,9136,10131],{},"Install a new Node-RED version",[662,10098],{},"In the FlowFuse Home directory, run the provided install script. You\nmust provide the full Node-RED version number, eg ",[18,10101,10102],{},"3.0.2",", or use ",[18,10105,285],{}," to install the most recent stable version.",[662,10108],{},"Linux\u002FMac:",[50,10111,10113],{"className":52,"code":10112,"language":54,"meta":55,"style":55},"cd \u002Fopt\u002Fflowforge\n.\u002Fbin\u002Fff-install-stack.sh 3.0.2\n",[18,10114,10115,10121],{"__ignoreMap":55},[59,10116,10117,10119],{"class":61,"line":62},[59,10118,7534],{"class":73},[59,10120,9194],{"class":69},[59,10122,10123,10126],{"class":61,"line":77},[59,10124,10125],{"class":65},".\u002Fbin\u002Fff-install-stack.sh",[59,10127,10128],{"class":73}," 3.0.2\n",[662,10130],{},[50,10132,10134],{"className":52,"code":10133,"language":54,"meta":55,"style":55},"cd c:\\flowforge\nbin\\ff-install-stack.bat 3.0.2\n",[18,10135,10136,10146],{"__ignoreMap":55},[59,10137,10138,10140,10142,10144],{"class":61,"line":62},[59,10139,7534],{"class":73},[59,10141,9223],{"class":69},[59,10143,9226],{"class":73},[59,10145,9229],{"class":69},[59,10147,10148,10151],{"class":61,"line":77},[59,10149,10150],{"class":65},"bin\\ff-install-stack.bat",[59,10152,10128],{"class":73},[31,10154,10155,10156,273],{},"Add the Stack into the FlowFuse Admin UI - see ",[41,10157,1108],{"href":10158},"\u002Fdocs\u002Fadmin\u002Fintroduction\u002F#managing-stacks",[104,10160,10161],{"id":8081},"Development Only",[14,10163,10164],{},"If you are developing FlowFuse having checked it out from GitHub then you can run\nthe following command in the repository root to install a stack:",[50,10166,10168],{"className":52,"code":10167,"language":54,"meta":55,"style":55},"npm run install-stack --vers=3.0.2\n",[18,10169,10170],{"__ignoreMap":55},[59,10171,10172,10174,10176,10178],{"class":61,"line":62},[59,10173,7542],{"class":65},[59,10175,70],{"class":69},[59,10177,7870],{"class":69},[59,10179,10180],{"class":73}," --vers=3.0.2\n",[316,10182,10183],{},"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 pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}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":55,"searchDepth":77,"depth":77,"links":10185},[10186,10187],{"id":10084,"depth":88,"text":10085},{"id":8081,"depth":88,"text":10161},{},"contribute\u002Flocal\u002Fstacks.md","\u002Fdocs\u002Fcontribute\u002Flocal\u002Fstacks",{"title":1128,"description":10044},"docs\u002Fcontribute\u002Flocal\u002Fstacks","2A_bubVA_hnvE8YpDMeOZydyhmkY83K6DJwEVeeQXcA",{"id":10195,"title":10196,"body":10197,"description":10204,"extension":329,"layout":330,"meta":10305,"navGroup":330,"navOrder":330,"navTitle":1008,"navigation":187,"originalPath":10306,"path":7843,"redirect":330,"seo":10307,"stem":10308,"updated":337,"version":338,"__hash__":10309},"docs\u002Fdocs\u002Fcontribute\u002Fteam-broker.md","Team Broker configuration",{"type":7,"value":10198,"toc":10298},[10199,10202,10205,10209,10213,10217,10220,10223,10229,10232,10238,10241,10247,10251,10254,10260,10262,10272,10278,10282,10288],[10,10200,10196],{"id":10201},"team-broker-configuration",[14,10203,10204],{},"The FlowFuse Team Broker makes use of an EMQX instance.",[23,10206,10208],{"id":10207},"requirement","Requirement",[28,10210,10211],{},[31,10212,1209],{},[23,10214,10216],{"id":10215},"configuration-files","Configuration files",[14,10218,10219],{},"Create a directory where the configuration for your broker will live. Create the following three files, with their respective content, in that directory:",[14,10221,10222],{},"cluster.hocon",[50,10224,10227],{"className":10225,"code":10226,"language":3920},[3918],"authentication = [\n  {\n    backend = http\n    body {\n      clientId = \"${clientid}\"\n      password = \"${password}\"\n      username = \"${username}\"\n    }\n    connect_timeout = \"15s\"\n    enable = true\n    enable_pipelining = 100\n    headers {\n      content-type = \"application\u002Fjson\"\n    }\n    mechanism = password_based\n    method = post\n    pool_size = 8\n    request_timeout = \"5s\"\n    ssl {\n      ciphers = []\n      depth = 10\n      enable = false\n      hibernate_after = \"5s\"\n      log_level = notice\n      reuse_sessions = true\n      secure_renegotiate = true\n      verify = verify_peer\n      versions = [\n        \"tlsv1.3\",\n        \"tlsv1.2\"\n      ]\n    }\n    url = \"http:\u002F\u002Fhost.docker.internal:3000\u002Fapi\u002Fcomms\u002Fv2\u002Fauth\"\n  },\n  {\n    backend = built_in_database\n    bootstrap_file = \"${EMQX_ETC_DIR}\u002Fauth-built-in-db-bootstrap.csv\"\n    bootstrap_type = plain\n    enable = true\n    mechanism = password_based\n    password_hash_algorithm {name = plain, salt_position = disable}\n    user_id_type = username\n  }\n]\nauthorization {\n  cache {\n    enable = true\n    excludes = []\n    max_size = 32\n    ttl = \"1m\"\n  }\n  deny_action = ignore\n  no_match = allow\n  sources = [\n    {\n      body {\n        action = \"${action}\"\n        topic = \"${topic}\"\n        username = \"${username}\"\n      }\n      connect_timeout = \"15s\"\n      enable = true\n      enable_pipelining = 100\n      headers {\n        content-type = \"application\u002Fjson\"\n      }\n      method = post\n      pool_size = 8\n      request_timeout = \"30s\"\n      ssl {\n        ciphers = []\n        depth = 10\n        enable = false\n        hibernate_after = \"5s\"\n        log_level = notice\n        reuse_sessions = true\n        secure_renegotiate = true\n        verify = verify_peer\n        versions = [\n          \"tlsv1.3\",\n          \"tlsv1.2\"\n        ]\n      }\n      type = http\n      url = \"http:\u002F\u002Fhost.docker.internal:3000\u002Fapi\u002Fcomms\u002Fv2\u002Facls\"\n    },\n    {   \n      enable = false\n      path = \"data\u002Fauthz\u002Facl.conf\"\n      type = file\n    }\n  ]\n}\nlisteners {\n  tcp {\n    default {\n      acceptors = 16\n      access_rules = [\n        \"allow all\"\n      ]\n      bind = \"0.0.0.0:1883\"\n      enable = true\n      enable_authn = true\n      max_conn_rate = infinity\n      max_connections = infinity\n      mountpoint = \"${client_attrs.team}\"\n      proxy_protocol = false\n      proxy_protocol_timeout = \"3s\"\n      tcp_options {\n        active_n = 100\n        backlog = 1024\n        buffer = \"4KB\"\n        high_watermark = \"1MB\"\n        keepalive = none\n        nodelay = true\n        reuseaddr = true\n        send_timeout = \"15s\"\n        send_timeout_close = true\n      }\n      zone = default\n    }\n  }\n  ws {\n    default {\n      acceptors = 16\n      access_rules = [\n        \"allow all\"\n      ]\n      bind = \"0.0.0.0:8083\"\n      enable = true\n      enable_authn = true\n      max_conn_rate = infinity\n      max_connections = infinity\n      mountpoint = \"${client_attrs.team}\"\n      proxy_protocol = false\n      proxy_protocol_timeout = \"3s\"\n      tcp_options {\n        active_n = 100\n        backlog = 1024\n        buffer = \"4KB\"\n        high_watermark = \"1MB\"\n        keepalive = none\n        nodelay = true\n        reuseaddr = true\n        send_timeout = \"15s\"\n        send_timeout_close = true\n      }\n      websocket {\n        allow_origin_absence = true\n        check_origin_enable = false\n        check_origins = \"http:\u002F\u002Flocalhost:18083, http:\u002F\u002F127.0.0.1:18083\"\n        compress = false\n        deflate_opts {\n          client_context_takeover = takeover\n          client_max_window_bits = 15\n          mem_level = 8\n          server_context_takeover = takeover\n          server_max_window_bits = 15\n          strategy = default\n        }\n        fail_if_no_subprotocol = true\n        idle_timeout = \"7200s\"\n        max_frame_size = infinity\n        mqtt_path = \"\u002F\"\n        mqtt_piggyback = multiple\n        proxy_address_header = \"x-forwarded-for\"\n        proxy_port_header = \"x-forwarded-port\"\n        supported_subprotocols = \"mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5\"\n        validate_utf8 = true\n      }\n      zone = default\n    }\n  }\n}\ndashboard {\n  default_password = topSecret\n}\napi_key {\n  bootstrap_file = \"\u002Fmounted\u002Fconfig\u002Fapi-keys\"\n}\n",[18,10228,10226],{"__ignoreMap":55},[14,10230,10231],{},"acl.conf",[50,10233,10236],{"className":10234,"code":10235,"language":3920},[3918],"{allow, {username, {re, \"^dashboard$\"}}, subscribe, [\"$SYS\u002F#\"]}.\n\n{allow, {ipaddr, \"127.0.0.1\"}, all, [\"$SYS\u002F#\", \"#\"]}.\n\n{deny, all, subscribe, [\"$SYS\u002F#\"]}.\n\n{allow, all}.\n",[18,10237,10235],{"__ignoreMap":55},[14,10239,10240],{},"api-keys",[50,10242,10245],{"className":10243,"code":10244,"language":3920},[3918],"flowforge:verySecret:administrator\n",[18,10246,10244],{"__ignoreMap":55},[23,10248,10250],{"id":10249},"starting","Starting",[14,10252,10253],{},"The following docker command should be run in the directory the configuration files were stored.",[50,10255,10258],{"className":10256,"code":10257,"language":3920},[3918],"docker run -d --rm \\\n  -v $(pwd)\u002Fcluster.hocon:\u002Fopt\u002Femqx\u002Fdata\u002Fconfigs\u002Fcluster.hocon \\\n  -v $(pwd)\u002Fapi-keys:\u002Fmounted\u002Fconfig\u002Fapi-keys \\\n  -v $(pwd)\u002Facl.conf:\u002Fopt\u002Femqx\u002Fdata\u002Fauthz\u002Facl.conf \\\n  --add-host=host.docker.internal:host-gateway \\\n  -p 1883:1883 -p 8083:8083 -p 18083:18083 --name emqx emqx\u002Femqx:5.8.0\n",[18,10259,10257],{"__ignoreMap":55},[23,10261,7820],{"id":8033},[14,10263,10264,10265,10267,10268,10271],{},"Make sure the ",[18,10266,9626],{}," section of the ",[18,10269,10270],{},"flowfuse.yml"," is updated as follows",[50,10273,10276],{"className":10274,"code":10275,"language":3920},[3918],"broker:\n  url: mqtt:\u002F\u002F[::1]:1883\n  public_url: ws:\u002F\u002F\u003Cip-of-dev-computer>:8083\n  teamBroker:\n    enabled: true\n",[18,10277,10275],{"__ignoreMap":55},[23,10279,10281],{"id":10280},"access-to-the-eqmx-dashboard","Access to the EQMX Dashboard",[14,10283,10284,10285],{},"You can log into the EMQX Dashboard at ",[18,10286,10287],{},"http:\u002F\u002Flocahost:18083",[14,10289,10290,10291,10294,10295],{},"Username: ",[18,10292,10293],{},"admin","\nPassword: ",[18,10296,10297],{},"topSecret",{"title":55,"searchDepth":77,"depth":77,"links":10299},[10300,10301,10302,10303,10304],{"id":10207,"depth":77,"text":10208},{"id":10215,"depth":77,"text":10216},{"id":10249,"depth":77,"text":10250},{"id":8033,"depth":77,"text":7820},{"id":10280,"depth":77,"text":10281},{},"contribute\u002Fteam-broker.md",{"title":10196,"description":10204},"docs\u002Fcontribute\u002Fteam-broker","kR7lNAMWYMuAW_yDIjvJsbVvghX4oezsRxhvJBRlg0I",{"id":10311,"title":10312,"body":10313,"description":55,"extension":329,"layout":330,"meta":10422,"navGroup":330,"navOrder":330,"navTitle":10423,"navigation":187,"originalPath":10424,"path":10425,"redirect":330,"seo":10426,"stem":10427,"updated":337,"version":338,"__hash__":10428},"docs\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fdevice-editor.md","Enabling the device editor",{"type":7,"value":10314,"toc":10420},[10315,10318,10418],[10,10316,10312],{"id":10317},"enabling-the-device-editor",[50,10319,10321],{"className":6423,"code":10320,"language":6425,"meta":55,"style":55},"sequenceDiagram\n    User->>FrontEnd: Clicks 'open editor' against device\n    FrontEnd->>+Forge: PUT \u002Fapi\u002Fv1\u002Fdevices\u002F:id\u002Feditor { tunnel: 'enable' }\n    Forge->Forge: Generates \u003Ctoken>\n    Forge--)Device: Publishes command to establish connection with \u003Ctoken>\n    Device--)Forge: WS Connect \u002Fapi\u002Fv1\u002Fdevices\u002F:id\u002Feditor\u002Fcomms\u002F:token\n    \n    Forge->>-FrontEnd: Returns session identifier\n    FrontEnd->>FrontEnd: Opens \u002Fdevice\u002F\u003Cid>\u002Feditor\u002F\n    FrontEnd-->+Forge: Sends requests to \u002Fdevice\u002F\u003Cid>\u002Feditor\u002F**\n    Forge--)+Device: Request proxied over WebSocket\n    Device-->>Editor: Performs request on local Node-RED\n    Editor-->>Device: Returns response\n    Device-->>-Forge: Streams response back\n    Forge-->>-FrontEnd: Streams response back\n    User->>FrontEnd: User navigates away\n    FrontEnd-->Forge: Node-RED WebSocket closes\n    Note over Forge: if no active WebSockets for this device\n    Forge--)Device: Close WebSocket\n",[18,10322,10323,10328,10333,10338,10343,10348,10353,10358,10363,10368,10373,10378,10383,10388,10393,10398,10403,10408,10413],{"__ignoreMap":55},[59,10324,10325],{"class":61,"line":62},[59,10326,10327],{},"sequenceDiagram\n",[59,10329,10330],{"class":61,"line":77},[59,10331,10332],{},"    User->>FrontEnd: Clicks 'open editor' against device\n",[59,10334,10335],{"class":61,"line":88},[59,10336,10337],{},"    FrontEnd->>+Forge: PUT \u002Fapi\u002Fv1\u002Fdevices\u002F:id\u002Feditor { tunnel: 'enable' }\n",[59,10339,10340],{"class":61,"line":99},[59,10341,10342],{},"    Forge->Forge: Generates \u003Ctoken>\n",[59,10344,10345],{"class":61,"line":156},[59,10346,10347],{},"    Forge--)Device: Publishes command to establish connection with \u003Ctoken>\n",[59,10349,10350],{"class":61,"line":216},[59,10351,10352],{},"    Device--)Forge: WS Connect \u002Fapi\u002Fv1\u002Fdevices\u002F:id\u002Feditor\u002Fcomms\u002F:token\n",[59,10354,10355],{"class":61,"line":224},[59,10356,10357],{},"    \n",[59,10359,10360],{"class":61,"line":233},[59,10361,10362],{},"    Forge->>-FrontEnd: Returns session identifier\n",[59,10364,10365],{"class":61,"line":241},[59,10366,10367],{},"    FrontEnd->>FrontEnd: Opens \u002Fdevice\u002F\u003Cid>\u002Feditor\u002F\n",[59,10369,10370],{"class":61,"line":249},[59,10371,10372],{},"    FrontEnd-->+Forge: Sends requests to \u002Fdevice\u002F\u003Cid>\u002Feditor\u002F**\n",[59,10374,10375],{"class":61,"line":257},[59,10376,10377],{},"    Forge--)+Device: Request proxied over WebSocket\n",[59,10379,10380],{"class":61,"line":3137},[59,10381,10382],{},"    Device-->>Editor: Performs request on local Node-RED\n",[59,10384,10385],{"class":61,"line":3150},[59,10386,10387],{},"    Editor-->>Device: Returns response\n",[59,10389,10390],{"class":61,"line":3163},[59,10391,10392],{},"    Device-->>-Forge: Streams response back\n",[59,10394,10395],{"class":61,"line":3176},[59,10396,10397],{},"    Forge-->>-FrontEnd: Streams response back\n",[59,10399,10400],{"class":61,"line":3187},[59,10401,10402],{},"    User->>FrontEnd: User navigates away\n",[59,10404,10405],{"class":61,"line":3193},[59,10406,10407],{},"    FrontEnd-->Forge: Node-RED WebSocket closes\n",[59,10409,10410],{"class":61,"line":3201},[59,10411,10412],{},"    Note over Forge: if no active WebSockets for this device\n",[59,10414,10415],{"class":61,"line":3214},[59,10416,10417],{},"    Forge--)Device: Close WebSocket\n",[316,10419,6631],{},{"title":55,"searchDepth":77,"depth":77,"links":10421},[],{},"Device Editor","contribute\u002Fworkflows\u002Fdevice-editor.md","\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fdevice-editor",{"title":10312,"description":55},"docs\u002Fcontribute\u002Fworkflows\u002Fdevice-editor","PtK6VGXttVaVSEp0BRIFFiQczGGXOuB1Z-rNHrqEmpw",{"id":10430,"title":10431,"body":10432,"description":10439,"extension":329,"layout":330,"meta":10486,"navGroup":330,"navOrder":330,"navTitle":10487,"navigation":187,"originalPath":10488,"path":10489,"redirect":330,"seo":10490,"stem":10491,"updated":337,"version":338,"__hash__":10492},"docs\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Findex.md","Workflows",{"type":7,"value":10433,"toc":10484},[10434,10437,10440],[10,10435,10431],{"id":10436},"workflows",[14,10438,10439],{},"A collection of sequence diagrams for key parts of the FlowFuse platform",[28,10441,10442,10448,10454,10460,10466,10472,10478],{},[31,10443,10444],{},[41,10445,10447],{"href":10446},"\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Flogin","Login",[31,10449,10450],{},[41,10451,10453],{"href":10452},"\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fsignup","User Sign Up",[31,10455,10456],{},[41,10457,10459],{"href":10458},"\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fpassword-reset","Password Reset",[31,10461,10462],{},[41,10463,10465],{"href":10464},"\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Finvite-external-user","Invite External User",[31,10467,10468],{},[41,10469,10471],{"href":10470},"\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fteam-create","Team Create",[31,10473,10474],{},[41,10475,10477],{"href":10476},"\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fproject-create","Project Create",[31,10479,10480],{},[41,10481,10483],{"href":10482},"\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fproject-states","Project States",{"title":55,"searchDepth":77,"depth":77,"links":10485},[],{},"State Flows","contribute\u002Fworkflows\u002FREADME.md","\u002Fdocs\u002Fcontribute\u002Fworkflows",{"title":10431,"description":10439},"docs\u002Fcontribute\u002Fworkflows\u002Findex","Pa2feIfgjtEvaA0KnaCJgPmLID7yIDBmjmwjb7Bzl3k",{"id":10494,"title":10495,"body":10496,"description":55,"extension":329,"layout":330,"meta":10641,"navGroup":330,"navOrder":330,"navTitle":10642,"navigation":187,"originalPath":10643,"path":10464,"redirect":330,"seo":10644,"stem":10645,"updated":337,"version":338,"__hash__":10646},"docs\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Finvite-external-user.md","Invite External User Flow",{"type":7,"value":10497,"toc":10639},[10498,10501,10630,10637],[10,10499,10495],{"id":10500},"invite-external-user-flow",[50,10502,10504],{"className":6423,"code":10503,"language":6425,"meta":55,"style":55},"sequenceDiagram\n    autonumber\n    participant UserEmail\n    participant InvitedUser\n    participant TeamOwner\n    participant UI\n    participant Runtime\n    participant DB\n    Note over TeamOwner: TeamOwner wants to invite an external user to a team\n    TeamOwner->>UI: Opens Add Team Members dialog\n    TeamOwner->>UI: Enters User email, clicks okay\n    UI->>+Runtime: POST \u002Fapi\u002Fv1\u002Fteams\u002F:teamId\u002Finvitations\n    Runtime->>DB: Create Invitation\n    Runtime->>UserEmail: Send email containing link to \u002Faccount\u002Fcreate?email={email}`\n    Runtime->>DB: Update audit log\n    Runtime-->>-UI: { status: 'okay' }\n    Note over TeamOwner: TeamOwner role complete\n    UserEmail-->>InvitedUser: Email received\n    InvitedUser->>+UI: Opens \u002Faccount\u002Fcreate?email={email}\n    UI->>UI: Prefills email field of sign-up page\n    InvitedUser->>UI: Enters details on sign-up page\n    InvitedUser->>UI: Clicks Sign-up\n    UI->>+Runtime: POST \u002Faccount\u002Fregister\n    Runtime->>Runtime: Checks an invite exists for this email\n    Note over InvitedUser,Runtime: Standard sign-up flow continues\n",[18,10505,10506,10510,10515,10520,10525,10530,10535,10540,10545,10550,10555,10560,10565,10570,10575,10580,10585,10590,10595,10600,10605,10610,10615,10620,10625],{"__ignoreMap":55},[59,10507,10508],{"class":61,"line":62},[59,10509,10327],{},[59,10511,10512],{"class":61,"line":77},[59,10513,10514],{},"    autonumber\n",[59,10516,10517],{"class":61,"line":88},[59,10518,10519],{},"    participant UserEmail\n",[59,10521,10522],{"class":61,"line":99},[59,10523,10524],{},"    participant InvitedUser\n",[59,10526,10527],{"class":61,"line":156},[59,10528,10529],{},"    participant TeamOwner\n",[59,10531,10532],{"class":61,"line":216},[59,10533,10534],{},"    participant UI\n",[59,10536,10537],{"class":61,"line":224},[59,10538,10539],{},"    participant Runtime\n",[59,10541,10542],{"class":61,"line":233},[59,10543,10544],{},"    participant DB\n",[59,10546,10547],{"class":61,"line":241},[59,10548,10549],{},"    Note over TeamOwner: TeamOwner wants to invite an external user to a team\n",[59,10551,10552],{"class":61,"line":249},[59,10553,10554],{},"    TeamOwner->>UI: Opens Add Team Members dialog\n",[59,10556,10557],{"class":61,"line":257},[59,10558,10559],{},"    TeamOwner->>UI: Enters User email, clicks okay\n",[59,10561,10562],{"class":61,"line":3137},[59,10563,10564],{},"    UI->>+Runtime: POST \u002Fapi\u002Fv1\u002Fteams\u002F:teamId\u002Finvitations\n",[59,10566,10567],{"class":61,"line":3150},[59,10568,10569],{},"    Runtime->>DB: Create Invitation\n",[59,10571,10572],{"class":61,"line":3163},[59,10573,10574],{},"    Runtime->>UserEmail: Send email containing link to \u002Faccount\u002Fcreate?email={email}`\n",[59,10576,10577],{"class":61,"line":3176},[59,10578,10579],{},"    Runtime->>DB: Update audit log\n",[59,10581,10582],{"class":61,"line":3187},[59,10583,10584],{},"    Runtime-->>-UI: { status: 'okay' }\n",[59,10586,10587],{"class":61,"line":3193},[59,10588,10589],{},"    Note over TeamOwner: TeamOwner role complete\n",[59,10591,10592],{"class":61,"line":3201},[59,10593,10594],{},"    UserEmail-->>InvitedUser: Email received\n",[59,10596,10597],{"class":61,"line":3214},[59,10598,10599],{},"    InvitedUser->>+UI: Opens \u002Faccount\u002Fcreate?email={email}\n",[59,10601,10602],{"class":61,"line":3222},[59,10603,10604],{},"    UI->>UI: Prefills email field of sign-up page\n",[59,10606,10607],{"class":61,"line":3233},[59,10608,10609],{},"    InvitedUser->>UI: Enters details on sign-up page\n",[59,10611,10612],{"class":61,"line":3239},[59,10613,10614],{},"    InvitedUser->>UI: Clicks Sign-up\n",[59,10616,10617],{"class":61,"line":3247},[59,10618,10619],{},"    UI->>+Runtime: POST \u002Faccount\u002Fregister\n",[59,10621,10622],{"class":61,"line":3256},[59,10623,10624],{},"    Runtime->>Runtime: Checks an invite exists for this email\n",[59,10626,10627],{"class":61,"line":3261},[59,10628,10629],{},"    Note over InvitedUser,Runtime: Standard sign-up flow continues\n",[28,10631,10632],{},[31,10633,10634,10635],{},"See also ",[41,10636,10453],{"href":10452},[316,10638,6631],{},{"title":55,"searchDepth":77,"depth":77,"links":10640},[],{},"Invite External Users","contribute\u002Fworkflows\u002Finvite-external-user.md",{"title":10495,"description":55},"docs\u002Fcontribute\u002Fworkflows\u002Finvite-external-user","AwV3hbJLCAdHLkBlYRecMu_XoJlv4BpGN2iFCJ5gbc0",{"id":10648,"title":10649,"body":10650,"description":10657,"extension":329,"layout":330,"meta":10867,"navGroup":330,"navOrder":330,"navTitle":10649,"navigation":187,"originalPath":10868,"path":10446,"redirect":330,"seo":10869,"stem":10870,"updated":337,"version":338,"__hash__":10871},"docs\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Flogin.md","User Login Flows",{"type":7,"value":10651,"toc":10865},[10652,10655,10658,10863],[10,10653,10649],{"id":10654},"user-login-flows",[14,10656,10657],{},"This represents the login flow as of FlowFuse 1.2, that incorporates optional\nSSO",[50,10659,10661],{"className":6423,"code":10660,"language":6425,"meta":55,"style":55},"sequenceDiagram\nparticipant U as User\nparticipant B as Browser\nparticipant RT as ForgeApp\nparticipant DB as Database\nparticipant IDP as IdentifyProvider\nU->>B: Enters username\u002Femail on sign-up page\nU->>B: Clicks login\nB->>RT: POST \u002Faccount\u002Flogin (username=XYZ)\nRT->DB: Checks username\u002Femail against list of SSO registered domains\nalt Email not SSO enabled\n    RT->>B: 403:{ code: 'password_required' }\n    B->>U: Shows password box\n    U->>B: Enters password\n    U->>B: Clicks login\n    B->>RT: POST \u002Faccount\u002Flogin (username\u002Fpassword)\n    RT->>DB: Validates username\u002Fpassword\n    RT->>B: 200:{}\nend\n\nalt Email SSO enabled\n    alt Username provided or Password provided\n        RT->>B: 403:{ code:'sso_required', error:'Please login with your email' }\n        B->>U: Prompts user to enter email not username\n    end\n    alt Email provided\n        RT->>B: 403:{ code:'sso_required', redirect:'\u002Faccount\u002Flogin?u=\u003Cbase64 encoded email>' }\n        B->>RT: GET \u002Faccount\u002Flogin?u=\u003Cbase64 encoded email>\n        RT->>RT: passport.authenticate\n        RT->>IDP: SAML exchange\n        IDP->>IDP: User authentication\n        IDP->>RT: POST \u003CTBD>\n        RT->>DB: Verify Email against users\n        alt Valid User\n            RT->>DB: create session\n            RT->>B: redirect to \u002F\n            else Unknown User\n            RT->>B: redirect to \u002F\n        end\n    end\nend\n",[18,10662,10663,10667,10672,10677,10682,10687,10692,10697,10702,10707,10712,10717,10722,10727,10732,10737,10742,10747,10752,10757,10761,10766,10771,10776,10781,10786,10791,10796,10801,10806,10811,10816,10821,10826,10831,10836,10841,10846,10850,10855,10859],{"__ignoreMap":55},[59,10664,10665],{"class":61,"line":62},[59,10666,10327],{},[59,10668,10669],{"class":61,"line":77},[59,10670,10671],{},"participant U as User\n",[59,10673,10674],{"class":61,"line":88},[59,10675,10676],{},"participant B as Browser\n",[59,10678,10679],{"class":61,"line":99},[59,10680,10681],{},"participant RT as ForgeApp\n",[59,10683,10684],{"class":61,"line":156},[59,10685,10686],{},"participant DB as Database\n",[59,10688,10689],{"class":61,"line":216},[59,10690,10691],{},"participant IDP as IdentifyProvider\n",[59,10693,10694],{"class":61,"line":224},[59,10695,10696],{},"U->>B: Enters username\u002Femail on sign-up page\n",[59,10698,10699],{"class":61,"line":233},[59,10700,10701],{},"U->>B: Clicks login\n",[59,10703,10704],{"class":61,"line":241},[59,10705,10706],{},"B->>RT: POST \u002Faccount\u002Flogin (username=XYZ)\n",[59,10708,10709],{"class":61,"line":249},[59,10710,10711],{},"RT->DB: Checks username\u002Femail against list of SSO registered domains\n",[59,10713,10714],{"class":61,"line":257},[59,10715,10716],{},"alt Email not SSO enabled\n",[59,10718,10719],{"class":61,"line":3137},[59,10720,10721],{},"    RT->>B: 403:{ code: 'password_required' }\n",[59,10723,10724],{"class":61,"line":3150},[59,10725,10726],{},"    B->>U: Shows password box\n",[59,10728,10729],{"class":61,"line":3163},[59,10730,10731],{},"    U->>B: Enters password\n",[59,10733,10734],{"class":61,"line":3176},[59,10735,10736],{},"    U->>B: Clicks login\n",[59,10738,10739],{"class":61,"line":3187},[59,10740,10741],{},"    B->>RT: POST \u002Faccount\u002Flogin (username\u002Fpassword)\n",[59,10743,10744],{"class":61,"line":3193},[59,10745,10746],{},"    RT->>DB: Validates username\u002Fpassword\n",[59,10748,10749],{"class":61,"line":3201},[59,10750,10751],{},"    RT->>B: 200:{}\n",[59,10753,10754],{"class":61,"line":3214},[59,10755,10756],{},"end\n",[59,10758,10759],{"class":61,"line":3222},[59,10760,188],{"emptyLinePlaceholder":187},[59,10762,10763],{"class":61,"line":3233},[59,10764,10765],{},"alt Email SSO enabled\n",[59,10767,10768],{"class":61,"line":3239},[59,10769,10770],{},"    alt Username provided or Password provided\n",[59,10772,10773],{"class":61,"line":3247},[59,10774,10775],{},"        RT->>B: 403:{ code:'sso_required', error:'Please login with your email' }\n",[59,10777,10778],{"class":61,"line":3256},[59,10779,10780],{},"        B->>U: Prompts user to enter email not username\n",[59,10782,10783],{"class":61,"line":3261},[59,10784,10785],{},"    end\n",[59,10787,10788],{"class":61,"line":3269},[59,10789,10790],{},"    alt Email provided\n",[59,10792,10793],{"class":61,"line":3278},[59,10794,10795],{},"        RT->>B: 403:{ code:'sso_required', redirect:'\u002Faccount\u002Flogin?u=\u003Cbase64 encoded email>' }\n",[59,10797,10798],{"class":61,"line":3284},[59,10799,10800],{},"        B->>RT: GET \u002Faccount\u002Flogin?u=\u003Cbase64 encoded email>\n",[59,10802,10803],{"class":61,"line":3289},[59,10804,10805],{},"        RT->>RT: passport.authenticate\n",[59,10807,10808],{"class":61,"line":3297},[59,10809,10810],{},"        RT->>IDP: SAML exchange\n",[59,10812,10813],{"class":61,"line":3310},[59,10814,10815],{},"        IDP->>IDP: User authentication\n",[59,10817,10818],{"class":61,"line":3321},[59,10819,10820],{},"        IDP->>RT: POST \u003CTBD>\n",[59,10822,10823],{"class":61,"line":3327},[59,10824,10825],{},"        RT->>DB: Verify Email against users\n",[59,10827,10828],{"class":61,"line":3333},[59,10829,10830],{},"        alt Valid User\n",[59,10832,10833],{"class":61,"line":6115},[59,10834,10835],{},"            RT->>DB: create session\n",[59,10837,10838],{"class":61,"line":6152},[59,10839,10840],{},"            RT->>B: redirect to \u002F\n",[59,10842,10843],{"class":61,"line":6158},[59,10844,10845],{},"            else Unknown User\n",[59,10847,10848],{"class":61,"line":6164},[59,10849,10840],{},[59,10851,10852],{"class":61,"line":6170},[59,10853,10854],{},"        end\n",[59,10856,10857],{"class":61,"line":6175},[59,10858,10785],{},[59,10860,10861],{"class":61,"line":6619},[59,10862,10756],{},[316,10864,6631],{},{"title":55,"searchDepth":77,"depth":77,"links":10866},[],{},"contribute\u002Fworkflows\u002Flogin.md",{"title":10649,"description":10657},"docs\u002Fcontribute\u002Fworkflows\u002Flogin","akR-TeLcOlTL7mG7O_3ctqB5oIs-czrBTXR4hXdUM88",{"id":10873,"title":10874,"body":10875,"description":55,"extension":329,"layout":330,"meta":11021,"navGroup":330,"navOrder":330,"navTitle":10874,"navigation":187,"originalPath":11022,"path":10458,"redirect":330,"seo":11023,"stem":11024,"updated":337,"version":338,"__hash__":11025},"docs\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fpassword-reset.md","Reset Password Flow",{"type":7,"value":10876,"toc":11019},[10877,10880,11017],[10,10878,10874],{"id":10879},"reset-password-flow",[50,10881,10883],{"className":6423,"code":10882,"language":6425,"meta":55,"style":55},"sequenceDiagram\n    autonumber\n    participant UserEmail\n    participant User\n    participant UI\n    participant Runtime\n    participant DB\n    User->>UI: Access the login page\n    UI->>UI: Displays 'forgot pw' if `user:reset-password`=true\n    User->>UI: Clicks 'forgot password'\n    User->>UI: Enters their email address, clicks submit\n    UI->>+Runtime: POST \u002Faccount\u002Fforgot_password { email: \u003Cemail> }\n    Runtime->>DB: Get user\n    DB->>Runtime: User\n    Runtime->>DB: Generate AccessToken { scope: 'password:reset' }\n    Runtime->>UserEmail: Send email containing reset link\n    Runtime-->>-UI: { status: 'okay' }\n\n    UserEmail-->>User: Email received\n    User->>UI: Opens \u002Faccount\u002Fchange-password\u002F{token}\n    User->>UI: Enters new details, clicks submit\n    UI->>+Runtime: POST \u002Faccount\u002Freset_password\u002F:token { password }\n    Runtime->>DB: Validate {token} is a valid password reset token\n    Runtime->>DB: Get the user associated with token\n    Runtime->>DB: Change users password\n    Runtime->>DB: Delete the token\n    Runtime->>-UI: {status: 'okay' }\n    UI->UI: Prompt user to login\n\n",[18,10884,10885,10889,10893,10897,10902,10906,10910,10914,10919,10924,10929,10934,10939,10944,10949,10954,10959,10963,10967,10972,10977,10982,10987,10992,10997,11002,11007,11012],{"__ignoreMap":55},[59,10886,10887],{"class":61,"line":62},[59,10888,10327],{},[59,10890,10891],{"class":61,"line":77},[59,10892,10514],{},[59,10894,10895],{"class":61,"line":88},[59,10896,10519],{},[59,10898,10899],{"class":61,"line":99},[59,10900,10901],{},"    participant User\n",[59,10903,10904],{"class":61,"line":156},[59,10905,10534],{},[59,10907,10908],{"class":61,"line":216},[59,10909,10539],{},[59,10911,10912],{"class":61,"line":224},[59,10913,10544],{},[59,10915,10916],{"class":61,"line":233},[59,10917,10918],{},"    User->>UI: Access the login page\n",[59,10920,10921],{"class":61,"line":241},[59,10922,10923],{},"    UI->>UI: Displays 'forgot pw' if `user:reset-password`=true\n",[59,10925,10926],{"class":61,"line":249},[59,10927,10928],{},"    User->>UI: Clicks 'forgot password'\n",[59,10930,10931],{"class":61,"line":257},[59,10932,10933],{},"    User->>UI: Enters their email address, clicks submit\n",[59,10935,10936],{"class":61,"line":3137},[59,10937,10938],{},"    UI->>+Runtime: POST \u002Faccount\u002Fforgot_password { email: \u003Cemail> }\n",[59,10940,10941],{"class":61,"line":3150},[59,10942,10943],{},"    Runtime->>DB: Get user\n",[59,10945,10946],{"class":61,"line":3163},[59,10947,10948],{},"    DB->>Runtime: User\n",[59,10950,10951],{"class":61,"line":3176},[59,10952,10953],{},"    Runtime->>DB: Generate AccessToken { scope: 'password:reset' }\n",[59,10955,10956],{"class":61,"line":3187},[59,10957,10958],{},"    Runtime->>UserEmail: Send email containing reset link\n",[59,10960,10961],{"class":61,"line":3193},[59,10962,10584],{},[59,10964,10965],{"class":61,"line":3201},[59,10966,188],{"emptyLinePlaceholder":187},[59,10968,10969],{"class":61,"line":3214},[59,10970,10971],{},"    UserEmail-->>User: Email received\n",[59,10973,10974],{"class":61,"line":3222},[59,10975,10976],{},"    User->>UI: Opens \u002Faccount\u002Fchange-password\u002F{token}\n",[59,10978,10979],{"class":61,"line":3233},[59,10980,10981],{},"    User->>UI: Enters new details, clicks submit\n",[59,10983,10984],{"class":61,"line":3239},[59,10985,10986],{},"    UI->>+Runtime: POST \u002Faccount\u002Freset_password\u002F:token { password }\n",[59,10988,10989],{"class":61,"line":3247},[59,10990,10991],{},"    Runtime->>DB: Validate {token} is a valid password reset token\n",[59,10993,10994],{"class":61,"line":3256},[59,10995,10996],{},"    Runtime->>DB: Get the user associated with token\n",[59,10998,10999],{"class":61,"line":3261},[59,11000,11001],{},"    Runtime->>DB: Change users password\n",[59,11003,11004],{"class":61,"line":3269},[59,11005,11006],{},"    Runtime->>DB: Delete the token\n",[59,11008,11009],{"class":61,"line":3278},[59,11010,11011],{},"    Runtime->>-UI: {status: 'okay' }\n",[59,11013,11014],{"class":61,"line":3284},[59,11015,11016],{},"    UI->UI: Prompt user to login\n",[316,11018,6631],{},{"title":55,"searchDepth":77,"depth":77,"links":11020},[],{},"contribute\u002Fworkflows\u002Fpassword-reset.md",{"title":10874,"description":55},"docs\u002Fcontribute\u002Fworkflows\u002Fpassword-reset","AAVUjI7HoLPw09ezhL2XEIyckpBYxwHmQ6EXACv1rZo",{"id":11027,"title":11028,"body":11029,"description":55,"extension":329,"layout":330,"meta":11218,"navGroup":330,"navOrder":330,"navTitle":11219,"navigation":187,"originalPath":11220,"path":10476,"redirect":330,"seo":11221,"stem":11222,"updated":337,"version":338,"__hash__":11223},"docs\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fproject-create.md","Sequence For Project Creation",{"type":7,"value":11030,"toc":11216},[11031,11034,11214],[10,11032,11028],{"id":11033},"sequence-for-project-creation",[50,11035,11037],{"className":6423,"code":11036,"language":6425,"meta":55,"style":55},"sequenceDiagram\nautonumber\nparticipant User\nparticipant Ui\nparticipant Runtime\nparticipant ContainerDriver\nparticipant DB\nparticipant Stripe\n\nUser->>Ui: Clicks Create Project\nUser->>Ui: Enters Project Name\nUi->>Runtime: POST \u002Fprojects\nalt billing enabled\n  Runtime->DB: Team has Subscription\n  DB->>Runtime: Subscription\n  alt Valid Subscription\n    Runtime->>Stripe: Add Project to Subscription\n    Runtime->>ContainerDriver: create()\n    Runtime->>ContainerDriver: start()\n    Runtime->>Ui: { status: \"okay\" }\n    Ui->>Ui: Show Project Overview\n    alt success\n      Stripe->>Runtime: POST \u002Fee\u002Fbilling\u002Fcallback\n    else failure\n      Stripe->>Runtime: POST \u002Fee\u002Fbilling\u002Fcallback\n      Runtime->>ContainerDriver: stop()\n      Runtime->>ContainerDriver: disable()\n      Runtime->>User: email message\n    end\n  else no Subscription\n    Runtime->>Ui: Failed to create project, no Billing info\n  end\nelse no billing\n  Runtime->>Ui: { status: \"okay\" }\n  Ui->>Ui: Show Project Overview\nend\n",[18,11038,11039,11043,11048,11053,11058,11063,11068,11073,11078,11082,11087,11092,11097,11102,11107,11112,11117,11122,11127,11132,11137,11142,11147,11152,11157,11161,11166,11171,11176,11180,11185,11190,11195,11200,11205,11210],{"__ignoreMap":55},[59,11040,11041],{"class":61,"line":62},[59,11042,10327],{},[59,11044,11045],{"class":61,"line":77},[59,11046,11047],{},"autonumber\n",[59,11049,11050],{"class":61,"line":88},[59,11051,11052],{},"participant User\n",[59,11054,11055],{"class":61,"line":99},[59,11056,11057],{},"participant Ui\n",[59,11059,11060],{"class":61,"line":156},[59,11061,11062],{},"participant Runtime\n",[59,11064,11065],{"class":61,"line":216},[59,11066,11067],{},"participant ContainerDriver\n",[59,11069,11070],{"class":61,"line":224},[59,11071,11072],{},"participant DB\n",[59,11074,11075],{"class":61,"line":233},[59,11076,11077],{},"participant Stripe\n",[59,11079,11080],{"class":61,"line":241},[59,11081,188],{"emptyLinePlaceholder":187},[59,11083,11084],{"class":61,"line":249},[59,11085,11086],{},"User->>Ui: Clicks Create Project\n",[59,11088,11089],{"class":61,"line":257},[59,11090,11091],{},"User->>Ui: Enters Project Name\n",[59,11093,11094],{"class":61,"line":3137},[59,11095,11096],{},"Ui->>Runtime: POST \u002Fprojects\n",[59,11098,11099],{"class":61,"line":3150},[59,11100,11101],{},"alt billing enabled\n",[59,11103,11104],{"class":61,"line":3163},[59,11105,11106],{},"  Runtime->DB: Team has Subscription\n",[59,11108,11109],{"class":61,"line":3176},[59,11110,11111],{},"  DB->>Runtime: Subscription\n",[59,11113,11114],{"class":61,"line":3187},[59,11115,11116],{},"  alt Valid Subscription\n",[59,11118,11119],{"class":61,"line":3193},[59,11120,11121],{},"    Runtime->>Stripe: Add Project to Subscription\n",[59,11123,11124],{"class":61,"line":3201},[59,11125,11126],{},"    Runtime->>ContainerDriver: create()\n",[59,11128,11129],{"class":61,"line":3214},[59,11130,11131],{},"    Runtime->>ContainerDriver: start()\n",[59,11133,11134],{"class":61,"line":3222},[59,11135,11136],{},"    Runtime->>Ui: { status: \"okay\" }\n",[59,11138,11139],{"class":61,"line":3233},[59,11140,11141],{},"    Ui->>Ui: Show Project Overview\n",[59,11143,11144],{"class":61,"line":3239},[59,11145,11146],{},"    alt success\n",[59,11148,11149],{"class":61,"line":3247},[59,11150,11151],{},"      Stripe->>Runtime: POST \u002Fee\u002Fbilling\u002Fcallback\n",[59,11153,11154],{"class":61,"line":3256},[59,11155,11156],{},"    else failure\n",[59,11158,11159],{"class":61,"line":3261},[59,11160,11151],{},[59,11162,11163],{"class":61,"line":3269},[59,11164,11165],{},"      Runtime->>ContainerDriver: stop()\n",[59,11167,11168],{"class":61,"line":3278},[59,11169,11170],{},"      Runtime->>ContainerDriver: disable()\n",[59,11172,11173],{"class":61,"line":3284},[59,11174,11175],{},"      Runtime->>User: email message\n",[59,11177,11178],{"class":61,"line":3289},[59,11179,10785],{},[59,11181,11182],{"class":61,"line":3297},[59,11183,11184],{},"  else no Subscription\n",[59,11186,11187],{"class":61,"line":3310},[59,11188,11189],{},"    Runtime->>Ui: Failed to create project, no Billing info\n",[59,11191,11192],{"class":61,"line":3321},[59,11193,11194],{},"  end\n",[59,11196,11197],{"class":61,"line":3327},[59,11198,11199],{},"else no billing\n",[59,11201,11202],{"class":61,"line":3333},[59,11203,11204],{},"  Runtime->>Ui: { status: \"okay\" }\n",[59,11206,11207],{"class":61,"line":6115},[59,11208,11209],{},"  Ui->>Ui: Show Project Overview\n",[59,11211,11212],{"class":61,"line":6152},[59,11213,10756],{},[316,11215,6631],{},{"title":55,"searchDepth":77,"depth":77,"links":11217},[],{},"Project Creation","contribute\u002Fworkflows\u002Fproject-create.md",{"title":11028,"description":55},"docs\u002Fcontribute\u002Fworkflows\u002Fproject-create","mMz9nJ4dn74D8KmdsHduEbIj1KwMa0HeBDgMtw3QsWg",{"id":11225,"title":11226,"body":11227,"description":55,"extension":329,"layout":330,"meta":11449,"navGroup":330,"navOrder":330,"navTitle":11450,"navigation":187,"originalPath":11451,"path":10482,"redirect":330,"seo":11452,"stem":11453,"updated":337,"version":338,"__hash__":11454},"docs\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fproject-states.md","States",{"type":7,"value":11228,"toc":11447},[11229,11232,11294,11298,11445],[10,11230,11226],{"id":11231},"states",[28,11233,11234,11240,11246,11252,11258,11264,11270,11276,11282,11288],{},[31,11235,11236,11239],{},[364,11237,11238],{},"Starting (first time)"," The nr-laucher process is starting up. On container based systems this is done by creating a container",[31,11241,11242,11245],{},[364,11243,11244],{},"Loading"," nr-launcher pulling instance settings",[31,11247,11248,11251],{},[364,11249,11250],{},"Installing"," Once the nr-launcher process has started and downloaded the Instance settings it will npm install any nodes in the settings -> palette section.",[31,11253,11254,11257],{},[364,11255,11256],{},"Starting (second time)"," Once any nodes are installed the Node-RED process will be started, this state ends once the process can will respond to HTTP request",[31,11259,11260,11263],{},[364,11261,11262],{},"Running"," The normal state for an Instance",[31,11265,11266,11269],{},[364,11267,11268],{},"Restarting"," If the \"Restart\" action is triggered the nr-launcher will restart just the NR process inside the container. It will pull the latest settings data from the Forge app. A restart will also be triggered if the NR process fails to respond to 3 HTTP health checks in a row. Health checks run ever 7 seconds by default.",[31,11271,11272,11275],{},[364,11273,11274],{},"Suspending"," driver has been asked to suspend the Instance",[31,11277,11278,11281],{},[364,11279,11280],{},"Suspended"," When an instance is Suspended the Node-RED process is stopped and the nr-launcher shutdown, the container is then shutdown and removed on container based platforms",[31,11283,11284,11287],{},[364,11285,11286],{},"Safe"," Node-RED can be started in Safe Mode, this starts the Editor but does not run the flows. This is to allow a user to edit the flow to fix a problem. The flows are started when the flows are deployed. This is triggered if the NR process restarts more than 5 times with a run time of less than 30 seconds between each restart.",[31,11289,11290,11293],{},[364,11291,11292],{},"Stopped"," If the NR process continues to crash while in Safe Mode then it will be placed in to a Stopped state. The nr-launcher is still running but the NR process is not.",[10,11295,11297],{"id":11296},"_230","2.3.0",[50,11299,11301],{"className":6423,"code":11300,"language":6425,"meta":55,"style":55},"stateDiagram-v2\ndirection TB\nInstanceCreated: Instance Created\nstate \"nr-launcher\" as nrLauncher {\n  direction TB\n  LoadingSettings\n  NPM\n  NodeRED\n  Safe: Safe Mode\n  Stopped\n\n  LoadingSettings --> NPM: loading\n  NPM --> NPM: installing\n  NPM --> NodeRED: starting\n  NodeRED --> NodeRED: restarting\n  NodeRED --> NodeRED: running\n  NodeRED --> Safe: safe\n  Safe --> Stopped: crashed\n}\nInstanceDeleted: Instance Deleted\nInstanceSuspended: Instance Suspended\n\n[*] --> InstanceCreated\nInstanceCreated --> nrLauncher : starting\nnrLauncher --> InstanceDeleted\nnrLauncher --> InstanceSuspended: suspending\nInstanceSuspended --> nrLauncher: starting\nInstanceSuspended --> InstanceSuspended: suspended\nInstanceDeleted --> [*]\n",[18,11302,11303,11308,11313,11318,11323,11328,11333,11338,11343,11348,11353,11357,11362,11367,11372,11377,11382,11387,11392,11396,11401,11406,11410,11415,11420,11425,11430,11435,11440],{"__ignoreMap":55},[59,11304,11305],{"class":61,"line":62},[59,11306,11307],{},"stateDiagram-v2\n",[59,11309,11310],{"class":61,"line":77},[59,11311,11312],{},"direction TB\n",[59,11314,11315],{"class":61,"line":88},[59,11316,11317],{},"InstanceCreated: Instance Created\n",[59,11319,11320],{"class":61,"line":99},[59,11321,11322],{},"state \"nr-launcher\" as nrLauncher {\n",[59,11324,11325],{"class":61,"line":156},[59,11326,11327],{},"  direction TB\n",[59,11329,11330],{"class":61,"line":216},[59,11331,11332],{},"  LoadingSettings\n",[59,11334,11335],{"class":61,"line":224},[59,11336,11337],{},"  NPM\n",[59,11339,11340],{"class":61,"line":233},[59,11341,11342],{},"  NodeRED\n",[59,11344,11345],{"class":61,"line":241},[59,11346,11347],{},"  Safe: Safe Mode\n",[59,11349,11350],{"class":61,"line":249},[59,11351,11352],{},"  Stopped\n",[59,11354,11355],{"class":61,"line":257},[59,11356,188],{"emptyLinePlaceholder":187},[59,11358,11359],{"class":61,"line":3137},[59,11360,11361],{},"  LoadingSettings --> NPM: loading\n",[59,11363,11364],{"class":61,"line":3150},[59,11365,11366],{},"  NPM --> NPM: installing\n",[59,11368,11369],{"class":61,"line":3163},[59,11370,11371],{},"  NPM --> NodeRED: starting\n",[59,11373,11374],{"class":61,"line":3176},[59,11375,11376],{},"  NodeRED --> NodeRED: restarting\n",[59,11378,11379],{"class":61,"line":3187},[59,11380,11381],{},"  NodeRED --> NodeRED: running\n",[59,11383,11384],{"class":61,"line":3193},[59,11385,11386],{},"  NodeRED --> Safe: safe\n",[59,11388,11389],{"class":61,"line":3201},[59,11390,11391],{},"  Safe --> Stopped: crashed\n",[59,11393,11394],{"class":61,"line":3214},[59,11395,3336],{},[59,11397,11398],{"class":61,"line":3222},[59,11399,11400],{},"InstanceDeleted: Instance Deleted\n",[59,11402,11403],{"class":61,"line":3233},[59,11404,11405],{},"InstanceSuspended: Instance Suspended\n",[59,11407,11408],{"class":61,"line":3239},[59,11409,188],{"emptyLinePlaceholder":187},[59,11411,11412],{"class":61,"line":3247},[59,11413,11414],{},"[*] --> InstanceCreated\n",[59,11416,11417],{"class":61,"line":3256},[59,11418,11419],{},"InstanceCreated --> nrLauncher : starting\n",[59,11421,11422],{"class":61,"line":3261},[59,11423,11424],{},"nrLauncher --> InstanceDeleted\n",[59,11426,11427],{"class":61,"line":3269},[59,11428,11429],{},"nrLauncher --> InstanceSuspended: suspending\n",[59,11431,11432],{"class":61,"line":3278},[59,11433,11434],{},"InstanceSuspended --> nrLauncher: starting\n",[59,11436,11437],{"class":61,"line":3284},[59,11438,11439],{},"InstanceSuspended --> InstanceSuspended: suspended\n",[59,11441,11442],{"class":61,"line":3289},[59,11443,11444],{},"InstanceDeleted --> [*]\n",[316,11446,6631],{},{"title":55,"searchDepth":77,"depth":77,"links":11448},[],{},"Instance states","contribute\u002Fworkflows\u002Fproject-states.md",{"title":11226,"description":55},"docs\u002Fcontribute\u002Fworkflows\u002Fproject-states","7YiqgRDle1lgbkB-lNdHqBeuOF20wT8nt3A_YpmPzQ8",{"id":11456,"title":11457,"body":11458,"description":55,"extension":329,"layout":330,"meta":11620,"navGroup":330,"navOrder":330,"navTitle":11621,"navigation":187,"originalPath":11622,"path":10452,"redirect":330,"seo":11623,"stem":11624,"updated":337,"version":338,"__hash__":11625},"docs\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fsignup.md","User Sign Up Flow",{"type":7,"value":11459,"toc":11618},[11460,11463,11616],[10,11461,11457],{"id":11462},"user-sign-up-flow",[50,11464,11466],{"className":6423,"code":11465,"language":6425,"meta":55,"style":55},"sequenceDiagram\n    autonumber\n    participant UE as UserEmail\n    participant US as User\n    participant UI\n    participant RT as Runtime\n    participant DB\n    US->>UI: Enters details on sign-up page\n    US->>UI: Clicks Sign-up\n    UI->>+RT: POST \u002Faccount\u002Fregister\n    RT->>DB: Create User\n    RT->>RT: Generate Email Verification Token\n    par Runtime to UserEmail\n        RT->>UE: Send email containing verification code\n    and Runtime to UI\n        RT-->>-UI: { status: 'okay' } and session created\n    end\n\n    UI->>UI: Show 'Check your email' page\n    UE-->>US: Email received\n    US->>+UI: Enters verification code\n    US->>UI: Click \"Continue\" button\n    UI->>+RT: POST \u002Faccount\u002Fverify\u002Ftoken { token }\n    RT->>RT: Checks {token} is for the logged in user\n    RT->>DB: User.email_verified=true\n    loop for each pending invite\n        RT->>DB: Add user to team\n        RT->>DB: Delete invite\n        RT->>DB: Update audit log\n    end\n    RT-->>-US: Redirect '\u002F'\n",[18,11467,11468,11472,11476,11481,11486,11490,11495,11499,11504,11509,11514,11519,11524,11529,11534,11539,11544,11548,11552,11557,11562,11567,11572,11577,11582,11587,11592,11597,11602,11607,11611],{"__ignoreMap":55},[59,11469,11470],{"class":61,"line":62},[59,11471,10327],{},[59,11473,11474],{"class":61,"line":77},[59,11475,10514],{},[59,11477,11478],{"class":61,"line":88},[59,11479,11480],{},"    participant UE as UserEmail\n",[59,11482,11483],{"class":61,"line":99},[59,11484,11485],{},"    participant US as User\n",[59,11487,11488],{"class":61,"line":156},[59,11489,10534],{},[59,11491,11492],{"class":61,"line":216},[59,11493,11494],{},"    participant RT as Runtime\n",[59,11496,11497],{"class":61,"line":224},[59,11498,10544],{},[59,11500,11501],{"class":61,"line":233},[59,11502,11503],{},"    US->>UI: Enters details on sign-up page\n",[59,11505,11506],{"class":61,"line":241},[59,11507,11508],{},"    US->>UI: Clicks Sign-up\n",[59,11510,11511],{"class":61,"line":249},[59,11512,11513],{},"    UI->>+RT: POST \u002Faccount\u002Fregister\n",[59,11515,11516],{"class":61,"line":257},[59,11517,11518],{},"    RT->>DB: Create User\n",[59,11520,11521],{"class":61,"line":3137},[59,11522,11523],{},"    RT->>RT: Generate Email Verification Token\n",[59,11525,11526],{"class":61,"line":3150},[59,11527,11528],{},"    par Runtime to UserEmail\n",[59,11530,11531],{"class":61,"line":3163},[59,11532,11533],{},"        RT->>UE: Send email containing verification code\n",[59,11535,11536],{"class":61,"line":3176},[59,11537,11538],{},"    and Runtime to UI\n",[59,11540,11541],{"class":61,"line":3187},[59,11542,11543],{},"        RT-->>-UI: { status: 'okay' } and session created\n",[59,11545,11546],{"class":61,"line":3193},[59,11547,10785],{},[59,11549,11550],{"class":61,"line":3201},[59,11551,188],{"emptyLinePlaceholder":187},[59,11553,11554],{"class":61,"line":3214},[59,11555,11556],{},"    UI->>UI: Show 'Check your email' page\n",[59,11558,11559],{"class":61,"line":3222},[59,11560,11561],{},"    UE-->>US: Email received\n",[59,11563,11564],{"class":61,"line":3233},[59,11565,11566],{},"    US->>+UI: Enters verification code\n",[59,11568,11569],{"class":61,"line":3239},[59,11570,11571],{},"    US->>UI: Click \"Continue\" button\n",[59,11573,11574],{"class":61,"line":3247},[59,11575,11576],{},"    UI->>+RT: POST \u002Faccount\u002Fverify\u002Ftoken { token }\n",[59,11578,11579],{"class":61,"line":3256},[59,11580,11581],{},"    RT->>RT: Checks {token} is for the logged in user\n",[59,11583,11584],{"class":61,"line":3261},[59,11585,11586],{},"    RT->>DB: User.email_verified=true\n",[59,11588,11589],{"class":61,"line":3269},[59,11590,11591],{},"    loop for each pending invite\n",[59,11593,11594],{"class":61,"line":3278},[59,11595,11596],{},"        RT->>DB: Add user to team\n",[59,11598,11599],{"class":61,"line":3284},[59,11600,11601],{},"        RT->>DB: Delete invite\n",[59,11603,11604],{"class":61,"line":3289},[59,11605,11606],{},"        RT->>DB: Update audit log\n",[59,11608,11609],{"class":61,"line":3297},[59,11610,10785],{},[59,11612,11613],{"class":61,"line":3310},[59,11614,11615],{},"    RT-->>-US: Redirect '\u002F'\n",[316,11617,6631],{},{"title":55,"searchDepth":77,"depth":77,"links":11619},[],{},"User Sign up Flow","contribute\u002Fworkflows\u002Fsignup.md",{"title":11457,"description":55},"docs\u002Fcontribute\u002Fworkflows\u002Fsignup","Mgeq8u2pnJfqoGX-ppWUJaNEtMbi0HI0rpiMKDU_HYE",{"id":11627,"title":11628,"body":11629,"description":55,"extension":329,"layout":330,"meta":11799,"navGroup":330,"navOrder":330,"navTitle":11800,"navigation":187,"originalPath":11801,"path":10470,"redirect":330,"seo":11802,"stem":11803,"updated":337,"version":338,"__hash__":11804},"docs\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fteam-create.md","Sequence For Team Creation",{"type":7,"value":11630,"toc":11797},[11631,11634,11795],[10,11632,11628],{"id":11633},"sequence-for-team-creation",[50,11635,11637],{"className":6423,"code":11636,"language":6425,"meta":55,"style":55},"sequenceDiagram\nautonumber\nparticipant User\nparticipant Ui\nparticipant Runtime\nparticipant ContainerDriver\nparticipant DB\nparticipant Stripe\n\nUser->>Ui: Clicks Create Team\nUser->>Ui: Enters Team Name\nalt billing enabled\nUi->>Runtime: POST \u002Fapi\u002Fv1\u002Fteams\nRuntime->>DB: Create Team\nRuntime->>Stripe: checkout.create.session\nStripe->>Runtime: Session ID\nRuntime->>Ui: { billingURL: \"https:\u002F\u002Fstripe...\" }\nUi->>Stripe: Redirect\nUser->>Stripe: Enters Credit Card info\nalt complete\nStripe->>Ui: Redirect to Ui\nUi->>Ui: Show Team Overview\nStripe->>Runtime: POST \u002Fee\u002Fbilling\u002Fcallback\nRuntime->>DB: Create Subscription\nelse abort\nStripe->>Ui: Message \nUi->>Runtime: DELETE \u002Fapi\u002Fv1\u002Fteams\u002F{id}\nend\nelse: no billing\nUi->>Runtime: POST \u002Fteams\nRuntime->>DB: Create Team\nRuntime->>Ui: { status: \"okay\"}\nUi->>Ui: Show Team Overview\nend\n",[18,11638,11639,11643,11647,11651,11655,11659,11663,11667,11671,11675,11680,11685,11689,11694,11699,11704,11709,11714,11719,11724,11729,11734,11739,11744,11749,11754,11759,11764,11768,11773,11778,11782,11787,11791],{"__ignoreMap":55},[59,11640,11641],{"class":61,"line":62},[59,11642,10327],{},[59,11644,11645],{"class":61,"line":77},[59,11646,11047],{},[59,11648,11649],{"class":61,"line":88},[59,11650,11052],{},[59,11652,11653],{"class":61,"line":99},[59,11654,11057],{},[59,11656,11657],{"class":61,"line":156},[59,11658,11062],{},[59,11660,11661],{"class":61,"line":216},[59,11662,11067],{},[59,11664,11665],{"class":61,"line":224},[59,11666,11072],{},[59,11668,11669],{"class":61,"line":233},[59,11670,11077],{},[59,11672,11673],{"class":61,"line":241},[59,11674,188],{"emptyLinePlaceholder":187},[59,11676,11677],{"class":61,"line":249},[59,11678,11679],{},"User->>Ui: Clicks Create Team\n",[59,11681,11682],{"class":61,"line":257},[59,11683,11684],{},"User->>Ui: Enters Team Name\n",[59,11686,11687],{"class":61,"line":3137},[59,11688,11101],{},[59,11690,11691],{"class":61,"line":3150},[59,11692,11693],{},"Ui->>Runtime: POST \u002Fapi\u002Fv1\u002Fteams\n",[59,11695,11696],{"class":61,"line":3163},[59,11697,11698],{},"Runtime->>DB: Create Team\n",[59,11700,11701],{"class":61,"line":3176},[59,11702,11703],{},"Runtime->>Stripe: checkout.create.session\n",[59,11705,11706],{"class":61,"line":3187},[59,11707,11708],{},"Stripe->>Runtime: Session ID\n",[59,11710,11711],{"class":61,"line":3193},[59,11712,11713],{},"Runtime->>Ui: { billingURL: \"https:\u002F\u002Fstripe...\" }\n",[59,11715,11716],{"class":61,"line":3201},[59,11717,11718],{},"Ui->>Stripe: Redirect\n",[59,11720,11721],{"class":61,"line":3214},[59,11722,11723],{},"User->>Stripe: Enters Credit Card info\n",[59,11725,11726],{"class":61,"line":3222},[59,11727,11728],{},"alt complete\n",[59,11730,11731],{"class":61,"line":3233},[59,11732,11733],{},"Stripe->>Ui: Redirect to Ui\n",[59,11735,11736],{"class":61,"line":3239},[59,11737,11738],{},"Ui->>Ui: Show Team Overview\n",[59,11740,11741],{"class":61,"line":3247},[59,11742,11743],{},"Stripe->>Runtime: POST \u002Fee\u002Fbilling\u002Fcallback\n",[59,11745,11746],{"class":61,"line":3256},[59,11747,11748],{},"Runtime->>DB: Create Subscription\n",[59,11750,11751],{"class":61,"line":3261},[59,11752,11753],{},"else abort\n",[59,11755,11756],{"class":61,"line":3269},[59,11757,11758],{},"Stripe->>Ui: Message \n",[59,11760,11761],{"class":61,"line":3278},[59,11762,11763],{},"Ui->>Runtime: DELETE \u002Fapi\u002Fv1\u002Fteams\u002F{id}\n",[59,11765,11766],{"class":61,"line":3284},[59,11767,10756],{},[59,11769,11770],{"class":61,"line":3289},[59,11771,11772],{},"else: no billing\n",[59,11774,11775],{"class":61,"line":3297},[59,11776,11777],{},"Ui->>Runtime: POST \u002Fteams\n",[59,11779,11780],{"class":61,"line":3310},[59,11781,11698],{},[59,11783,11784],{"class":61,"line":3321},[59,11785,11786],{},"Runtime->>Ui: { status: \"okay\"}\n",[59,11788,11789],{"class":61,"line":3327},[59,11790,11738],{},[59,11792,11793],{"class":61,"line":3333},[59,11794,10756],{},[316,11796,6631],{},{"title":55,"searchDepth":77,"depth":77,"links":11798},[],{},"Team creation Flow","contribute\u002Fworkflows\u002Fteam-create.md",{"title":11628,"description":55},"docs\u002Fcontribute\u002Fworkflows\u002Fteam-create","2DWidZduGNYjjTrbfvUHC81IHrIxO12gcVFD8lkJnTs",{"id":11806,"title":11807,"body":11808,"description":11815,"extension":329,"layout":330,"meta":11847,"navGroup":4117,"navOrder":330,"navTitle":11848,"navigation":187,"originalPath":11849,"path":11850,"redirect":330,"seo":11851,"stem":11852,"updated":337,"version":338,"__hash__":11853},"docs\u002Fdocs\u002Fdebugging\u002Findex.md","Node-RED Safe Mode",{"type":7,"value":11809,"toc":11845},[11810,11813,11816,11836,11839],[10,11811,11807],{"id":11812},"node-red-safe-mode",[14,11814,11815],{},"When a Node-RED instance is unresponsive, for example due to an infinite loop,\nit can be put into Safe Mode.",[398,11817,11818,11825,11833],{},[31,11819,11820,11821],{},"Edit the instance's ",[41,11822,11824],{"href":11823},"\u002Fdocs\u002Fuser\u002Fenvvar.md","Environment Variables",[31,11826,11827,11828,470,11831,273],{},"Add a variable called ",[18,11829,11830],{},"NODE_RED_ENABLE_SAFE_MODE",[18,11832,3558],{},[31,11834,11835],{},"Save the changes then suspend\u002Frestart the instance.",[14,11837,11838],{},"When starting up in Safe Mode, Node-RED will provide access to the editor without\nstarting the flows. You can log in to the editor, make any necessary changes\nand then deploy to restart the flows.",[14,11840,11841,11842,11844],{},"Once recovered you should delete the ",[18,11843,11830],{}," environment variable\nto prevent it entering Safe Mode the next time it is restarted.",{"title":55,"searchDepth":77,"depth":77,"links":11846},[],{},"Debugging Node-RED issues","debugging\u002FREADME.md","\u002Fdocs\u002Fdebugging",{"title":11807,"description":11815},"docs\u002Fdebugging\u002Findex","iTmhuwqp0pcc7iS42TpD5H3Cv0Nu6_dOfXMayHM8Ymw",{"id":11855,"title":11856,"body":11857,"description":11864,"extension":329,"layout":330,"meta":12164,"navGroup":330,"navOrder":216,"navTitle":12165,"navigation":187,"originalPath":12166,"path":12167,"redirect":330,"seo":12168,"stem":12169,"updated":337,"version":338,"__hash__":12170},"docs\u002Fdocs\u002Fdevice-agent\u002Fdeploy.md","Deploying Flows to the Device Agent",{"type":7,"value":11858,"toc":12156},[11859,11862,11865,11890,11894,11897,11917,11920,11924,11927,11935,11939,11942,11951,11954,11957,11962,11974,11979,11986,11993,11996,11999,12019,12022,12027,12044,12048,12057,12061,12066,12070,12075,12078,12083,12086,12091,12098,12102],[10,11860,11856],{"id":11861},"deploying-flows-to-the-device-agent",[14,11863,11864],{},"Before you're able to deploy your flows to your Remote Instance,\nyou will have needed to have completed these steps:",[398,11866,11867,11874,11883],{},[31,11868,11869,11873],{},[41,11870,11872],{"href":11871},"\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Foverview","Install the Device Agent on the Device"," - installs Node-RED and other requirements in order to communicate with FlowFuse.",[31,11875,11876,11879,11880,11882],{},[41,11877,11878],{"href":43},"Register the Remote Instance with FlowFuse"," - this step will have provided you with a ",[18,11881,20],{}," file to move to your Remote Instance.",[31,11884,11885,11889],{},[41,11886,11888],{"href":11887},"\u002Fdocs\u002Fdevice-agent\u002Frunning","Run the Device Agent"," - starts the Device Agent on the Remote Instance.",[23,11891,11893],{"id":11892},"deploying-a-node-red-snapshot-to-the-remote-instance-from-a-hosted-instance","Deploying a Node-RED Snapshot to the Remote Instance From a Hosted Instance",[14,11895,11896],{},"To deploy a Node-RED Snapshot to the Remote Instance:",[398,11898,11899,11906],{},[31,11900,11901,11905],{},[41,11902,11904],{"href":11903},"\u002Fdocs\u002Fuser\u002Fsnapshots#create-a-snapshot","Create a snapshot"," - a point-in-time\nbackup of the Node-RED flows and configuration.",[31,11907,11908,11912,11913,11916],{},[41,11909,11911],{"href":11910},"\u002Fdocs\u002Fuser\u002Fsnapshots#setting-a-device-target-snapshot","Mark that snapshot"," as the ",[364,11914,11915],{},"Remote Instance Target"," snapshot.",[14,11918,11919],{},"This model allows you to develop your flows in FlowFuse and only push it out\nto the registered Remote Instances when you're happy with what you've created.",[23,11921,11923],{"id":11922},"starting-node-red-on-the-remote-instance-without-deploying-a-snapshot","Starting Node-RED on the Remote Instance without deploying a snapshot",[14,11925,11926],{},"A Remote Instance can be assigned to an application without a snapshot being deployed to it.",[14,11928,11929,11930,11934],{},"In this mode, the Remote Instance will start Node-RED with a default set of flows that can\nbe edited on the Remote Instance see ",[41,11931,11933],{"href":11932},"#editing-the-node-red-flows-on-a-remote-instance-that-is-assigned-to-an-application","Editing the Node-RED flows on a Remote Instance that is assigned to an application"," below",[23,11936,11938],{"id":11937},"editing-the-node-red-flows-on-a-remote-instance-that-is-assigned-to-an-instance","Editing the Node-RED flows on a Remote Instance that is assigned to an instance",[14,11940,11941],{},"When running in the default of Fleet Mode, the device agent does not allow local access to the\nNode-RED editor. This ensures the Remote Instance is running the deployed snapshot without modification.",[14,11943,11944,11945,11950],{},"When running on FlowFuse Cloud, or a premium licensed FlowFuse instance (with the\n",[41,11946,11949],{"href":11947,"rel":11948},"https:\u002F\u002Fflowfuse.com\u002Fdocs\u002Fcontribute\u002Flocal\u002F#setting-up-mosquitto-(optional)",[831],"MQTT broker enabled","\nthe Remote Instance can be placed in Developer Mode that enables remote access to the editor.",[14,11952,11953],{},"This can then be used to develop the flows directly on the Remote Instance and a new snapshot\ngenerated from the Remote Instance that can be deployed to other Remote Instances in the application.",[14,11955,11956],{},"Whilst in Developer Mode the Remote Instance will not receive new updates from the platform\nwhen new snapshots are deployed.",[14,11958,11959],{},[364,11960,11961],{},"Accessing the Editor",[398,11963,11964,11971],{},[31,11965,11966,11967,11970],{},"Once developer mode is enabled, click the ",[364,11968,11969],{},"Enable"," button next to the 'Editor Access' option",[31,11972,11973],{},"When the editor is available, the Editor button in the header will become active and will take you to the device editor.",[14,11975,11976],{},[364,11977,11978],{},"Creating a Remote Instance Snapshot",[14,11980,11981,11982,11985],{},"To create an instance snapshot from the Remote Instance use the ",[364,11983,11984],{},"Create Snapshot"," button\nin the Developer Mode options panel.",[14,11987,11988,11989,11992],{},"You will be prompted to give the snapshot a name and description. See ",[41,11990,11991],{"href":949},"Snapshots"," for more information\nabout working with snapshots.",[23,11994,11933],{"id":11995},"editing-the-node-red-flows-on-a-remote-instance-that-is-assigned-to-an-application",[14,11997,11998],{},"Access to the editor is only available when:",[28,12000,12001,12004,12009],{},[31,12002,12003],{},"The Remote Instance is in Developer Mode",[31,12005,11944,12006,11950],{},[41,12007,11949],{"href":11947,"rel":12008},[831],[31,12010,12011,12012,12016],{},"Local access to the editor can be enabled by defining a Username & Password in the Device\nSettings -> Security and enabling \"Allow offline access\"\n",[638,12013],{"alt":12014,"dataZoomable":55,"src":12015},"Device Allow Offline Access Settings","\u002Fdocs\u002Fdevice-agent\u002Fimages\u002Fdevice-local-access.png",[1160,12017,12018],{},"Device Allow Offline Access Srttings",[14,12020,12021],{},"Whilst in Developer Mode the Remote Instance will not receive new updates from the platform.",[14,12023,12024],{},[364,12025,12026],{},"Enabling Developer Mode",[398,12028,12029,12035,12038,12041],{},[31,12030,12031,12032,4576],{},"Go to your team's ",[364,12033,12034],{},"Remote Instances",[31,12036,12037],{},"Select the Remote Instance you want to edit by clicking its name.",[31,12039,12040],{},"Click the \"Developer Mode\" button to enable developer mode.",[31,12042,12043],{},"Once enabled, Developer Mode options are available under the tab labelled \"Developer Mode\" on the Remote Instance page.",[14,12045,12046],{},[364,12047,11961],{},[398,12049,12050,12054],{},[31,12051,11966,12052,11970],{},[364,12053,11969],{},[31,12055,12056],{},"When the editor is available, the Editor button in the header will become active and will take you to the Remote Instance editor.",[14,12058,12059],{},[364,12060,11978],{},[14,12062,12063,12064,11985],{},"To create a snapshot from an application owned Remote Instance use the ",[364,12065,11984],{},[14,12067,11988,12068,11992],{},[41,12069,11991],{"href":949},[14,12071,12072],{},[364,12073,12074],{},"Auto Remote Instance Snapshots",[14,12076,12077],{},"For Remote Instances that are assigned to an application, the platform will automatically create a snapshot of the Remote Instance\nwhen it detects flows modified. This snapshot will be created with the name \"Auto Snapshot - yyyy-mm-dd hh:mm-ss\".\nOnly the last 10 auto snapshots are kept, others are deleted on a first in first out basis.",[14,12079,12080],{},[364,12081,12082],{},"Custom Node Catalogues",[14,12084,12085],{},"For Remote Instances that want to make use of custom node catalogues, these can be configured\nunder the Remote Instance settings page on the Palette tab",[14,12087,12088],{},[364,12089,12090],{},".npmrc file",[14,12092,12093,12094,12097],{},"Likewise for Remote Instances that need to be provided with a custom ",[18,12095,12096],{},".npmrc"," file to allow access\nto a custom npm registry or to provide an access token this can also be set on the Remote Instance\nsettings Palette tab",[104,12099,12101],{"id":12100},"important-notes","Important Notes",[28,12103,12104,12107,12110,12113,12116,12119,12130,12138,12141,12144,12147,12150,12153],{},[31,12105,12106],{},"Remote access to the editor requires Device Agent v0.8.0 or later.",[31,12108,12109],{},"The Web UI requires Device Agent v0.9.0 or later.",[31,12111,12112],{},"Assigning a Remote Instance to an application requires Device Agent v1.11.0 and FlowFuse v1.11.0 or later.",[31,12114,12115],{},"Snapshots of Remote Instances assigned to an application are supported in FlowFuse V1.12.0 or later.",[31,12117,12118],{},"Deploying a snapshot from a different Hosted Instance or Remote Instance to an application owned Remote Instance is supported in FlowFuse V1.13.0 or later.",[31,12120,12121,12122],{},"When a Remote Instance is assigned to a Hosted Instance:\n",[28,12123,12124,12127],{},[31,12125,12126],{},"It must first have a snapshot applied before editor access is possible.",[31,12128,12129],{},"Disabling Developer Mode and returning to Fleet Mode will cause the Remote Instance to check in with the platform.\nIf the Remote Instance flows have changed, it will be reloaded with the current target snapshot assigned to that Remote Instance,\ncausing any changes made in Developer Mode to be overwritten. Therefore, it is recommended to create a snapshot\nof the changes before disabling Developer Mode.",[31,12131,12132,12133],{},"When a Remote Instance is assigned to an application:\n",[28,12134,12135],{},[31,12136,12137],{},"It will start with a set of default flows.",[31,12139,12140],{},"The Remote Instance will not receive any updates from the platform while in Developer Mode.",[31,12142,12143],{},"The Remote Instance must be online and connected to the platform to enable \"Editor Access\".",[31,12145,12146],{},"To minimise server and Remote Instance resources, it is recommended to disable \"Editor Access\" when not actively developing flows on a Remote Instance.",[31,12148,12149],{},"Auto snapshots were introduced in FlowFuse V2.1.",[31,12151,12152],{},"Auto snapshots are only supported for Remote Instance assigned to an application.",[31,12154,12155],{},"If an auto snapshot is set as the target snapshot for a Remote Instance or assigned to a pipeline stage, it will not be auto cleaned up meaning it is possible to have more than 10 auto snapshots.",{"title":55,"searchDepth":77,"depth":77,"links":12157},[12158,12159,12160,12161],{"id":11892,"depth":77,"text":11893},{"id":11922,"depth":77,"text":11923},{"id":11937,"depth":77,"text":11938},{"id":11995,"depth":77,"text":11933,"children":12162},[12163],{"id":12100,"depth":88,"text":12101},{},"Deploying your Flows","device-agent\u002Fdeploy.md","\u002Fdocs\u002Fdevice-agent\u002Fdeploy",{"title":11856,"description":11864},"docs\u002Fdevice-agent\u002Fdeploy","xreKIGksg1Potkkqy4a0MTn-0MIGzuCuykBVE7b-6EQ",{"id":12172,"title":55,"body":12173,"description":55,"extension":329,"layout":532,"meta":12177,"navGroup":12179,"navOrder":62,"navTitle":12179,"navigation":187,"originalPath":12180,"path":12181,"redirect":12182,"seo":12184,"stem":12185,"updated":337,"version":338,"__hash__":12186},"docs\u002Fdocs\u002Fdevice-agent\u002Findex.md",{"type":7,"value":12174,"toc":12175},[],{"title":55,"searchDepth":77,"depth":77,"links":12176},[],{"tags":12178},[4066],"Device Agent","device-agent\u002FREADME.md","\u002Fdocs\u002Fdevice-agent",{"to":12183},"\u002Fdocs\u002Fdevice-agent\u002Fintroduction",{"description":55},"docs\u002Fdevice-agent\u002Findex","St2AY3OTWrnmcFA1yvi-NCFASW4RjYYATYYJD42L56c",{"id":12188,"title":12189,"body":12190,"description":55,"extension":329,"layout":330,"meta":13314,"navGroup":332,"navOrder":77,"navTitle":13315,"navigation":187,"originalPath":13316,"path":13317,"redirect":330,"seo":13318,"stem":13319,"updated":337,"version":338,"__hash__":13320},"docs\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Fdevice-agent-installer.md","FlowFuse Device Agent Installer",{"type":7,"value":12191,"toc":13291},[12192,12195,12199,12202,12205,12209,12220,12224,12230,12234,12238,12245,12247,12250,12254,12258,12281,12284,12299,12303,12307,12332,12341,12361,12365,12369,12379,12382,12391,12400,12409,12413,12420,12423,12438,12447,12484,12488,12502,12505,12520,12529,12542,12546,12550,12557,12575,12581,12584,12591,12609,12618,12622,12626,12636,12640,12707,12711,12773,12777,12837,12841,12907,12911,12949,12953,12956,12959,12983,12986,13010,13013,13036,13039,13063,13066,13088,13092,13095,13099,13115,13119,13137,13140,13150,13154,13162,13166,13172,13180,13184,13187,13196,13214,13226,13235,13238,13242,13249,13288],[10,12193,12189],{"id":12194},"flowfuse-device-agent-installer",[23,12196,12198],{"id":12197},"what-is-the-flowfuse-device-agent-installer","What is the FlowFuse Device Agent Installer?",[14,12200,12201],{},"The FlowFuse Device Agent Installer is a CLI tool for the FlowFuse Device Agent that automatically sets up Node.js runtime, installs the device agent package, and configures it as a system service.\nAdditionally, it provides a simple interface for keeping the device agent up to date.",[14,12203,12204],{},"The FlowFuse Device Agent Installer is the easiest way to get the FlowFuse Device Agent up and running on your remote device.",[23,12206,12208],{"id":12207},"requirements","Requirements",[28,12210,12211,12214,12217],{},[31,12212,12213],{},"Linux, macOS, or Windows",[31,12215,12216],{},"Internet connection for downloading dependencies",[31,12218,12219],{},"Administrator\u002Froot privileges for system service installation",[104,12221,12223],{"id":12222},"networking-requirements","Networking requirements",[14,12225,12226,12227,273],{},"Please see ",[41,12228,12223],{"href":12229},"\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Foverview#networking-requirements",[23,12231,12233],{"id":12232},"use-the-flowfuse-device-agent-installer-to-install-or-update-the-flowfuse-device-agent","Use the FlowFuse Device Agent Installer to install or update the FlowFuse Device Agent",[104,12235,12237],{"id":12236},"one-line-install","One-line install",[14,12239,12240,12241,273],{},"For the fastest one-line install experience, see the ",[41,12242,12244],{"href":12243},"\u002Fdocs\u002Fdevice-agent\u002Fquickstart.md","Quick Start guide",[104,12246,9558],{"id":9557},[14,12248,12249],{},"If you prefer to install the FlowFuse Device Agent manually with the Installer, you can follow these steps:",[768,12251,12253],{"id":12252},"_1-download-the-installer-script","1. Download the installer script:",[1146,12255,12257],{"id":12256},"linuxmacos","Linux\u002FmacOS",[50,12259,12261],{"className":52,"code":12260,"language":54,"meta":55,"style":55},"\u002Fbin\u002Fbash -c \"$(curl -fsSL https:\u002F\u002Fflowfuse.github.io\u002Fdevice-agent\u002Fget.sh)\"\n",[18,12262,12263],{"__ignoreMap":55},[59,12264,12265,12268,12270,12273,12275,12278],{"class":61,"line":62},[59,12266,12267],{"class":65},"\u002Fbin\u002Fbash",[59,12269,9670],{"class":73},[59,12271,12272],{"class":69}," \"$(",[59,12274,1381],{"class":65},[59,12276,12277],{"class":73}," -fsSL",[59,12279,12280],{"class":69}," https:\u002F\u002Fflowfuse.github.io\u002Fdevice-agent\u002Fget.sh)\"\n",[1146,12282,9136],{"id":12283},"windows",[50,12285,12287],{"className":52,"code":12286,"language":54,"meta":55,"style":55},"powershell -c \"irm https:\u002F\u002Fflowfuse.github.io\u002Fdevice-agent\u002Fget.ps1|iex\"\n",[18,12288,12289],{"__ignoreMap":55},[59,12290,12291,12294,12296],{"class":61,"line":62},[59,12292,12293],{"class":65},"powershell",[59,12295,9670],{"class":73},[59,12297,12298],{"class":69}," \"irm https:\u002F\u002Fflowfuse.github.io\u002Fdevice-agent\u002Fget.ps1|iex\"\n",[768,12300,12302],{"id":12301},"_2-install-the-device-agent-using-one-time-code","2. Install the Device Agent using One Time Code",[1146,12304,12306],{"id":12305},"linuxmacos-1","Linux\u002FMacOS",[50,12308,12310],{"className":52,"code":12309,"language":54,"meta":55,"style":55},".\u002Fflowfuse-device-agent-installer --otc \u003Cone-time-code>\n",[18,12311,12312],{"__ignoreMap":55},[59,12313,12314,12317,12320,12323,12326,12329],{"class":61,"line":62},[59,12315,12316],{"class":65},".\u002Fflowfuse-device-agent-installer",[59,12318,12319],{"class":73}," --otc",[59,12321,12322],{"class":1372}," \u003C",[59,12324,12325],{"class":69},"one-time-cod",[59,12327,12328],{"class":178},"e",[59,12330,12331],{"class":1372},">\n",[1146,12333,12335,12336,660],{"id":12334},"windows-run-elevated1","Windows (run elevated",[8351,12337,12338],{},[41,12339,8359],{"href":8355,"ariaDescribedBy":12340,"dataFootnoteRef":55,"id":8358},[8357],[50,12342,12344],{"className":52,"code":12343,"language":54,"meta":55,"style":55},"flowfuse-device-agent-installer.exe --otc \u003Cone-time-code>\n",[18,12345,12346],{"__ignoreMap":55},[59,12347,12348,12351,12353,12355,12357,12359],{"class":61,"line":62},[59,12349,12350],{"class":65},"flowfuse-device-agent-installer.exe",[59,12352,12319],{"class":73},[59,12354,12322],{"class":1372},[59,12356,12325],{"class":69},[59,12358,12328],{"class":178},[59,12360,12331],{"class":1372},[104,12362,12364],{"id":12363},"other-installation-options","Other installation options",[768,12366,12368],{"id":12367},"install-without-one-time-code","Install without One Time Code",[14,12370,12371,12372,12374,12375,12378],{},"You can also install the FlowFuse Device Agent without a One Time Code by providing the ",[18,12373,20],{}," content during interactive installation. To do this, run the installer without the ",[18,12376,12377],{},"--otc"," flag.",[1146,12380,12306],{"id":12381},"linuxmacos-2",[50,12383,12385],{"className":52,"code":12384,"language":54,"meta":55,"style":55},".\u002Fflowfuse-device-agent-installer\n",[18,12386,12387],{"__ignoreMap":55},[59,12388,12389],{"class":61,"line":62},[59,12390,12384],{"class":65},[1146,12392,12335,12394,660],{"id":12393},"windows-run-elevated1-1",[8351,12395,12396],{},[41,12397,8359],{"href":8355,"ariaDescribedBy":12398,"dataFootnoteRef":55,"id":12399},[8357],"user-content-fnref-1-2",[50,12401,12403],{"className":52,"code":12402,"language":54,"meta":55,"style":55},"flowfuse-device-agent-installer.exe\n",[18,12404,12405],{"__ignoreMap":55},[59,12406,12407],{"class":61,"line":62},[59,12408,12402],{"class":65},[768,12410,12412],{"id":12411},"install-in-custom-directory","Install in custom directory",[14,12414,12415,12416,12419],{},"There is a possibility to install the Device Agent in a custom directory by using the ",[18,12417,12418],{},"--dir"," option. For example:",[1146,12421,12306],{"id":12422},"linuxmacos-3",[50,12424,12426],{"className":52,"code":12425,"language":54,"meta":55,"style":55},".\u002Fflowfuse-device-agent-installer --dir \u002Fpath\u002Fto\u002Fcustom\u002Fdir\n",[18,12427,12428],{"__ignoreMap":55},[59,12429,12430,12432,12435],{"class":61,"line":62},[59,12431,12316],{"class":65},[59,12433,12434],{"class":73}," --dir",[59,12436,12437],{"class":69}," \u002Fpath\u002Fto\u002Fcustom\u002Fdir\n",[1146,12439,12335,12441,660],{"id":12440},"windows-run-elevated1-2",[8351,12442,12443],{},[41,12444,8359],{"href":8355,"ariaDescribedBy":12445,"dataFootnoteRef":55,"id":12446},[8357],"user-content-fnref-1-3",[50,12448,12450],{"className":52,"code":12449,"language":54,"meta":55,"style":55},"flowfuse-device-agent-installer.exe --dir C:\\path\\to\\custom\\dir\n",[18,12451,12452],{"__ignoreMap":55},[59,12453,12454,12456,12458,12461,12464,12467,12469,12472,12475,12478,12481],{"class":61,"line":62},[59,12455,12350],{"class":65},[59,12457,12434],{"class":73},[59,12459,12460],{"class":69}," C:",[59,12462,12463],{"class":73},"\\p",[59,12465,12466],{"class":69},"ath",[59,12468,9317],{"class":73},[59,12470,12471],{"class":69},"o",[59,12473,12474],{"class":73},"\\c",[59,12476,12477],{"class":69},"ustom",[59,12479,12480],{"class":73},"\\d",[59,12482,12483],{"class":69},"ir\n",[768,12485,12487],{"id":12486},"install-on-custom-port","Install on custom port",[14,12489,12490,12491,12494,12495,12498,12499,273],{},"You can configure a custom port using the ",[18,12492,12493],{},"--port"," flag. The service name includes the port, for example ",[18,12496,12497],{},"flowfuse-device-agent-1882"," for ",[18,12500,12501],{},"--port 1882",[1146,12503,12306],{"id":12504},"linuxmacos-4",[50,12506,12508],{"className":52,"code":12507,"language":54,"meta":55,"style":55},".\u002Fflowfuse-device-agent-installer --port 1882\n",[18,12509,12510],{"__ignoreMap":55},[59,12511,12512,12514,12517],{"class":61,"line":62},[59,12513,12316],{"class":65},[59,12515,12516],{"class":73}," --port",[59,12518,12519],{"class":73}," 1882\n",[1146,12521,12335,12523,660],{"id":12522},"windows-run-elevated1-3",[8351,12524,12525],{},[41,12526,8359],{"href":8355,"ariaDescribedBy":12527,"dataFootnoteRef":55,"id":12528},[8357],"user-content-fnref-1-4",[50,12530,12532],{"className":52,"code":12531,"language":54,"meta":55,"style":55},"flowfuse-device-agent-installer.exe --port 1882\n",[18,12533,12534],{"__ignoreMap":55},[59,12535,12536,12538,12540],{"class":61,"line":62},[59,12537,12350],{"class":65},[59,12539,12516],{"class":73},[59,12541,12519],{"class":73},[23,12543,12545],{"id":12544},"updating-components","Updating components",[104,12547,12549],{"id":12548},"nodejs-runtime","Node.js runtime",[14,12551,12552,12553,12556],{},"To update bundled Node.js runtime, specify the ",[18,12554,12555],{},"--update-nodejs"," flag with the desired version:",[50,12558,12560],{"className":52,"code":12559,"language":54,"meta":55,"style":55},".\u002Fflowfuse-device-agent-installer --update-nodejs --nodejs-version 20.19.1\n",[18,12561,12562],{"__ignoreMap":55},[59,12563,12564,12566,12569,12572],{"class":61,"line":62},[59,12565,12316],{"class":65},[59,12567,12568],{"class":73}," --update-nodejs",[59,12570,12571],{"class":73}," --nodejs-version",[59,12573,12574],{"class":73}," 20.19.1\n",[14,12576,12577,12578,12580],{},"Specifying ",[18,12579,12555],{}," without a version will pick the default version defined in the installer.",[104,12582,12179],{"id":12583},"device-agent",[14,12585,12586,12587,12590],{},"To update the Device Agent package, use the ",[18,12588,12589],{},"--update-agent"," flag, optionally specifying the version:",[50,12592,12594],{"className":52,"code":12593,"language":54,"meta":55,"style":55},".\u002Fflowfuse-device-agent-installer --update-agent --agent-version 3.3.2\n",[18,12595,12596],{"__ignoreMap":55},[59,12597,12598,12600,12603,12606],{"class":61,"line":62},[59,12599,12316],{"class":65},[59,12601,12602],{"class":73}," --update-agent",[59,12604,12605],{"class":73}," --agent-version",[59,12607,12608],{"class":73}," 3.3.2\n",[14,12610,12577,12611,12613,12614,12617],{},[18,12612,12589],{}," without the ",[18,12615,12616],{},"--agent-version"," flag will update to the latest available version.",[23,12619,12621],{"id":12620},"troubleshooting","Troubleshooting",[104,12623,12625],{"id":12624},"managing-the-device-agent-service","Managing the Device Agent service",[14,12627,12628,12629,12632,12633,273],{},"Services are named per-port, for example ",[18,12630,12631],{},"flowfuse-device-agent-1880",". On macOS, the launchd label is ",[18,12634,12635],{},"com.flowfuse.device-agent-1880",[768,12637,12639],{"id":12638},"linux-systemd","Linux (systemd)",[50,12641,12643],{"className":52,"code":12642,"language":54,"meta":55,"style":55},"sudo systemctl start flowfuse-device-agent-\u003Cport>\nsudo systemctl stop flowfuse-device-agent-\u003Cport>\nsudo systemctl restart flowfuse-device-agent-\u003Cport>\n",[18,12644,12645,12669,12688],{"__ignoreMap":55},[59,12646,12647,12649,12652,12655,12658,12661,12664,12667],{"class":61,"line":62},[59,12648,9188],{"class":65},[59,12650,12651],{"class":69}," systemctl",[59,12653,12654],{"class":69}," start",[59,12656,12657],{"class":69}," flowfuse-device-agent-",[59,12659,12660],{"class":1372},"\u003C",[59,12662,12663],{"class":69},"por",[59,12665,12666],{"class":178},"t",[59,12668,12331],{"class":1372},[59,12670,12671,12673,12675,12678,12680,12682,12684,12686],{"class":61,"line":77},[59,12672,9188],{"class":65},[59,12674,12651],{"class":69},[59,12676,12677],{"class":69}," stop",[59,12679,12657],{"class":69},[59,12681,12660],{"class":1372},[59,12683,12663],{"class":69},[59,12685,12666],{"class":178},[59,12687,12331],{"class":1372},[59,12689,12690,12692,12694,12697,12699,12701,12703,12705],{"class":61,"line":88},[59,12691,9188],{"class":65},[59,12693,12651],{"class":69},[59,12695,12696],{"class":69}," restart",[59,12698,12657],{"class":69},[59,12700,12660],{"class":1372},[59,12702,12663],{"class":69},[59,12704,12666],{"class":178},[59,12706,12331],{"class":1372},[768,12708,12710],{"id":12709},"linux-sysvinit","Linux (SysVinit)",[50,12712,12714],{"className":52,"code":12713,"language":54,"meta":55,"style":55},"sudo service flowfuse-device-agent-\u003Cport> start\nsudo service flowfuse-device-agent-\u003Cport> stop\nsudo service flowfuse-device-agent-\u003Cport> restart\n",[18,12715,12716,12735,12754],{"__ignoreMap":55},[59,12717,12718,12720,12722,12724,12726,12728,12730,12733],{"class":61,"line":62},[59,12719,9188],{"class":65},[59,12721,9801],{"class":69},[59,12723,12657],{"class":69},[59,12725,12660],{"class":1372},[59,12727,12663],{"class":69},[59,12729,12666],{"class":178},[59,12731,12732],{"class":1372},">",[59,12734,9510],{"class":69},[59,12736,12737,12739,12741,12743,12745,12747,12749,12751],{"class":61,"line":77},[59,12738,9188],{"class":65},[59,12740,9801],{"class":69},[59,12742,12657],{"class":69},[59,12744,12660],{"class":1372},[59,12746,12663],{"class":69},[59,12748,12666],{"class":178},[59,12750,12732],{"class":1372},[59,12752,12753],{"class":69}," stop\n",[59,12755,12756,12758,12760,12762,12764,12766,12768,12770],{"class":61,"line":88},[59,12757,9188],{"class":65},[59,12759,9801],{"class":69},[59,12761,12657],{"class":69},[59,12763,12660],{"class":1372},[59,12765,12663],{"class":69},[59,12767,12666],{"class":178},[59,12769,12732],{"class":1372},[59,12771,12772],{"class":69}," restart\n",[768,12774,12776],{"id":12775},"linux-openrc","Linux (OpenRC)",[50,12778,12780],{"className":52,"code":12779,"language":54,"meta":55,"style":55},"sudo rc-service flowfuse-device-agent-\u003Cport> start\nsudo rc-service flowfuse-device-agent-\u003Cport> stop\nsudo rc-service flowfuse-device-agent-\u003Cport> restart\n",[18,12781,12782,12801,12819],{"__ignoreMap":55},[59,12783,12784,12786,12789,12791,12793,12795,12797,12799],{"class":61,"line":62},[59,12785,9188],{"class":65},[59,12787,12788],{"class":69}," rc-service",[59,12790,12657],{"class":69},[59,12792,12660],{"class":1372},[59,12794,12663],{"class":69},[59,12796,12666],{"class":178},[59,12798,12732],{"class":1372},[59,12800,9510],{"class":69},[59,12802,12803,12805,12807,12809,12811,12813,12815,12817],{"class":61,"line":77},[59,12804,9188],{"class":65},[59,12806,12788],{"class":69},[59,12808,12657],{"class":69},[59,12810,12660],{"class":1372},[59,12812,12663],{"class":69},[59,12814,12666],{"class":178},[59,12816,12732],{"class":1372},[59,12818,12753],{"class":69},[59,12820,12821,12823,12825,12827,12829,12831,12833,12835],{"class":61,"line":88},[59,12822,9188],{"class":65},[59,12824,12788],{"class":69},[59,12826,12657],{"class":69},[59,12828,12660],{"class":1372},[59,12830,12663],{"class":69},[59,12832,12666],{"class":178},[59,12834,12732],{"class":1372},[59,12836,12772],{"class":69},[768,12838,12840],{"id":12839},"macos-launchd","macOS (launchd)",[50,12842,12844],{"className":52,"code":12843,"language":54,"meta":55,"style":55},"sudo launchctl start com.flowfuse.device-agent-\u003Cport>\nsudo launchctl stop com.flowfuse.device-agent-\u003Cport>\nsudo launchctl kickstart -k system\u002Fcom.flowfuse.device-agent-\u003Cport>\n",[18,12845,12846,12866,12884],{"__ignoreMap":55},[59,12847,12848,12850,12853,12855,12858,12860,12862,12864],{"class":61,"line":62},[59,12849,9188],{"class":65},[59,12851,12852],{"class":69}," launchctl",[59,12854,12654],{"class":69},[59,12856,12857],{"class":69}," com.flowfuse.device-agent-",[59,12859,12660],{"class":1372},[59,12861,12663],{"class":69},[59,12863,12666],{"class":178},[59,12865,12331],{"class":1372},[59,12867,12868,12870,12872,12874,12876,12878,12880,12882],{"class":61,"line":77},[59,12869,9188],{"class":65},[59,12871,12852],{"class":69},[59,12873,12677],{"class":69},[59,12875,12857],{"class":69},[59,12877,12660],{"class":1372},[59,12879,12663],{"class":69},[59,12881,12666],{"class":178},[59,12883,12331],{"class":1372},[59,12885,12886,12888,12890,12893,12896,12899,12901,12903,12905],{"class":61,"line":88},[59,12887,9188],{"class":65},[59,12889,12852],{"class":69},[59,12891,12892],{"class":69}," kickstart",[59,12894,12895],{"class":73}," -k",[59,12897,12898],{"class":69}," system\u002Fcom.flowfuse.device-agent-",[59,12900,12660],{"class":1372},[59,12902,12663],{"class":69},[59,12904,12666],{"class":178},[59,12906,12331],{"class":1372},[768,12908,12910],{"id":12909},"windows-service-control","Windows (Service Control)",[50,12912,12914],{"className":52,"code":12913,"language":54,"meta":55,"style":55},"sc.exe start flowfuse-device-agent-\u003Cport>\nsc.exe stop flowfuse-device-agent-\u003Cport>\n",[18,12915,12916,12933],{"__ignoreMap":55},[59,12917,12918,12921,12923,12925,12927,12929,12931],{"class":61,"line":62},[59,12919,12920],{"class":65},"sc.exe",[59,12922,12654],{"class":69},[59,12924,12657],{"class":69},[59,12926,12660],{"class":1372},[59,12928,12663],{"class":69},[59,12930,12666],{"class":178},[59,12932,12331],{"class":1372},[59,12934,12935,12937,12939,12941,12943,12945,12947],{"class":61,"line":77},[59,12936,12920],{"class":65},[59,12938,12677],{"class":69},[59,12940,12657],{"class":69},[59,12942,12660],{"class":1372},[59,12944,12663],{"class":69},[59,12946,12666],{"class":178},[59,12948,12331],{"class":1372},[104,12950,12952],{"id":12951},"check-the-device-agent-service-status","Check the Device Agent service status",[14,12954,12955],{},"You can check the status of the Device Agent service to verify if it is running correctly or to diagnose any issues.\nThe status command provides information about the current state of the service, including whether it is active, inactive, or failed.",[768,12957,12639],{"id":12958},"linux-systemd-1",[50,12960,12962],{"className":52,"code":12961,"language":54,"meta":55,"style":55},"sudo systemctl status flowfuse-device-agent-\u003Cport>\n",[18,12963,12964],{"__ignoreMap":55},[59,12965,12966,12968,12970,12973,12975,12977,12979,12981],{"class":61,"line":62},[59,12967,9188],{"class":65},[59,12969,12651],{"class":69},[59,12971,12972],{"class":69}," status",[59,12974,12657],{"class":69},[59,12976,12660],{"class":1372},[59,12978,12663],{"class":69},[59,12980,12666],{"class":178},[59,12982,12331],{"class":1372},[768,12984,12710],{"id":12985},"linux-sysvinit-1",[50,12987,12989],{"className":52,"code":12988,"language":54,"meta":55,"style":55},"sudo service flowfuse-device-agent-\u003Cport> status\n",[18,12990,12991],{"__ignoreMap":55},[59,12992,12993,12995,12997,12999,13001,13003,13005,13007],{"class":61,"line":62},[59,12994,9188],{"class":65},[59,12996,9801],{"class":69},[59,12998,12657],{"class":69},[59,13000,12660],{"class":1372},[59,13002,12663],{"class":69},[59,13004,12666],{"class":178},[59,13006,12732],{"class":1372},[59,13008,13009],{"class":69}," status\n",[768,13011,12776],{"id":13012},"linux-openrc-1",[50,13014,13016],{"className":52,"code":13015,"language":54,"meta":55,"style":55},"sudo rc-service flowfuse-device-agent-\u003Cport> status\n",[18,13017,13018],{"__ignoreMap":55},[59,13019,13020,13022,13024,13026,13028,13030,13032,13034],{"class":61,"line":62},[59,13021,9188],{"class":65},[59,13023,12788],{"class":69},[59,13025,12657],{"class":69},[59,13027,12660],{"class":1372},[59,13029,12663],{"class":69},[59,13031,12666],{"class":178},[59,13033,12732],{"class":1372},[59,13035,13009],{"class":69},[768,13037,12840],{"id":13038},"macos-launchd-1",[50,13040,13042],{"className":52,"code":13041,"language":54,"meta":55,"style":55},"sudo launchctl print system\u002Fcom.flowfuse.device-agent-\u003Cport>\n",[18,13043,13044],{"__ignoreMap":55},[59,13045,13046,13048,13050,13053,13055,13057,13059,13061],{"class":61,"line":62},[59,13047,9188],{"class":65},[59,13049,12852],{"class":69},[59,13051,13052],{"class":69}," print",[59,13054,12898],{"class":69},[59,13056,12660],{"class":1372},[59,13058,12663],{"class":69},[59,13060,12666],{"class":178},[59,13062,12331],{"class":1372},[768,13064,12910],{"id":13065},"windows-service-control-1",[50,13067,13069],{"className":52,"code":13068,"language":54,"meta":55,"style":55},"sc.exe query flowfuse-device-agent-\u003Cport>\n",[18,13070,13071],{"__ignoreMap":55},[59,13072,13073,13075,13078,13080,13082,13084,13086],{"class":61,"line":62},[59,13074,12920],{"class":65},[59,13076,13077],{"class":69}," query",[59,13079,12657],{"class":69},[59,13081,12660],{"class":1372},[59,13083,12663],{"class":69},[59,13085,12666],{"class":178},[59,13087,12331],{"class":1372},[104,13089,13091],{"id":13090},"viewing-device-agent-log-files","Viewing Device Agent log files",[14,13093,13094],{},"Adjust the path if custom directory has been specified during installation.",[768,13096,13098],{"id":13097},"linuxmacos-5","Linux\u002FmacOS:",[50,13100,13102],{"className":52,"code":13101,"language":54,"meta":55,"style":55},"tail -f \u002Fopt\u002Fflowfuse-device\u002Flogs\u002Fflowfuse-device-agent.log\n",[18,13103,13104],{"__ignoreMap":55},[59,13105,13106,13109,13112],{"class":61,"line":62},[59,13107,13108],{"class":65},"tail",[59,13110,13111],{"class":73}," -f",[59,13113,13114],{"class":69}," \u002Fopt\u002Fflowfuse-device\u002Flogs\u002Fflowfuse-device-agent.log\n",[768,13116,13118],{"id":13117},"linux-systemd-2","Linux (systemd):",[50,13120,13122],{"className":52,"code":13121,"language":54,"meta":55,"style":55},"journalctl -f -u 'flowfuse-device-agent-\u003Cport>'\n",[18,13123,13124],{"__ignoreMap":55},[59,13125,13126,13129,13131,13134],{"class":61,"line":62},[59,13127,13128],{"class":65},"journalctl",[59,13130,13111],{"class":73},[59,13132,13133],{"class":73}," -u",[59,13135,13136],{"class":69}," 'flowfuse-device-agent-\u003Cport>'\n",[768,13138,9478],{"id":13139},"windows-1",[50,13141,13144],{"className":13142,"code":13143,"language":12293,"meta":55,"style":55},"language-powershell shiki shiki-themes github-light github-dark","Get-Content -Path 'C:\\opt\\flowfuse-device\\flowfuse-device-agent.log' -Wait\n",[18,13145,13146],{"__ignoreMap":55},[59,13147,13148],{"class":61,"line":62},[59,13149,13143],{},[104,13151,13153],{"id":13152},"error-disk-space-check-failed","Error: Disk space check failed",[1110,13155,13156],{},[14,13157,13158,13161],{},[59,13159,13160],{},"ERROR"," Disk space check failed: insufficient disk space in temporary directory (\u002Ftmp): need at least 500.0 MB, available 490.4 MB",[1146,13163,13165],{"id":13164},"cause","Cause:",[14,13167,372,13168,13171],{},[18,13169,13170],{},"Disk space check failed"," error indicates that the installer has detected insufficient disk space in the temporary directory.\nThe FlowFuse Device Agent Installer requires a minimum of 500MB of free disk space in the temporary directory to ensure proper installation.",[14,13173,13174,13175,13179],{},"This error might also appear if there is not enough space on the disk partition where the Device Agent is being installed.\nMake sure that the target installation directory has at least 500MB of free space available.\n",[41,13176,13178],{"href":13177},"\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Fdevice-agent-installer\u002F#install-in-custom-directory","Adjust installation directory"," accordingly.",[1146,13181,13183],{"id":13182},"solution","Solution:",[14,13185,13186],{},"To fix this issue, you can try to free up some disk space by deleting unnecessary files or moving them to another location.\nAlternatively, you can specify a different temporary directory with sufficient space by setting proper environmental variable before running the installer.",[14,13188,13189,13192,13193,114],{},[364,13190,13191],{},"On Linux\u002FmacOS",", set the ",[18,13194,13195],{},"TMPDIR",[50,13197,13199],{"className":52,"code":13198,"language":54,"meta":55,"style":55},"export TMPDIR=\u002Fpath\u002Fto\u002Fexisting\u002Fdirectory\u002Fwith\u002Fsufficient\u002Fspace\n",[18,13200,13201],{"__ignoreMap":55},[59,13202,13203,13206,13209,13211],{"class":61,"line":62},[59,13204,13205],{"class":1372},"export",[59,13207,13208],{"class":178}," TMPDIR",[59,13210,1373],{"class":1372},[59,13212,13213],{"class":178},"\u002Fpath\u002Fto\u002Fexisting\u002Fdirectory\u002Fwith\u002Fsufficient\u002Fspace\n",[14,13215,13216,13219,13220,1706,13223,114],{},[364,13217,13218],{},"On Windows",", you can set the ",[18,13221,13222],{},"TEMP",[18,13224,13225],{},"TMP",[50,13227,13229],{"className":13142,"code":13228,"language":12293,"meta":55,"style":55},"Set TMP=\"C:\\path\\to\\existing\\directory\\with\\sufficient\\space\"\n",[18,13230,13231],{"__ignoreMap":55},[59,13232,13233],{"class":61,"line":62},[59,13234,13228],{},[14,13236,13237],{},"Retry installation after making these adjustments.",[23,13239,13241],{"id":13240},"further-reading","Further reading",[14,13243,13244,13245,273],{},"For more detailed technical information about the FlowFuse Device Agent, like list of supported parameters or how to contribute, please refer to the ",[41,13246,7646],{"href":13247,"rel":13248},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdevice-agent\u002Fblob\u002Fmain\u002Finstaller\u002FREADME.md",[831],[8989,13250,13252,13255],{"className":13251,"dataFootnotes":55},[8992],[23,13253,8997],{"className":13254,"id":8357},[8996],[398,13256,13257],{},[31,13258,13259,13260,13263,13264,3497,13267,3497,13274,3497,13281],{"id":9002},"Run ",[18,13261,13262],{},"powershell -Command \"Start-Process 'cmd' -Verb RunAs\""," to launch an elevated command prompt window (e.g. as an admin user) ",[41,13265,9013],{"href":9009,"ariaLabel":9010,"className":13266,"dataFootnoteBackref":55},[9012],[41,13268,9013,13272],{"href":13269,"ariaLabel":13270,"className":13271,"dataFootnoteBackref":55},"#user-content-fnref-1-2","Back to reference 1-2",[9012],[8351,13273,3158],{},[41,13275,9013,13279],{"href":13276,"ariaLabel":13277,"className":13278,"dataFootnoteBackref":55},"#user-content-fnref-1-3","Back to reference 1-3",[9012],[8351,13280,9921],{},[41,13282,9013,13286],{"href":13283,"ariaLabel":13284,"className":13285,"dataFootnoteBackref":55},"#user-content-fnref-1-4","Back to reference 1-4",[9012],[8351,13287,3109],{},[316,13289,13290],{},"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}",{"title":55,"searchDepth":77,"depth":77,"links":13292},[13293,13294,13297,13302,13306,13312,13313],{"id":12197,"depth":77,"text":12198},{"id":12207,"depth":77,"text":12208,"children":13295},[13296],{"id":12222,"depth":88,"text":12223},{"id":12232,"depth":77,"text":12233,"children":13298},[13299,13300,13301],{"id":12236,"depth":88,"text":12237},{"id":9557,"depth":88,"text":9558},{"id":12363,"depth":88,"text":12364},{"id":12544,"depth":77,"text":12545,"children":13303},[13304,13305],{"id":12548,"depth":88,"text":12549},{"id":12583,"depth":88,"text":12179},{"id":12620,"depth":77,"text":12621,"children":13307},[13308,13309,13310,13311],{"id":12624,"depth":88,"text":12625},{"id":12951,"depth":88,"text":12952},{"id":13090,"depth":88,"text":13091},{"id":13152,"depth":88,"text":13153},{"id":13240,"depth":77,"text":13241},{"id":8357,"depth":77,"text":8997},{},"Device Agent Installer","device-agent\u002Finstall\u002Fdevice-agent-installer.md","\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Fdevice-agent-installer",{"title":12189,"description":55},"docs\u002Fdevice-agent\u002Finstall\u002Fdevice-agent-installer","PYnaUx9XUueJpT15tYrr9TWl6RqlTX5fB7ul20u6RNQ",{"id":4,"title":5,"body":13322,"description":328,"extension":329,"layout":330,"meta":13537,"navGroup":332,"navOrder":88,"navTitle":5,"navigation":187,"originalPath":333,"path":334,"redirect":330,"seo":13538,"stem":336,"updated":337,"version":338,"__hash__":339},{"type":7,"value":13323,"toc":13528},[13324,13326,13330,13332,13342,13344,13376,13378,13382,13422,13424,13496,13498,13502,13504,13526],[10,13325,5],{"id":12},[14,13327,16,13328,21],{},[18,13329,20],{},[23,13331,26],{"id":25},[28,13333,13334,13336],{},[31,13335,33],{},[31,13337,36,13338,39,13340],{},[18,13339,20],{},[41,13341,44],{"href":43},[23,13343,48],{"id":47},[50,13345,13346],{"className":52,"code":53,"language":54,"meta":55,"style":55},[18,13347,13348,13356,13364,13372],{"__ignoreMap":55},[59,13349,13350,13352,13354],{"class":61,"line":62},[59,13351,66],{"class":65},[59,13353,70],{"class":69},[59,13355,74],{"class":73},[59,13357,13358,13360,13362],{"class":61,"line":77},[59,13359,80],{"class":73},[59,13361,83],{"class":69},[59,13363,74],{"class":73},[59,13365,13366,13368,13370],{"class":61,"line":88},[59,13367,91],{"class":73},[59,13369,94],{"class":69},[59,13371,74],{"class":73},[59,13373,13374],{"class":61,"line":99},[59,13375,102],{"class":69},[104,13377,107],{"id":106},[14,13379,110,13380,114],{},[18,13381,113],{},[50,13383,13384],{"className":52,"code":117,"language":54,"meta":55,"style":55},[18,13385,13386,13394,13402,13410,13418],{"__ignoreMap":55},[59,13387,13388,13390,13392],{"class":61,"line":62},[59,13389,66],{"class":65},[59,13391,70],{"class":69},[59,13393,74],{"class":73},[59,13395,13396,13398,13400],{"class":61,"line":77},[59,13397,132],{"class":73},[59,13399,135],{"class":69},[59,13401,74],{"class":73},[59,13403,13404,13406,13408],{"class":61,"line":88},[59,13405,80],{"class":73},[59,13407,83],{"class":69},[59,13409,74],{"class":73},[59,13411,13412,13414,13416],{"class":61,"line":99},[59,13413,91],{"class":73},[59,13415,94],{"class":69},[59,13417,74],{"class":73},[59,13419,13420],{"class":61,"line":156},[59,13421,102],{"class":69},[23,13423,162],{"id":161},[50,13425,13426],{"className":165,"code":166,"language":167,"meta":55,"style":55},[18,13427,13428,13436,13440,13446,13452,13460,13466,13472,13478,13484,13490],{"__ignoreMap":55},[59,13429,13430,13432,13434],{"class":61,"line":62},[59,13431,175],{"class":174},[59,13433,179],{"class":178},[59,13435,182],{"class":69},[59,13437,13438],{"class":61,"line":77},[59,13439,188],{"emptyLinePlaceholder":187},[59,13441,13442,13444],{"class":61,"line":88},[59,13443,193],{"class":174},[59,13445,196],{"class":178},[59,13447,13448,13450],{"class":61,"line":99},[59,13449,201],{"class":174},[59,13451,196],{"class":178},[59,13453,13454,13456,13458],{"class":61,"line":156},[59,13455,208],{"class":174},[59,13457,179],{"class":178},[59,13459,213],{"class":69},[59,13461,13462,13464],{"class":61,"line":216},[59,13463,219],{"class":174},[59,13465,196],{"class":178},[59,13467,13468,13470],{"class":61,"line":224},[59,13469,227],{"class":178},[59,13471,230],{"class":69},[59,13473,13474,13476],{"class":61,"line":233},[59,13475,236],{"class":174},[59,13477,196],{"class":178},[59,13479,13480,13482],{"class":61,"line":241},[59,13481,227],{"class":178},[59,13483,246],{"class":69},[59,13485,13486,13488],{"class":61,"line":249},[59,13487,252],{"class":174},[59,13489,196],{"class":178},[59,13491,13492,13494],{"class":61,"line":257},[59,13493,227],{"class":178},[59,13495,262],{"class":69},[23,13497,266],{"id":265},[14,13499,269,13500,273],{},[18,13501,272],{},[23,13503,277],{"id":276},[28,13505,13506,13510,13516],{},[31,13507,282,13508,286],{},[18,13509,285],{},[31,13511,289,13512,293,13514,273],{},[18,13513,292],{},[18,13515,285],{},[31,13517,298,13518,302,13520,306,13522,310,13524,273],{},[18,13519,301],{},[18,13521,305],{},[18,13523,309],{},[41,13525,314],{"href":313},[316,13527,318],{},{"title":55,"searchDepth":77,"depth":77,"links":13529},[13530,13531,13534,13535,13536],{"id":25,"depth":77,"text":26},{"id":47,"depth":77,"text":48,"children":13532},[13533],{"id":106,"depth":88,"text":107},{"id":161,"depth":77,"text":162},{"id":265,"depth":77,"text":266},{"id":276,"depth":77,"text":277},{},{"title":5,"description":328},{"id":13540,"title":55,"body":13541,"description":55,"extension":329,"layout":532,"meta":13545,"navGroup":332,"navOrder":88,"navTitle":13547,"navigation":187,"originalPath":13548,"path":13549,"redirect":13550,"seo":13551,"stem":13552,"updated":337,"version":338,"__hash__":13553},"docs\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Findex.md",{"type":7,"value":13542,"toc":13543},[],{"title":55,"searchDepth":77,"depth":77,"links":13544},[],{"tags":13546},[4066],"Installation","device-agent\u002Finstall\u002FREADME.md","\u002Fdocs\u002Fdevice-agent\u002Finstall",{"to":11871},{"description":55},"docs\u002Fdevice-agent\u002Finstall\u002Findex","OVlkgvltFdPewFasmicFzXqpIVStkPeJ1y3dJgWwCls",{"id":13555,"title":13556,"body":13557,"description":55,"extension":329,"layout":330,"meta":14749,"navGroup":332,"navOrder":99,"navTitle":13556,"navigation":187,"originalPath":14750,"path":14751,"redirect":330,"seo":14752,"stem":14753,"updated":337,"version":338,"__hash__":14754},"docs\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Fkubernetes.md","Kubernetes Install",{"type":7,"value":13558,"toc":14744},[13559,13562,13566,13569,13572,13593,13596,13606,13609,13612,13618,13643,13646,14079,14082,14085,14091,14097,14117,14120,14126,14741],[10,13560,13556],{"id":13561},"kubernetes-install",[23,13563,13565],{"id":13564},"when-to-use-each-option","When to Use Each Option",[14,13567,13568],{},"Running the Device Agent in Kubernetes is appropriate when devices are containerized or managed as part of a Kubernetes-based edge or infrastructure platform.",[14,13570,13571],{},"Choose your deployment pattern based on how you manage device identity:",[28,13573,13574,13585],{},[31,13575,13576,13579,13581,13582,13584],{},[364,13577,13578],{},"Fixed Configuration",[662,13580],{},"\nUse when the device already exists in FlowFuse and you have a ",[18,13583,20],{}," with its credentials. One deployment maps to one device identity.",[31,13586,13587,13590,13592],{},[364,13588,13589],{},"Automatic Provisioning",[662,13591],{},"\nUse when devices should register themselves at startup using a Provisioning Token. Each instance requires writable persistent storage.",[14,13594,13595],{},"Any deployment on Kubernetes is going to be specific to the environment and requirements of the solution. The following examples show two common patterns for running the FlowFuse Device Agent on Kubernetes:",[28,13597,13598,13603],{},[31,13599,13600,13601],{},"Fixed configuration using a static ",[18,13602,20],{},[31,13604,13605],{},"Automatic provisioning using a FlowFuse Provisioning Token",[14,13607,13608],{},"Choose the approach that matches how you manage device lifecycle and credentials.",[23,13610,13578],{"id":13611},"fixed-configuration",[14,13613,13614,13615,13617],{},"If you have an existing ",[18,13616,20],{}," file containing a set of Device Agent credentials.",[50,13619,13621],{"className":52,"code":13620,"language":54,"meta":55,"style":55},"kubectl create secret generic device-one-secret --from-file=device.yml=.\u002Fdevice.yml\n",[18,13622,13623],{"__ignoreMap":55},[59,13624,13625,13628,13631,13634,13637,13640],{"class":61,"line":62},[59,13626,13627],{"class":65},"kubectl",[59,13629,13630],{"class":69}," create",[59,13632,13633],{"class":69}," secret",[59,13635,13636],{"class":69}," generic",[59,13638,13639],{"class":69}," device-one-secret",[59,13641,13642],{"class":73}," --from-file=device.yml=.\u002Fdevice.yml\n",[14,13644,13645],{},"The following manifest will create a Deployment and Service for a device using the supplied Secret as its credentials",[50,13647,13649],{"className":165,"code":13648,"language":167,"meta":55,"style":55},"apiVersion: apps\u002Fv1\nkind: Deployment\nmetadata:\n  name: device-one\n  labels:\n    app: device-one\nspec:\n  replicas: 1 # there can only be one replica as there is one configuration\n  revisionHistoryLimit: 10\n  selector:\n    matchLabels:\n      app: device-one\n  template:\n    metadata:\n      labels:\n        app: device-one\n    spec:\n      containers:\n      - name: device-one\n        image: flowfuse\u002Fdevice-agent:latest\n        ports:\n        - containerPort: 1880\n        volumeMounts:\n        - name: config\n          mountPath: \"\u002Fopt\u002Fflowfuse-device\u002Fdevice.yml\"\n          subPath: \"device.yml\"\n          readOnly: true\n        resources:\n          limits:\n            cpu: 1000m\n            memory: 256Mi\n          requests:\n            cpu: 500m\n            memory: 128Mi\n      volumes:\n      - name: config\n        secret:\n          secretName: device-one-secret\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: device-one-service\nspec:\n  selector:\n    app: device-one\n  ports:\n  - protocol: TCP\n    port: 1880\n    targetPort: 1880\n",[18,13650,13651,13661,13671,13678,13688,13695,13704,13711,13723,13732,13739,13746,13755,13762,13769,13776,13785,13792,13799,13809,13818,13825,13838,13845,13856,13866,13876,13885,13892,13899,13909,13919,13926,13935,13944,13951,13961,13968,13978,13983,13992,14001,14007,14016,14022,14028,14037,14045,14059,14069],{"__ignoreMap":55},[59,13652,13653,13656,13658],{"class":61,"line":62},[59,13654,13655],{"class":174},"apiVersion",[59,13657,179],{"class":178},[59,13659,13660],{"class":69},"apps\u002Fv1\n",[59,13662,13663,13666,13668],{"class":61,"line":77},[59,13664,13665],{"class":174},"kind",[59,13667,179],{"class":178},[59,13669,13670],{"class":69},"Deployment\n",[59,13672,13673,13676],{"class":61,"line":88},[59,13674,13675],{"class":174},"metadata",[59,13677,196],{"class":178},[59,13679,13680,13683,13685],{"class":61,"line":99},[59,13681,13682],{"class":174},"  name",[59,13684,179],{"class":178},[59,13686,13687],{"class":69},"device-one\n",[59,13689,13690,13693],{"class":61,"line":156},[59,13691,13692],{"class":174},"  labels",[59,13694,196],{"class":178},[59,13696,13697,13700,13702],{"class":61,"line":216},[59,13698,13699],{"class":174},"    app",[59,13701,179],{"class":178},[59,13703,13687],{"class":69},[59,13705,13706,13709],{"class":61,"line":224},[59,13707,13708],{"class":174},"spec",[59,13710,196],{"class":178},[59,13712,13713,13716,13718,13720],{"class":61,"line":233},[59,13714,13715],{"class":174},"  replicas",[59,13717,179],{"class":178},[59,13719,8359],{"class":73},[59,13721,13722],{"class":3773}," # there can only be one replica as there is one configuration\n",[59,13724,13725,13728,13730],{"class":61,"line":241},[59,13726,13727],{"class":174},"  revisionHistoryLimit",[59,13729,179],{"class":178},[59,13731,8221],{"class":73},[59,13733,13734,13737],{"class":61,"line":249},[59,13735,13736],{"class":174},"  selector",[59,13738,196],{"class":178},[59,13740,13741,13744],{"class":61,"line":257},[59,13742,13743],{"class":174},"    matchLabels",[59,13745,196],{"class":178},[59,13747,13748,13751,13753],{"class":61,"line":3137},[59,13749,13750],{"class":174},"      app",[59,13752,179],{"class":178},[59,13754,13687],{"class":69},[59,13756,13757,13760],{"class":61,"line":3150},[59,13758,13759],{"class":174},"  template",[59,13761,196],{"class":178},[59,13763,13764,13767],{"class":61,"line":3163},[59,13765,13766],{"class":174},"    metadata",[59,13768,196],{"class":178},[59,13770,13771,13774],{"class":61,"line":3176},[59,13772,13773],{"class":174},"      labels",[59,13775,196],{"class":178},[59,13777,13778,13781,13783],{"class":61,"line":3187},[59,13779,13780],{"class":174},"        app",[59,13782,179],{"class":178},[59,13784,13687],{"class":69},[59,13786,13787,13790],{"class":61,"line":3193},[59,13788,13789],{"class":174},"    spec",[59,13791,196],{"class":178},[59,13793,13794,13797],{"class":61,"line":3201},[59,13795,13796],{"class":174},"      containers",[59,13798,196],{"class":178},[59,13800,13801,13803,13805,13807],{"class":61,"line":3214},[59,13802,227],{"class":178},[59,13804,5782],{"class":174},[59,13806,179],{"class":178},[59,13808,13687],{"class":69},[59,13810,13811,13814,13816],{"class":61,"line":3222},[59,13812,13813],{"class":174},"        image",[59,13815,179],{"class":178},[59,13817,213],{"class":69},[59,13819,13820,13823],{"class":61,"line":3233},[59,13821,13822],{"class":174},"        ports",[59,13824,196],{"class":178},[59,13826,13827,13830,13833,13835],{"class":61,"line":3239},[59,13828,13829],{"class":178},"        - ",[59,13831,13832],{"class":174},"containerPort",[59,13834,179],{"class":178},[59,13836,13837],{"class":73},"1880\n",[59,13839,13840,13843],{"class":61,"line":3247},[59,13841,13842],{"class":174},"        volumeMounts",[59,13844,196],{"class":178},[59,13846,13847,13849,13851,13853],{"class":61,"line":3256},[59,13848,13829],{"class":178},[59,13850,5782],{"class":174},[59,13852,179],{"class":178},[59,13854,13855],{"class":69},"config\n",[59,13857,13858,13861,13863],{"class":61,"line":3261},[59,13859,13860],{"class":174},"          mountPath",[59,13862,179],{"class":178},[59,13864,13865],{"class":69},"\"\u002Fopt\u002Fflowfuse-device\u002Fdevice.yml\"\n",[59,13867,13868,13871,13873],{"class":61,"line":3269},[59,13869,13870],{"class":174},"          subPath",[59,13872,179],{"class":178},[59,13874,13875],{"class":69},"\"device.yml\"\n",[59,13877,13878,13881,13883],{"class":61,"line":3278},[59,13879,13880],{"class":174},"          readOnly",[59,13882,179],{"class":178},[59,13884,3230],{"class":73},[59,13886,13887,13890],{"class":61,"line":3284},[59,13888,13889],{"class":174},"        resources",[59,13891,196],{"class":178},[59,13893,13894,13897],{"class":61,"line":3289},[59,13895,13896],{"class":174},"          limits",[59,13898,196],{"class":178},[59,13900,13901,13904,13906],{"class":61,"line":3297},[59,13902,13903],{"class":174},"            cpu",[59,13905,179],{"class":178},[59,13907,13908],{"class":69},"1000m\n",[59,13910,13911,13914,13916],{"class":61,"line":3310},[59,13912,13913],{"class":174},"            memory",[59,13915,179],{"class":178},[59,13917,13918],{"class":69},"256Mi\n",[59,13920,13921,13924],{"class":61,"line":3321},[59,13922,13923],{"class":174},"          requests",[59,13925,196],{"class":178},[59,13927,13928,13930,13932],{"class":61,"line":3327},[59,13929,13903],{"class":174},[59,13931,179],{"class":178},[59,13933,13934],{"class":69},"500m\n",[59,13936,13937,13939,13941],{"class":61,"line":3333},[59,13938,13913],{"class":174},[59,13940,179],{"class":178},[59,13942,13943],{"class":69},"128Mi\n",[59,13945,13946,13949],{"class":61,"line":6115},[59,13947,13948],{"class":174},"      volumes",[59,13950,196],{"class":178},[59,13952,13953,13955,13957,13959],{"class":61,"line":6152},[59,13954,227],{"class":178},[59,13956,5782],{"class":174},[59,13958,179],{"class":178},[59,13960,13855],{"class":69},[59,13962,13963,13966],{"class":61,"line":6158},[59,13964,13965],{"class":174},"        secret",[59,13967,196],{"class":178},[59,13969,13970,13973,13975],{"class":61,"line":6164},[59,13971,13972],{"class":174},"          secretName",[59,13974,179],{"class":178},[59,13976,13977],{"class":69},"device-one-secret\n",[59,13979,13980],{"class":61,"line":6170},[59,13981,13982],{"class":65},"---\n",[59,13984,13985,13987,13989],{"class":61,"line":6175},[59,13986,13655],{"class":174},[59,13988,179],{"class":178},[59,13990,13991],{"class":69},"v1\n",[59,13993,13994,13996,13998],{"class":61,"line":6619},[59,13995,13665],{"class":174},[59,13997,179],{"class":178},[59,13999,14000],{"class":69},"Service\n",[59,14002,14003,14005],{"class":61,"line":6625},[59,14004,13675],{"class":174},[59,14006,196],{"class":178},[59,14008,14009,14011,14013],{"class":61,"line":8974},[59,14010,13682],{"class":174},[59,14012,179],{"class":178},[59,14014,14015],{"class":69},"device-one-service\n",[59,14017,14018,14020],{"class":61,"line":8979},[59,14019,13708],{"class":174},[59,14021,196],{"class":178},[59,14023,14024,14026],{"class":61,"line":8985},[59,14025,13736],{"class":174},[59,14027,196],{"class":178},[59,14029,14031,14033,14035],{"class":61,"line":14030},46,[59,14032,13699],{"class":174},[59,14034,179],{"class":178},[59,14036,13687],{"class":69},[59,14038,14040,14043],{"class":61,"line":14039},47,[59,14041,14042],{"class":174},"  ports",[59,14044,196],{"class":178},[59,14046,14048,14051,14054,14056],{"class":61,"line":14047},48,[59,14049,14050],{"class":178},"  - ",[59,14052,14053],{"class":174},"protocol",[59,14055,179],{"class":178},[59,14057,14058],{"class":69},"TCP\n",[59,14060,14062,14065,14067],{"class":61,"line":14061},49,[59,14063,14064],{"class":174},"    port",[59,14066,179],{"class":178},[59,14068,13837],{"class":73},[59,14070,14072,14075,14077],{"class":61,"line":14071},50,[59,14073,14074],{"class":174},"    targetPort",[59,14076,179],{"class":178},[59,14078,13837],{"class":73},[23,14080,13589],{"id":14081},"automatic-provisioning",[14,14083,14084],{},"Using a FlowFuse Provisioning Token to automatically configure a new Device Agent on deployment.",[14,14086,14087,14088,14090],{},"Because the Device Agent will need to re-write the ",[18,14089,20],{}," file it can no longer be stored in a Secret and a PersistentVolume must be used for each instance of the Device Agent.",[14,14092,14093,14094,14096],{},"A Secret is used to hold the initial ",[18,14095,20],{}," which contains the provisioning token.",[50,14098,14100],{"className":52,"code":14099,"language":54,"meta":55,"style":55},"kubectl create secret generic device-provisioning-secret --from-file=device.yml=.\u002Fdevice.yml\n",[18,14101,14102],{"__ignoreMap":55},[59,14103,14104,14106,14108,14110,14112,14115],{"class":61,"line":62},[59,14105,13627],{"class":65},[59,14107,13630],{"class":69},[59,14109,13633],{"class":69},[59,14111,13636],{"class":69},[59,14113,14114],{"class":69}," device-provisioning-secret",[59,14116,13642],{"class":73},[14,14118,14119],{},"The following manifest will create a Deployment, Service and PVC for a device using the supplied Secret as the source of the Provisioning token.",[14,14121,14122,14123,14125],{},"The PVC will be used to store the updated ",[18,14124,20],{}," and the Node-RED nodes installed by the Remote Instance.",[50,14127,14129],{"className":165,"code":14128,"language":167,"meta":55,"style":55},"apiVersion: apps\u002Fv1\nkind: Deployment\nmetadata:\n  name: device-one\n  labels:\n    app: device-one\nspec:\n  replicas: 1 # to scale to more than one instance you should modify this to use a StatefulSet\n  revisionHistoryLimit: 10\n  selector:\n    matchLabels:\n      app: device-one\n  template:\n    metadata:\n      labels:\n        app: device-one\n    spec:\n      initContainers: # on first run copies the device.yml from Secret to PVC volume\n      - name: config-copy\n        image: busybox:latest\n        command: \n        - \"\u002Fbin\u002Fsh\"\n        - \"-c\"\n        - \"if [ ! -f \u002Fopt\u002Fflowfuse-device\u002Fdevice.yml ]; then cp \u002Ftmp\u002Fdevice.yml \u002Fopt\u002Fflowfuse-device\u002Fdevice.yml; fi\"\n        volumeMounts:\n        - name: config\n          mountPath: \"\u002Fopt\u002Fflowfuse-device\"\n        - name: initial-config\n          mountPath: \"\u002Ftmp\u002Fdevice.yml\"\n          subPath: \"device.yml\"\n          readOnly: true\n      containers:\n      - name: device-one\n        image: flowfuse\u002Fdevice-agent:latest\n        ports:\n        - containerPort: 1880\n        volumeMounts:\n        - name: config\n          mountPath: \"\u002Fopt\u002Fflowfuse-device\"\n        resources:\n          limits:\n            cpu: 1000m\n            memory: 256Mi\n          requests:\n            cpu: 500m\n            memory: 128Mi\n      volumes:\n      - name: initial-config\n        secret:\n          secretName: device-provisioning-secret\n      - name: config\n        persistentVolumeClaim:\n          claimName: device-one-pvc\n        \n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: device-one-service\nspec:\n  selector:\n    app: device-one\n  ports:\n  - protocol: TCP\n    port: 1880\n    targetPort: 1880\n---\napiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n  name: device-one-pvc\nspec:\n  accessModes:\n  - ReadWriteOnce\n  resources:\n    requests:\n      storage: 1Gi\n",[18,14130,14131,14139,14147,14153,14161,14167,14175,14181,14192,14200,14206,14212,14220,14226,14232,14238,14246,14252,14262,14273,14282,14290,14297,14304,14311,14317,14327,14336,14347,14356,14364,14372,14378,14388,14396,14402,14412,14418,14428,14436,14442,14448,14456,14464,14470,14478,14486,14492,14502,14508,14517,14528,14536,14547,14553,14558,14567,14576,14583,14592,14599,14606,14615,14622,14633,14642,14651,14656,14665,14675,14682,14691,14698,14706,14714,14722,14730],{"__ignoreMap":55},[59,14132,14133,14135,14137],{"class":61,"line":62},[59,14134,13655],{"class":174},[59,14136,179],{"class":178},[59,14138,13660],{"class":69},[59,14140,14141,14143,14145],{"class":61,"line":77},[59,14142,13665],{"class":174},[59,14144,179],{"class":178},[59,14146,13670],{"class":69},[59,14148,14149,14151],{"class":61,"line":88},[59,14150,13675],{"class":174},[59,14152,196],{"class":178},[59,14154,14155,14157,14159],{"class":61,"line":99},[59,14156,13682],{"class":174},[59,14158,179],{"class":178},[59,14160,13687],{"class":69},[59,14162,14163,14165],{"class":61,"line":156},[59,14164,13692],{"class":174},[59,14166,196],{"class":178},[59,14168,14169,14171,14173],{"class":61,"line":216},[59,14170,13699],{"class":174},[59,14172,179],{"class":178},[59,14174,13687],{"class":69},[59,14176,14177,14179],{"class":61,"line":224},[59,14178,13708],{"class":174},[59,14180,196],{"class":178},[59,14182,14183,14185,14187,14189],{"class":61,"line":233},[59,14184,13715],{"class":174},[59,14186,179],{"class":178},[59,14188,8359],{"class":73},[59,14190,14191],{"class":3773}," # to scale to more than one instance you should modify this to use a StatefulSet\n",[59,14193,14194,14196,14198],{"class":61,"line":241},[59,14195,13727],{"class":174},[59,14197,179],{"class":178},[59,14199,8221],{"class":73},[59,14201,14202,14204],{"class":61,"line":249},[59,14203,13736],{"class":174},[59,14205,196],{"class":178},[59,14207,14208,14210],{"class":61,"line":257},[59,14209,13743],{"class":174},[59,14211,196],{"class":178},[59,14213,14214,14216,14218],{"class":61,"line":3137},[59,14215,13750],{"class":174},[59,14217,179],{"class":178},[59,14219,13687],{"class":69},[59,14221,14222,14224],{"class":61,"line":3150},[59,14223,13759],{"class":174},[59,14225,196],{"class":178},[59,14227,14228,14230],{"class":61,"line":3163},[59,14229,13766],{"class":174},[59,14231,196],{"class":178},[59,14233,14234,14236],{"class":61,"line":3176},[59,14235,13773],{"class":174},[59,14237,196],{"class":178},[59,14239,14240,14242,14244],{"class":61,"line":3187},[59,14241,13780],{"class":174},[59,14243,179],{"class":178},[59,14245,13687],{"class":69},[59,14247,14248,14250],{"class":61,"line":3193},[59,14249,13789],{"class":174},[59,14251,196],{"class":178},[59,14253,14254,14257,14259],{"class":61,"line":3201},[59,14255,14256],{"class":174},"      initContainers",[59,14258,179],{"class":178},[59,14260,14261],{"class":3773},"# on first run copies the device.yml from Secret to PVC volume\n",[59,14263,14264,14266,14268,14270],{"class":61,"line":3214},[59,14265,227],{"class":178},[59,14267,5782],{"class":174},[59,14269,179],{"class":178},[59,14271,14272],{"class":69},"config-copy\n",[59,14274,14275,14277,14279],{"class":61,"line":3222},[59,14276,13813],{"class":174},[59,14278,179],{"class":178},[59,14280,14281],{"class":69},"busybox:latest\n",[59,14283,14284,14287],{"class":61,"line":3233},[59,14285,14286],{"class":174},"        command",[59,14288,14289],{"class":178},": \n",[59,14291,14292,14294],{"class":61,"line":3239},[59,14293,13829],{"class":178},[59,14295,14296],{"class":69},"\"\u002Fbin\u002Fsh\"\n",[59,14298,14299,14301],{"class":61,"line":3247},[59,14300,13829],{"class":178},[59,14302,14303],{"class":69},"\"-c\"\n",[59,14305,14306,14308],{"class":61,"line":3256},[59,14307,13829],{"class":178},[59,14309,14310],{"class":69},"\"if [ ! -f \u002Fopt\u002Fflowfuse-device\u002Fdevice.yml ]; then cp \u002Ftmp\u002Fdevice.yml \u002Fopt\u002Fflowfuse-device\u002Fdevice.yml; fi\"\n",[59,14312,14313,14315],{"class":61,"line":3261},[59,14314,13842],{"class":174},[59,14316,196],{"class":178},[59,14318,14319,14321,14323,14325],{"class":61,"line":3269},[59,14320,13829],{"class":178},[59,14322,5782],{"class":174},[59,14324,179],{"class":178},[59,14326,13855],{"class":69},[59,14328,14329,14331,14333],{"class":61,"line":3278},[59,14330,13860],{"class":174},[59,14332,179],{"class":178},[59,14334,14335],{"class":69},"\"\u002Fopt\u002Fflowfuse-device\"\n",[59,14337,14338,14340,14342,14344],{"class":61,"line":3284},[59,14339,13829],{"class":178},[59,14341,5782],{"class":174},[59,14343,179],{"class":178},[59,14345,14346],{"class":69},"initial-config\n",[59,14348,14349,14351,14353],{"class":61,"line":3289},[59,14350,13860],{"class":174},[59,14352,179],{"class":178},[59,14354,14355],{"class":69},"\"\u002Ftmp\u002Fdevice.yml\"\n",[59,14357,14358,14360,14362],{"class":61,"line":3297},[59,14359,13870],{"class":174},[59,14361,179],{"class":178},[59,14363,13875],{"class":69},[59,14365,14366,14368,14370],{"class":61,"line":3310},[59,14367,13880],{"class":174},[59,14369,179],{"class":178},[59,14371,3230],{"class":73},[59,14373,14374,14376],{"class":61,"line":3321},[59,14375,13796],{"class":174},[59,14377,196],{"class":178},[59,14379,14380,14382,14384,14386],{"class":61,"line":3327},[59,14381,227],{"class":178},[59,14383,5782],{"class":174},[59,14385,179],{"class":178},[59,14387,13687],{"class":69},[59,14389,14390,14392,14394],{"class":61,"line":3333},[59,14391,13813],{"class":174},[59,14393,179],{"class":178},[59,14395,213],{"class":69},[59,14397,14398,14400],{"class":61,"line":6115},[59,14399,13822],{"class":174},[59,14401,196],{"class":178},[59,14403,14404,14406,14408,14410],{"class":61,"line":6152},[59,14405,13829],{"class":178},[59,14407,13832],{"class":174},[59,14409,179],{"class":178},[59,14411,13837],{"class":73},[59,14413,14414,14416],{"class":61,"line":6158},[59,14415,13842],{"class":174},[59,14417,196],{"class":178},[59,14419,14420,14422,14424,14426],{"class":61,"line":6164},[59,14421,13829],{"class":178},[59,14423,5782],{"class":174},[59,14425,179],{"class":178},[59,14427,13855],{"class":69},[59,14429,14430,14432,14434],{"class":61,"line":6170},[59,14431,13860],{"class":174},[59,14433,179],{"class":178},[59,14435,14335],{"class":69},[59,14437,14438,14440],{"class":61,"line":6175},[59,14439,13889],{"class":174},[59,14441,196],{"class":178},[59,14443,14444,14446],{"class":61,"line":6619},[59,14445,13896],{"class":174},[59,14447,196],{"class":178},[59,14449,14450,14452,14454],{"class":61,"line":6625},[59,14451,13903],{"class":174},[59,14453,179],{"class":178},[59,14455,13908],{"class":69},[59,14457,14458,14460,14462],{"class":61,"line":8974},[59,14459,13913],{"class":174},[59,14461,179],{"class":178},[59,14463,13918],{"class":69},[59,14465,14466,14468],{"class":61,"line":8979},[59,14467,13923],{"class":174},[59,14469,196],{"class":178},[59,14471,14472,14474,14476],{"class":61,"line":8985},[59,14473,13903],{"class":174},[59,14475,179],{"class":178},[59,14477,13934],{"class":69},[59,14479,14480,14482,14484],{"class":61,"line":14030},[59,14481,13913],{"class":174},[59,14483,179],{"class":178},[59,14485,13943],{"class":69},[59,14487,14488,14490],{"class":61,"line":14039},[59,14489,13948],{"class":174},[59,14491,196],{"class":178},[59,14493,14494,14496,14498,14500],{"class":61,"line":14047},[59,14495,227],{"class":178},[59,14497,5782],{"class":174},[59,14499,179],{"class":178},[59,14501,14346],{"class":69},[59,14503,14504,14506],{"class":61,"line":14061},[59,14505,13965],{"class":174},[59,14507,196],{"class":178},[59,14509,14510,14512,14514],{"class":61,"line":14071},[59,14511,13972],{"class":174},[59,14513,179],{"class":178},[59,14515,14516],{"class":69},"device-provisioning-secret\n",[59,14518,14520,14522,14524,14526],{"class":61,"line":14519},51,[59,14521,227],{"class":178},[59,14523,5782],{"class":174},[59,14525,179],{"class":178},[59,14527,13855],{"class":69},[59,14529,14531,14534],{"class":61,"line":14530},52,[59,14532,14533],{"class":174},"        persistentVolumeClaim",[59,14535,196],{"class":178},[59,14537,14539,14542,14544],{"class":61,"line":14538},53,[59,14540,14541],{"class":174},"          claimName",[59,14543,179],{"class":178},[59,14545,14546],{"class":69},"device-one-pvc\n",[59,14548,14550],{"class":61,"line":14549},54,[59,14551,14552],{"class":178},"        \n",[59,14554,14556],{"class":61,"line":14555},55,[59,14557,13982],{"class":65},[59,14559,14561,14563,14565],{"class":61,"line":14560},56,[59,14562,13655],{"class":174},[59,14564,179],{"class":178},[59,14566,13991],{"class":69},[59,14568,14570,14572,14574],{"class":61,"line":14569},57,[59,14571,13665],{"class":174},[59,14573,179],{"class":178},[59,14575,14000],{"class":69},[59,14577,14579,14581],{"class":61,"line":14578},58,[59,14580,13675],{"class":174},[59,14582,196],{"class":178},[59,14584,14586,14588,14590],{"class":61,"line":14585},59,[59,14587,13682],{"class":174},[59,14589,179],{"class":178},[59,14591,14015],{"class":69},[59,14593,14595,14597],{"class":61,"line":14594},60,[59,14596,13708],{"class":174},[59,14598,196],{"class":178},[59,14600,14602,14604],{"class":61,"line":14601},61,[59,14603,13736],{"class":174},[59,14605,196],{"class":178},[59,14607,14609,14611,14613],{"class":61,"line":14608},62,[59,14610,13699],{"class":174},[59,14612,179],{"class":178},[59,14614,13687],{"class":69},[59,14616,14618,14620],{"class":61,"line":14617},63,[59,14619,14042],{"class":174},[59,14621,196],{"class":178},[59,14623,14625,14627,14629,14631],{"class":61,"line":14624},64,[59,14626,14050],{"class":178},[59,14628,14053],{"class":174},[59,14630,179],{"class":178},[59,14632,14058],{"class":69},[59,14634,14636,14638,14640],{"class":61,"line":14635},65,[59,14637,14064],{"class":174},[59,14639,179],{"class":178},[59,14641,13837],{"class":73},[59,14643,14645,14647,14649],{"class":61,"line":14644},66,[59,14646,14074],{"class":174},[59,14648,179],{"class":178},[59,14650,13837],{"class":73},[59,14652,14654],{"class":61,"line":14653},67,[59,14655,13982],{"class":65},[59,14657,14659,14661,14663],{"class":61,"line":14658},68,[59,14660,13655],{"class":174},[59,14662,179],{"class":178},[59,14664,13991],{"class":69},[59,14666,14668,14670,14672],{"class":61,"line":14667},69,[59,14669,13665],{"class":174},[59,14671,179],{"class":178},[59,14673,14674],{"class":69},"PersistentVolumeClaim\n",[59,14676,14678,14680],{"class":61,"line":14677},70,[59,14679,13675],{"class":174},[59,14681,196],{"class":178},[59,14683,14685,14687,14689],{"class":61,"line":14684},71,[59,14686,13682],{"class":174},[59,14688,179],{"class":178},[59,14690,14546],{"class":69},[59,14692,14694,14696],{"class":61,"line":14693},72,[59,14695,13708],{"class":174},[59,14697,196],{"class":178},[59,14699,14701,14704],{"class":61,"line":14700},73,[59,14702,14703],{"class":174},"  accessModes",[59,14705,196],{"class":178},[59,14707,14709,14711],{"class":61,"line":14708},74,[59,14710,14050],{"class":178},[59,14712,14713],{"class":69},"ReadWriteOnce\n",[59,14715,14717,14720],{"class":61,"line":14716},75,[59,14718,14719],{"class":174},"  resources",[59,14721,196],{"class":178},[59,14723,14725,14728],{"class":61,"line":14724},76,[59,14726,14727],{"class":174},"    requests",[59,14729,196],{"class":178},[59,14731,14733,14736,14738],{"class":61,"line":14732},77,[59,14734,14735],{"class":174},"      storage",[59,14737,179],{"class":178},[59,14739,14740],{"class":69},"1Gi\n",[316,14742,14743],{},"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 .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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}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":55,"searchDepth":77,"depth":77,"links":14745},[14746,14747,14748],{"id":13564,"depth":77,"text":13565},{"id":13611,"depth":77,"text":13578},{"id":14081,"depth":77,"text":13589},{},"device-agent\u002Finstall\u002Fkubernetes.md","\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Fkubernetes",{"title":13556,"description":55},"docs\u002Fdevice-agent\u002Finstall\u002Fkubernetes","15kSBTL-HZ8e0hSX8PLN8uNNtB6pjvSx5-DRWTYVJKw",{"id":14756,"title":14757,"body":14758,"description":14765,"extension":329,"layout":330,"meta":15254,"navGroup":332,"navOrder":156,"navTitle":15255,"navigation":187,"originalPath":15256,"path":15257,"redirect":330,"seo":15258,"stem":15259,"updated":337,"version":338,"__hash__":15260},"docs\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Fmanual.md","Manual Install (NPM)",{"type":7,"value":14759,"toc":15231},[14760,14763,14766,14768,14778,14780,14784,14788,14796,14798,14818,14825,14840,14844,14859,14862,14864,14893,14900,14947,14950,14954,14963,14967,14977,14992,14996,14999,15004,15025,15050,15099,15106,15108,15111,15123,15128,15132,15142,15153,15155,15173,15180,15195,15202,15228],[10,14761,14757],{"id":14762},"manual-install-npm",[14,14764,14765],{},"Use this method if you want direct control over the Node.js runtime and filesystem.",[23,14767,26],{"id":25},[28,14769,14770,14773,14775],{},[31,14771,14772],{},"Node.js 18 or later installed",[31,14774,12213],{},[31,14776,14777],{},"Outbound network access to FlowFuse platform and the NPM registry",[104,14779,12223],{"id":12222},[14,14781,12226,14782,273],{},[41,14783,12223],{"href":12229},[23,14785,14787],{"id":14786},"install-the-device-agent","Install the Device Agent",[14,14789,14790,14791,273],{},"The Device Agent is published to npm as ",[41,14792,14795],{"href":14793,"rel":14794},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@flowfuse\u002Fdevice-agent",[831],"@flowfuse\u002Fdevice-agent",[104,14797,12257],{"id":12256},[50,14799,14801],{"className":52,"code":14800,"language":54,"meta":55,"style":55},"sudo npm install -g @flowfuse\u002Fdevice-agent\n",[18,14802,14803],{"__ignoreMap":55},[59,14804,14805,14807,14810,14812,14815],{"class":61,"line":62},[59,14806,9188],{"class":65},[59,14808,14809],{"class":69}," npm",[59,14811,7956],{"class":69},[59,14813,14814],{"class":73}," -g",[59,14816,14817],{"class":69}," @flowfuse\u002Fdevice-agent\n",[104,14819,12335,14820,660],{"id":12334},[8351,14821,14822],{},[41,14823,8359],{"href":8355,"ariaDescribedBy":14824,"dataFootnoteRef":55,"id":8358},[8357],[50,14826,14828],{"className":52,"code":14827,"language":54,"meta":55,"style":55},"npm install -g @flowfuse\u002Fdevice-agent\n",[18,14829,14830],{"__ignoreMap":55},[59,14831,14832,14834,14836,14838],{"class":61,"line":62},[59,14833,7542],{"class":65},[59,14835,7956],{"class":69},[59,14837,14814],{"class":73},[59,14839,14817],{"class":69},[23,14841,14843],{"id":14842},"working-directory","Working directory",[14,14845,14846,14847,14850,14851,14854,14855,14858],{},"By default the agent uses ",[18,14848,14849],{},"\u002Fopt\u002Fflowfuse-device"," (Linux\u002FmacOS) or ",[18,14852,14853],{},"c:\\opt\\flowfuse-device"," (Windows) as its working directory. Override with ",[18,14856,14857],{},"-d\u002F--dir"," if needed.",[14,14860,14861],{},"Ensure the directory exists and is writable by the service user.",[104,14863,12257],{"id":12305},[50,14865,14867],{"className":52,"code":14866,"language":54,"meta":55,"style":55},"sudo mkdir -p \u002Fopt\u002Fflowfuse-device\nsudo chown -R $USER \u002Fopt\u002Fflowfuse-device\n",[18,14868,14869,14880],{"__ignoreMap":55},[59,14870,14871,14873,14875,14877],{"class":61,"line":62},[59,14872,9188],{"class":65},[59,14874,9191],{"class":69},[59,14876,8441],{"class":73},[59,14878,14879],{"class":69}," \u002Fopt\u002Fflowfuse-device\n",[59,14881,14882,14884,14886,14888,14890],{"class":61,"line":77},[59,14883,9188],{"class":65},[59,14885,9201],{"class":69},[59,14887,9284],{"class":73},[59,14889,9204],{"class":178},[59,14891,14892],{"class":69},"\u002Fopt\u002Fflowfuse-device\n",[104,14894,12335,14895,660],{"id":12393},[8351,14896,14897],{},[41,14898,8359],{"href":8355,"ariaDescribedBy":14899,"dataFootnoteRef":55,"id":12399},[8357],[50,14901,14903],{"className":52,"code":14902,"language":54,"meta":55,"style":55},"mkdir c:\\opt\\flowfuse-device\nicacls c:\\opt\\flowfuse-device \u002Fgrant \"user\":F \u002FT\n",[18,14904,14905,14922],{"__ignoreMap":55},[59,14906,14907,14909,14911,14914,14917,14919],{"class":61,"line":62},[59,14908,9220],{"class":65},[59,14910,9223],{"class":69},[59,14912,14913],{"class":73},"\\o",[59,14915,14916],{"class":69},"pt",[59,14918,9226],{"class":73},[59,14920,14921],{"class":69},"lowfuse-device\n",[59,14923,14924,14927,14929,14931,14933,14935,14938,14941,14944],{"class":61,"line":77},[59,14925,14926],{"class":65},"icacls",[59,14928,9223],{"class":69},[59,14930,14913],{"class":73},[59,14932,14916],{"class":69},[59,14934,9226],{"class":73},[59,14936,14937],{"class":69},"lowfuse-device",[59,14939,14940],{"class":69}," \u002Fgrant",[59,14942,14943],{"class":69}," \"user\":F",[59,14945,14946],{"class":69}," \u002FT\n",[14,14948,14949],{},"Where \"user\" is the service account that will run the device agent (ideally, not an admin account).",[23,14951,14953],{"id":14952},"configuration","Configuration",[14,14955,14956,14957,14959,14960,14962],{},"Place a ",[18,14958,20],{}," in the working directory. See ",[41,14961,44],{"href":43}," for obtaining the configuration via Quick Connect or provisioning.",[23,14964,14966],{"id":14965},"listen-port","Listen Port",[14,14968,14969,14970,14973,14974,3939],{},"Node-RED listens on port ",[18,14971,14972],{},"1880"," by default. Change with ",[18,14975,14976],{},"-p\u002F--port",[50,14978,14980],{"className":52,"code":14979,"language":54,"meta":55,"style":55},"flowfuse-device-agent --port 1881\n",[18,14981,14982],{"__ignoreMap":55},[59,14983,14984,14987,14989],{"class":61,"line":62},[59,14985,14986],{"class":65},"flowfuse-device-agent",[59,14988,12516],{"class":73},[59,14990,14991],{"class":73}," 1881\n",[23,14993,14995],{"id":14994},"start-on-system-boot","Start on system boot",[14,14997,14998],{},"Use the provided systemd service file on Linux to run the agent as a service.",[398,15000,15001],{},[31,15002,15003],{},"Download the service file:",[50,15005,15007],{"className":52,"code":15006,"language":54,"meta":55,"style":55},"curl -L https:\u002F\u002Fraw.githubusercontent.com\u002FFlowFuse\u002Fdevice-agent\u002Fmain\u002Fservice\u002Fflowfuse-device.service -o flowfuse-device.service\n",[18,15008,15009],{"__ignoreMap":55},[59,15010,15011,15013,15016,15019,15022],{"class":61,"line":62},[59,15012,1381],{"class":65},[59,15014,15015],{"class":73}," -L",[59,15017,15018],{"class":69}," https:\u002F\u002Fraw.githubusercontent.com\u002FFlowFuse\u002Fdevice-agent\u002Fmain\u002Fservice\u002Fflowfuse-device.service",[59,15020,15021],{"class":73}," -o",[59,15023,15024],{"class":69}," flowfuse-device.service\n",[398,15026,15027,15040,15047],{"start":77},[31,15028,15029,15030,3012,15033,15036,15037,14858],{},"Adjust ",[18,15031,15032],{},"User",[18,15034,15035],{},"Group",", and ",[18,15038,15039],{},"WorkingDirectory",[31,15041,15042,15043,15046],{},"Update ",[18,15044,15045],{},"ExecStart"," to include a custom port if required.",[31,15048,15049],{},"Move the file into place and enable the service:",[50,15051,15053],{"className":52,"code":15052,"language":54,"meta":55,"style":55},"sudo mv flowfuse-device.service \u002Fetc\u002Fsystemd\u002Fsystem\u002F\nsudo systemctl daemon-reload\nsudo systemctl enable flowfuse-device\nsudo systemctl start flowfuse-device\n",[18,15054,15055,15068,15077,15089],{"__ignoreMap":55},[59,15056,15057,15059,15062,15065],{"class":61,"line":62},[59,15058,9188],{"class":65},[59,15060,15061],{"class":69}," mv",[59,15063,15064],{"class":69}," flowfuse-device.service",[59,15066,15067],{"class":69}," \u002Fetc\u002Fsystemd\u002Fsystem\u002F\n",[59,15069,15070,15072,15074],{"class":61,"line":77},[59,15071,9188],{"class":65},[59,15073,12651],{"class":69},[59,15075,15076],{"class":69}," daemon-reload\n",[59,15078,15079,15081,15083,15086],{"class":61,"line":88},[59,15080,9188],{"class":65},[59,15082,12651],{"class":69},[59,15084,15085],{"class":69}," enable",[59,15087,15088],{"class":69}," flowfuse-device\n",[59,15090,15091,15093,15095,15097],{"class":61,"line":99},[59,15092,9188],{"class":65},[59,15094,12651],{"class":69},[59,15096,12654],{"class":69},[59,15098,15088],{"class":69},[14,15100,15101,15102,15105],{},"On Windows or macOS, consider using the ",[41,15103,15104],{"href":13317},"Installer"," to set up services automatically.",[23,15107,266],{"id":265},[14,15109,15110],{},"Start the agent from a terminal to confirm it runs:",[50,15112,15114],{"className":52,"code":15113,"language":54,"meta":55,"style":55},"flowfuse-device-agent -v\n",[18,15115,15116],{"__ignoreMap":55},[59,15117,15118,15120],{"class":61,"line":62},[59,15119,14986],{"class":65},[59,15121,15122],{"class":73}," -v\n",[14,15124,15125,15126,273],{},"Once assigned, access the Node-RED editor at ",[18,15127,272],{},[23,15129,15131],{"id":15130},"upgrading-the-agent","Upgrading the agent",[14,15133,15134,15135,15138,15139,3939],{},"With Device Agent 1.13+, the package moved from the ",[18,15136,15137],{},"@flowforge"," scope to ",[18,15140,15141],{},"@flowfuse",[28,15143,15144],{},[31,15145,15146,15147,15150,15151],{},"npm: ",[18,15148,15149],{},"@flowforge\u002Fflowforge-device-agent"," ➜ ",[18,15152,14795],{},[104,15154,12257],{"id":12381},[50,15156,15158],{"className":52,"code":15157,"language":54,"meta":55,"style":55},"sudo npm install -g @flowfuse\u002Fdevice-agent@latest\n",[18,15159,15160],{"__ignoreMap":55},[59,15161,15162,15164,15166,15168,15170],{"class":61,"line":62},[59,15163,9188],{"class":65},[59,15165,14809],{"class":69},[59,15167,7956],{"class":69},[59,15169,14814],{"class":73},[59,15171,15172],{"class":69}," @flowfuse\u002Fdevice-agent@latest\n",[104,15174,12335,15175,660],{"id":12440},[8351,15176,15177],{},[41,15178,8359],{"href":8355,"ariaDescribedBy":15179,"dataFootnoteRef":55,"id":12446},[8357],[50,15181,15183],{"className":52,"code":15182,"language":54,"meta":55,"style":55},"npm install -g @flowfuse\u002Fdevice-agent@latest\n",[18,15184,15185],{"__ignoreMap":55},[59,15186,15187,15189,15191,15193],{"class":61,"line":62},[59,15188,7542],{"class":65},[59,15190,7956],{"class":69},[59,15192,14814],{"class":73},[59,15194,15172],{"class":69},[14,15196,15197,15198,15201],{},"If you must stay on 2.x, use ",[18,15199,15200],{},"@2.x",". Device Agent 3.x requires Node.js 18+.",[8989,15203,15205,15208],{"className":15204,"dataFootnotes":55},[8992],[23,15206,8997],{"className":15207,"id":8357},[8996],[398,15209,15210],{},[31,15211,13259,15212,15214,15215,3497,15218,3497,15223],{"id":9002},[18,15213,13262],{}," to launch an elevated command prompt (e.g. as an admin user) ",[41,15216,9013],{"href":9009,"ariaLabel":9010,"className":15217,"dataFootnoteBackref":55},[9012],[41,15219,9013,15221],{"href":13269,"ariaLabel":13270,"className":15220,"dataFootnoteBackref":55},[9012],[8351,15222,3158],{},[41,15224,9013,15226],{"href":13276,"ariaLabel":13277,"className":15225,"dataFootnoteBackref":55},[9012],[8351,15227,9921],{},[316,15229,15230],{},"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 .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 .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":55,"searchDepth":77,"depth":77,"links":15232},[15233,15236,15241,15245,15246,15247,15248,15249,15253],{"id":25,"depth":77,"text":26,"children":15234},[15235],{"id":12222,"depth":88,"text":12223},{"id":14786,"depth":77,"text":14787,"children":15237},[15238,15239],{"id":12256,"depth":88,"text":12257},{"id":12334,"depth":88,"text":15240},"Windows (run elevated1)",{"id":14842,"depth":77,"text":14843,"children":15242},[15243,15244],{"id":12305,"depth":88,"text":12257},{"id":12393,"depth":88,"text":15240},{"id":14952,"depth":77,"text":14953},{"id":14965,"depth":77,"text":14966},{"id":14994,"depth":77,"text":14995},{"id":265,"depth":77,"text":266},{"id":15130,"depth":77,"text":15131,"children":15250},[15251,15252],{"id":12381,"depth":88,"text":12257},{"id":12440,"depth":88,"text":15240},{"id":8357,"depth":77,"text":8997},{},"Manual Install with NPM","device-agent\u002Finstall\u002Fmanual.md","\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Fmanual",{"title":14757,"description":14765},"docs\u002Fdevice-agent\u002Finstall\u002Fmanual","hzkRc5NjCLr8w-RKr3nahAL34hBQEUn2RjW8R8JIEOE",{"id":15262,"title":15263,"body":15264,"description":55,"extension":329,"layout":330,"meta":15464,"navGroup":332,"navOrder":62,"navTitle":15465,"navigation":187,"originalPath":15466,"path":11871,"redirect":330,"seo":15467,"stem":15468,"updated":337,"version":338,"__hash__":15469},"docs\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Foverview.md","Installing Device Agent",{"type":7,"value":15265,"toc":15456},[15266,15269,15273,15282,15334,15336,15359,15364,15366,15369,15372,15384,15387,15394,15399,15403,15406,15430,15434],[10,15267,15263],{"id":15268},"installing-device-agent",[23,15270,15272],{"id":15271},"choose-your-install-path","Choose your install path",[14,15274,15275,15278,15279,15281],{},[364,15276,15277],{},"Recommended for most users:"," Use the Device Agent Installer (Quick Start), the fastest way to deploy with minimal configuration. Power users can choose Manual (using ",[18,15280,7542],{},"), Docker, or Kubernetes deployments.",[28,15283,15284,15301,15313,15323],{},[31,15285,15286,15287],{},"Recommended: Use the Device Agent Installer\n",[28,15288,15289,15295],{},[31,15290,15291,15292],{},"Fastest way to get started with a one-line command in the ",[41,15293,12244],{"href":15294},"\u002Fdocs\u002Fdevice-agent\u002Fquickstart",[31,15296,15297,15298],{},"Full options and service management in the ",[41,15299,15300],{"href":13317},"Installer reference",[31,15302,15303,15304,5041,15306],{},"Alternative: Manual install (using ",[18,15305,7542],{},[28,15307,15308],{},[31,15309,15310,15311],{},"Install the npm package, set working directory, configure, and run as a service. See ",[41,15312,9558],{"href":15257},[31,15314,15315,15316],{},"Alternative: Docker \u002F Docker Compose\n",[28,15317,15318],{},[31,15319,15320,15321],{},"Run the agent in a container; bind-mount the configuration. See ",[41,15322,9554],{"href":334},[31,15324,15325,15326],{},"Alternative: Kubernetes\n",[28,15327,15328],{},[31,15329,15330,15331],{},"Deploy the agent in a Kubernetes cluster. See ",[41,15332,15333],{"href":14751},"Kubernetes install",[23,15335,26],{"id":25},[28,15337,15338,15341,15344,15353],{},[31,15339,15340],{},"Node.js 18 or later (for Manual install and for running locally)",[31,15342,15343],{},"Supported OS: Linux, macOS, Windows, or Docker container",[31,15345,15346,15347],{},"Networking: allow outbound access on 443 to:\n",[28,15348,15349,15351],{},[31,15350,301],{},[31,15352,305],{},[31,15354,15355,15356],{},"Access to npm registry when snapshots are installed: ",[41,15357,309],{"href":309,"rel":15358},[831],[14,15360,15361,15362,273],{},"Note: The Device Agent downloads the required Node-RED version and any nodes specified by the assigned snapshot. Ensure firewall\u002Fproxy permits access to the npm registry or see ",[41,15363,314],{"href":313},[104,15365,12223],{"id":12222},[14,15367,15368],{},"If you're working behind a firewall, and need to configure it to allow the Device Agent to connect to FlowFuse and the npm registry, see the following:",[14,15370,15371],{},"Allow outbound TCP 443 to:",[28,15373,15374,15376,15378,15381],{},[31,15375,301],{},[31,15377,305],{},[31,15379,15380],{},"registry.flowfuse.cloud",[31,15382,15383],{},"registry.flowfuse.com",[14,15385,15386],{},"Ensure access to npm registry to download Node-RED and nodes:",[28,15388,15389],{},[31,15390,15391],{},[41,15392,309],{"href":309,"rel":15393},[831],[14,15395,15396,15397,273],{},"For offline environments, see ",[41,15398,314],{"href":313},[23,15400,15402],{"id":15401},"verify-the-installation","Verify the installation",[14,15404,15405],{},"After installing by any method:",[398,15407,15408,15416,15424],{},[31,15409,15410,15411,1706,15413,15415],{},"Ensure a working directory exists (default is ",[18,15412,14849],{},[18,15414,14853],{},").",[31,15417,15418,15419,15421,15422,273],{},"Provide a device configuration (via Quick Connect, provisioning, or manual ",[18,15420,20],{},"). See ",[41,15423,44],{"href":43},[31,15425,15426,15427,15429],{},"Start the agent (service or CLI) and open ",[18,15428,272],{}," when assigned and running.",[23,15431,15433],{"id":15432},"whats-next","What’s next",[28,15435,15436,15442,15448],{},[31,15437,15438,15439,15441],{},"Follow the ",[41,15440,12244],{"href":15294}," to add and connect a Remote Instance",[31,15443,15444,15445],{},"Learn ",[41,15446,15447],{"href":11887},"how to run and configure the agent",[31,15449,15450,15451,15455],{},"Use ",[41,15452,15454],{"href":15453},"\u002Fdocs\u002Fuser\u002Fdevops-pipelines.md","DevOps Pipelines"," to deploy flows",{"title":55,"searchDepth":77,"depth":77,"links":15457},[15458,15459,15462,15463],{"id":15271,"depth":77,"text":15272},{"id":25,"depth":77,"text":26,"children":15460},[15461],{"id":12222,"depth":88,"text":12223},{"id":15401,"depth":77,"text":15402},{"id":15432,"depth":77,"text":15433},{},"Overview","device-agent\u002Finstall\u002Foverview.md",{"title":15263,"description":55},"docs\u002Fdevice-agent\u002Finstall\u002Foverview","_-6AbuRm_3vDW4V04XGuFEIQ3w4ey0-FOejSPuZQFDU",{"id":15471,"title":15472,"body":15473,"description":15485,"extension":329,"layout":330,"meta":15537,"navGroup":330,"navOrder":62,"navTitle":15538,"navigation":187,"originalPath":15539,"path":12183,"redirect":330,"seo":15540,"stem":15541,"updated":337,"version":338,"__hash__":15542},"docs\u002Fdocs\u002Fdevice-agent\u002Fintroduction.md","FlowFuse Device Agent",{"type":7,"value":15474,"toc":15535},[15475,15477,15486,15489,15492,15497,15500],[10,15476,15472],{"id":14986},[14,15478,15479,15483],{},[638,15480],{"alt":15481,"dataZoomable":55,"src":15482},"FlowFuse Device Agent Workflow","\u002Fdocs\u002Fdevice-agent\u002Fimages\u002Fdevice-agent-workflow.png",[1160,15484,15485],{},"Workflow to onboard new Remote Instances through the FlowFuse Platform and Device Agent",[14,15487,15488],{},"The FlowFuse platform can be used to manage Node-RED Remote Instances running on remote hardware, e.g. devices on your factory floor.",[14,15490,15491],{},"By installing the FlowFuse Device Agent, you can securely connect your hardware to FlowFuse in order to manage and deploy Node-RED flows remotely.",[14,15493,15494,15496],{},[364,15495,1798],{}," The FlowFuse Device Agent will install Node-RED when the agent receives a snapshot to run from FlowFuse.",[14,15498,15499],{},"In order to connect your device to FlowFuse, and to allow FlowFuse to manage it, you'll need to do the following steps:",[28,15501,15502,15508,15515,15522,15528],{},[31,15503,15504,15507],{},[41,15505,15506],{"href":12243},"Quick Start Guide"," - Install on a device and remotely edit through FlowFuse Cloud.",[31,15509,15510,15514],{},[41,15511,15513],{"href":15512},"\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Foverview.md","Install the FlowFuse Device Agent"," - Install the agent directly onto your device.",[31,15516,15517,15521],{},[41,15518,15520],{"href":15519},"\u002Fdocs\u002Fdevice-agent\u002Fregister.md","Register your Device"," - Let FlowFuse know your device has been setup with the Device Agent.",[31,15523,15524,15527],{},[41,15525,11888],{"href":15526},"\u002Fdocs\u002Fdevice-agent\u002Frunning.md"," - Run the agent on your device, this will connect to FlowFuse and wait for instruction on which Node-RED flows to run.",[31,15529,15530,15534],{},[41,15531,15533],{"href":15532},"\u002Fdocs\u002Fdevice-agent\u002Fdeploy.md","Deploy Flows to your Device"," - With the above steps completed, you can now run Node-RED flows directly on your device, and have them managed by FlowFuse remotely.",{"title":55,"searchDepth":77,"depth":77,"links":15536},[],{},"FlowFuse Device Agent Introduction","device-agent\u002Fintroduction.md",{"title":15472,"description":15485},"docs\u002Fdevice-agent\u002Fintroduction","u5rZfIe-Cs-o-p6XdXSNXMktX2ynjKLAXEUWmHsaYzs",{"id":15544,"title":15545,"body":15546,"description":15553,"extension":329,"layout":330,"meta":15860,"navGroup":330,"navOrder":77,"navTitle":15861,"navigation":187,"originalPath":15862,"path":15294,"redirect":330,"seo":15863,"stem":15864,"updated":337,"version":338,"__hash__":15865},"docs\u002Fdocs\u002Fdevice-agent\u002Fquickstart.md","Quick Start Guide: Device Agent",{"type":7,"value":15547,"toc":15847},[15548,15551,15554,15560,15564,15567,15576,15580,15584,15649,15653,15681,15684,15688,15691,15711,15714,15775,15778,15788,15800,15804,15807,15830],[10,15549,15545],{"id":15550},"quick-start-guide-device-agent",[14,15552,15553],{},"This guide will walk you through the process of adding a device to FlowFuse, connecting it to the platform, and deploying your Node-RED flows remotely. FlowFuse's Device Agent helps unlock the power of your devices by allowing you to manage and deploy Node-RED flows running on those devices securely and remotely.",[14,15555,15556,15558],{},[638,15557],{"alt":15481,"src":15482,"dataZoomable":55},[1160,15559,15485],{},[23,15561,15563],{"id":15562},"video-walkthrough","Video Walkthrough",[14,15565,15566],{},"This video will walk you through every step from creating a remote instance, installing the Device Agent, and connecting the two. It is focused on the Free tier, but applies to all users.",[15568,15569],"iframe",{"width":15570,"height":15571,"src":15572,"title":15573,"frameBorder":3171,"allow":15574,"referrerPolicy":15575,"allowFullScreen":187},560,315,"https:\u002F\u002Fwww.youtube.com\u002Fembed\u002FJFY0s8X5RVo?si=MDgOaO8Iqzmc-KCk","YouTube video player","accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share","strict-origin-when-cross-origin",[23,15577,15579],{"id":15578},"setup-installation","Setup & Installation",[104,15581,15583],{"id":15582},"step-1-register-a-remote-instance-on-flowfuse-platform","Step 1: Register a Remote Instance (On FlowFuse Platform)",[398,15585,15586,15589,15594,15600,15623,15628,15646],{},[31,15587,15588],{},"Open your web browser and go to your FlowFuse application page.",[31,15590,15591,15592,1497],{},"Navigate to the ",[364,15593,12034],{},[31,15595,15596,15597],{},"Click ",[364,15598,15599],{},"Add Remote Instance.",[31,15601,15602,15603,15606,15607,15610,15611,15614,15616,15620,15621],{},"Fill in the ",[364,15604,15605],{},"Name"," & ",[364,15608,15609],{},"Type"," and select an ",[364,15612,15613],{},"Application",[662,15615],{},[638,15617],{"alt":15618,"src":15619,"dataZoomable":55},"Screenshot of the dialog form to fill out when registering a Device","\u002Fdocs\u002Fdevice-agent\u002Fimages\u002Fadd_remote_instance.png","{ style=\"max-width: 600px\"; }\n",[1160,15622,15618],{},[31,15624,15596,15625],{},[364,15626,15627],{},"Add",[31,15629,15630,15631,15634,15635,15638,15640,15644],{},"You will be presented with a ",[364,15632,15633],{},"Device Configuration"," that you will need in the next step. ",[1160,15636,15637],{},"(Do not close this window just yet.)",[662,15639],{},[638,15641],{"alt":15642,"src":15643,"dataZoomable":55},"Screenshot of the dialog shown to a user when a Device is registered","\u002Fdocs\u002Fdevice-agent\u002Fimages\u002Fconfig_yml2a.png",[1160,15645,15642],{},[31,15647,15648],{},"Copy the installation command. You will need it in next step.",[104,15650,15652],{"id":15651},"step-2-install-and-configure-the-device-agent-on-device","Step 2. Install and Configure the Device Agent (On Device)",[398,15654,15655,15664,15667,15670],{},[31,15656,15657,15658,15663],{},"Open a Terminal\u002FWindows Command Prompt (run elevated",[8351,15659,15660],{},[41,15661,8359],{"href":8355,"ariaDescribedBy":15662,"dataFootnoteRef":55,"id":8358},[8357],") on the Device.",[31,15665,15666],{},"Paste the command you copied from the previous step and execute it.",[31,15668,15669],{},"Answer prompts as needed.",[31,15671,15672,15673,15675,15679],{},"Installer will set up the runtime and configure the Device Agent, and summarize on successful completion.",[662,15674],{},[638,15676],{"alt":15677,"src":15678,"dataZoomable":55},"Example output in a Terminal showing a device agent successfully installed","\u002Fdocs\u002Fdevice-agent\u002Fimages\u002Finstaller_output.png",[1160,15680,15677],{},[14,15682,15683],{},"Your device is now connected to FlowFuse and ready to be used.",[23,15685,15687],{"id":15686},"deploy-flows-to-remote-instances","Deploy Flows to Remote Instances",[14,15689,15690],{},"There are two approaches to deploying flows to your Remote Instances.",[28,15692,15693,15699],{},[31,15694,15695,15698],{},[364,15696,15697],{},"Developer Mode",": This mode allows you to edit and deploy flows directly from the FlowFuse platform.",[31,15700,15701,15703,15704,15706,15707,15710],{},[364,15702,15454],{},": FlowFuse provides ",[41,15705,15454],{"href":15453}," as a way of pushing flows from one Hosted Instance\u002FRemote Instance to another (or several in the case of ",[41,15708,932],{"href":15709},"\u002Fdocs\u002Fuser\u002Fdevice-groups.md","). This is the recommended approach if you're pushing from development environments (e.g. remote test instances) out to remote production instances.",[104,15712,15697],{"id":15713},"developer-mode",[398,15715,15716,15723,15729,15736,15750,15761],{},[31,15717,15718,15719,15722],{},"Navigate to ",[364,15720,15721],{},"Applications"," and select the application your device was added to.",[31,15724,15725,15726,15728],{},"Go to the ",[364,15727,12034],{}," tab within the application.",[31,15730,15731,15732,15735],{},"Locate your newly added device and ",[364,15733,15734],{},"click"," on your Remote Instance.",[31,15737,15738,15739,15741,15742,15744,15748],{},"Then Click ",[364,15740,15697],{}," toggle button on upper right.",[662,15743],{},[638,15745],{"alt":15746,"src":15747,"dataZoomable":55},"The \"Developer Mode\" toggle button available on the Device screen","\u002Fdocs\u002Fdevice-agent\u002Fimages\u002Fdeveloper.png",[1160,15749,15746],{},[31,15751,15752,15753,15755,15759],{},"This will enable editor access for your device.",[662,15754],{},[638,15756],{"alt":15757,"src":15758,"dataZoomable":55},"The \"Device Editor\" button available on the Device screen","\u002Fdocs\u002Fdevice-agent\u002Fimages\u002FeditorEnabled.png",[1160,15760,15757],{},[31,15762,15763,15764,15766,15767,15769,15773],{},"Clicking ",[364,15765,10423],{}," will launch the editor.",[662,15768],{},[638,15770],{"alt":15771,"src":15772,"dataZoomable":55},"Screenshot of a Node-RED Editor for a Device","\u002Fdocs\u002Fdevice-agent\u002Fimages\u002Fnr_editor.png",[1160,15774,15771],{},[104,15776,15454],{"id":15777},"devops-pipelines",[14,15779,15780,15785,15786],{},[638,15781],{"alt":15782,"src":15783,"width":15784},"Screenshot showing the user interface for creating and running DevOps Pipelines in FlowFuse","\u002Fdocs\u002Fdevice-agent\u002Fimages\u002Fui-devops-pipelines.png",750,"{data-zoomable}\n",[1160,15787,15782],{},[14,15789,15790,15791,3601,15794,15797,15798,273],{},"To work with Pipelines, you need at least one other Hosted Instance or Remote Device to push ",[1160,15792,15793],{},"from",[1160,15795,15796],{},"to",". You can follow the instructions on setting up a Pipeline and deploying your flows between Hosted Instances\u002FRemote Instances ",[41,15799,15454],{"href":15453},[23,15801,15803],{"id":15802},"next-steps","Next Steps",[14,15805,15806],{},"Now you have a device connected to the platform, there are many features you can use to manage and monitor your Remote Instances.\nHere are a few to get you started:",[28,15808,15809,15813,15819,15824],{},[31,15810,15811],{},[41,15812,11991],{"href":949},[31,15814,15815],{},[41,15816,15818],{"href":15817},"\u002Fdocs\u002Fuser\u002Fdevops-pipelines","Pipelines",[31,15820,15821],{},[41,15822,11824],{"href":15823},"\u002Fdocs\u002Fuser\u002Fenvvar",[31,15825,15826],{},[41,15827,15829],{"href":15828},"\u002Fdocs\u002Fuser\u002Flogs","Logs",[8989,15831,15833,15836],{"className":15832,"dataFootnotes":55},[8992],[23,15834,8997],{"className":15835,"id":8357},[8996],[398,15837,15838],{},[31,15839,13259,15840,15843,15844],{"id":9002},[18,15841,15842],{},"powershell -Command \"Start-Process 'cmd' -Verb runAs"," to launching an elevated command prompt window (e.g. as an admin user) ",[41,15845,9013],{"href":9009,"ariaLabel":9010,"className":15846,"dataFootnoteBackref":55},[9012],{"title":55,"searchDepth":77,"depth":77,"links":15848},[15849,15850,15854,15858,15859],{"id":15562,"depth":77,"text":15563},{"id":15578,"depth":77,"text":15579,"children":15851},[15852,15853],{"id":15582,"depth":88,"text":15583},{"id":15651,"depth":88,"text":15652},{"id":15686,"depth":77,"text":15687,"children":15855},[15856,15857],{"id":15713,"depth":88,"text":15697},{"id":15777,"depth":88,"text":15454},{"id":15802,"depth":77,"text":15803},{"id":8357,"depth":77,"text":8997},{},"Quick Start","device-agent\u002Fquickstart.md",{"title":15545,"description":15553},"docs\u002Fdevice-agent\u002Fquickstart","EEGA6fEi2RXVIIniJGg_Z6ma9Siy_6PPn0HZkTZOMsQ",{"id":15867,"title":44,"body":15868,"description":15875,"extension":329,"layout":330,"meta":17021,"navGroup":330,"navOrder":99,"navTitle":44,"navigation":187,"originalPath":17022,"path":43,"redirect":330,"seo":17023,"stem":17024,"updated":337,"version":338,"__hash__":17025},"docs\u002Fdocs\u002Fdevice-agent\u002Fregister.md",{"type":7,"value":15869,"toc":16991},[15870,15873,15876,15887,15890,15910,15914,15917,15921,15968,15972,15979,15983,15991,15995,15998,16002,16018,16021,16024,16031,16040,16044,16098,16104,16110,16115,16125,16135,16139,16143,16152,16155,16197,16201,16204,16207,16210,16213,16225,16230,16237,16240,16252,16255,16259,16266,16269,16279,16292,16298,16307,16310,16314,16327,16331,16340,16343,16354,16358,16376,16380,16383,16400,16403,16407,16410,16431,16435,16458,16461,16465,16468,16483,16487,16504,16513,16517,16520,16534,16537,16541,16544,16563,16566,16587,16591,16594,16619,16622,16641,16644,16647,16665,16672,16676,16679,16695,16698,16702,16705,16709,16714,16731,16743,16756,16798,16804,16808,16811,16816,16830,16833,16886,16892,16897,16900,16903,16910,16915,16955,16958,16973,16976,16978,16981,16988],[10,15871,44],{"id":15872},"register-your-remote-instance",[14,15874,15875],{},"To connect your hardware to FlowFuse, you will need to:",[398,15877,15878,15881,15884],{},[31,15879,15880],{},"Install the Device Agent on your hardware",[31,15882,15883],{},"Add a \"Remote Instance\" to FlowFuse, via the FlowFuse UI",[31,15885,15886],{},"Connect your hardware to FlowFuse by configuring the Device Agent",[14,15888,15889],{},"The best configuration to use will depend on how many Remote Instances you want to connect:",[28,15891,15892,15901],{},[31,15893,15894,15900],{},[364,15895,15896],{},[41,15897,15899],{"href":15898},"#single-remote-instance-registration","Single Instance Registration",": for connecting a single Remote Instance, or a small number of Remote Instances, to the platform.",[31,15902,15903,15909],{},[364,15904,15905],{},[41,15906,15908],{"href":15907},"#bulk-registration","Bulk Registration",": for setting up one or more Remote Instances which will automatically register themselves to the platform when the device agent is run.",[23,15911,15913],{"id":15912},"single-remote-instance-registration","Single Remote Instance Registration",[14,15915,15916],{},"For a single Remote Instance, or small batch of Remote Instances, you can manually register each Remote Instance individually, naming each Remote Instance and assigning it to an application.",[104,15918,15920],{"id":15919},"add-remote-instance","Add Remote Instance",[398,15922,15923,15928,15934,15964],{},[31,15924,15925,15926,4576],{},"Go to your teams's ",[364,15927,12034],{},[31,15929,15930,15931,15933],{},"Click the ",[364,15932,15920],{}," button.",[31,15935,15936,15937,15939,15940,15942,15943,15945,15946],{},"You will be prompted to give the Remote Instance a ",[364,15938,15605],{},", an optional ",[364,15941,15609],{}," and to chose which ",[364,15944,15613],{},", if any, the Instance should be assigned to.\n",[28,15947,15948,15953,15958],{},[31,15949,15950],{},[638,15951],{"src":15619,"width":15952},500,[31,15954,372,15955,15957],{},[364,15956,15609],{}," field can be used to record additional meta information about the Remote Instance.",[31,15959,15960,15961,15963],{},"If you do not wish to assign the Remote Instance to an ",[364,15962,15613],{}," at this time, you can do so later.",[31,15965,15596,15966],{},[364,15967,15627],{},[104,15969,15971],{"id":15970},"connect-hardware-to-flowfuse","Connect Hardware to FlowFuse",[14,15973,15974,15975,15978],{},"Once the Remote Instance has been added in the FlowFuse UI, you will be shown the ",[364,15976,15977],{},"Device Agent Configuration"," dialog which\ncontains all the information needed to connect your hardware to the FlowFuse platform.",[768,15980,15982],{"id":15981},"quick-connect","Quick Connect",[14,15984,15985,15986,15990],{},"By default, you are offered the ",[41,15987,15989],{"href":15988},"#quick-connect","Setup command"," method that was introduced in FlowFuse V2.1, and provides a one-time passcode to automatically connect the hardware to FlowFuse.",[638,15992],{"src":15993,"width":15994},"\u002Fdocs\u002Fdevice-agent\u002Fimages\u002Fconfig_yml1.png","500px",[14,15996,15997],{},"Running this command on hardware with the Device Agent installed will automatically configure it and connect it to FlowFuse.",[768,15999,16001],{"id":16000},"manual-setup","Manual Setup",[14,16003,16004,16005,16007,16008,16012,16013,16017],{},"For older versions of the device agent, you can expand the the ",[364,16006,16001],{}," section\nand use the configuration data with the ",[41,16009,16011],{"href":16010},"#device-agent-web-ui","Device Agent Web UI"," or the ",[41,16014,16016],{"href":16015},"#manual-download","Manual Download"," methods instead.",[14,16019,16020],{},"Repeat these steps for each Remote Instance you want to connect to the platform.",[23,16022,15908],{"id":16023},"bulk-registration",[14,16025,16026,16027,16030],{},"If you have dozens, or hundreds of pieces of hardware to connect, you can use the ",[364,16028,16029],{},"Device Provisioning Configuration"," method.",[14,16032,16033,16034,16036,16037,16030],{},"This approach provides you with a single \"Provisioning Token\" for all of your Remote Instances. When passed to the ",[18,16035,14986],{},", this token will automatically register your Remote Instances with the relevant instance or application. There is no need to \"Add Remote Instance\" each time, as is the case with the ",[41,16038,16039],{"href":15898},"Single Device Registration",[104,16041,16043],{"id":16042},"generating-a-device-provisioning-configuration","Generating a \"Device Provisioning Configuration\"",[398,16045,16046,16052,16059,16064,16070,16093],{},[31,16047,16048,16049,4576],{},"Go to your ",[364,16050,16051],{},"Team Settings",[31,16053,16054,16055,16058],{},"Open the ",[364,16056,16057],{},"Provisioning"," tab.",[31,16060,15930,16061,15933],{},[364,16062,16063],{},"Add Token",[31,16065,16066,16067],{},"Enter a value for ",[364,16068,16069],{},"Token Name",[31,16071,16072,16073,16076,16077,16079,16080],{},"Optionally, chose whether the device should Auto Assign to an ",[364,16074,16075],{},"Instance",", an ",[364,16078,15613],{},", or be left unassigned.\n",[28,16081,16082,16088],{},[31,16083,16084,16085,16087],{},"Select an ",[364,16086,16075],{}," if you want the device to be automatically assigned to an instance.",[31,16089,16084,16090,16092],{},[364,16091,15613],{}," if you want the device to be automatically assigned to an application.",[31,16094,15596,16095],{},[364,16096,16097],{},"Create",[14,16099,16100],{},[638,16101],{"alt":16102,"dataZoomable":55,"src":16103},"Screenshot of a FlowFuse Remote Instance Provisioning Token","\u002Fdocs\u002Fdevice-agent\u002Fimages\u002Fcreate-provisioning-token.png",[14,16105,16106,16107,16109],{},"Once the Provisioning Token has been created, you will be shown the\n",[364,16108,16029],{}," dialog:",[14,16111,16112],{},[638,16113],{"alt":16102,"dataZoomable":55,"src":16114},"\u002Fdocs\u002Fdevice-agent\u002Fimages\u002Fprovisioning-token.png",[14,16116,16117,16120,16121,16124],{},[364,16118,16119],{},"IMPORTANT:"," This is the only time the platform will show you\nthis information. Make sure to take a copy or use the ",[364,16122,16123],{},"Download","\nbutton to save the configuration file locally.",[14,16126,16127,16128,16130,16131],{},"Once the token is created, it will be shown in the list of tokens on the ",[364,16129,16057],{}," tab.\n",[638,16132],{"alt":16133,"dataZoomable":55,"src":16134},"Screenshot of FlowFuse Remote Instance Provisioning Tokens","\u002Fdocs\u002Fdevice-agent\u002Fimages\u002Fprovisioning-tokens.png",[23,16136,16138],{"id":16137},"connecting-your-hardware","Connecting your Hardware",[104,16140,16142],{"id":16141},"install-the-configuration","Install the configuration",[14,16144,16145,16146,16148,16149,16151],{},"The Device Agent requires information about the FlowFuse Platform, and how to connect. This comes in the form of a a ",[364,16147,15633],{}," file or a ",[364,16150,16029],{},"\nfile present in its working directory",[14,16153,16154],{},"There are three methods by which you can get this configuration onto your hardware:",[28,16156,16157,16164,16186],{},[31,16158,16159,16163],{},[364,16160,16161,3939],{},[41,16162,15982],{"href":15988}," Copy the Setup Command (with one-time passcode) and run it in a terminal window on the hardware. Your hardware will then automatically configure itself.",[31,16165,16166,16170,16171,16173,16174],{},[364,16167,16168,3939],{},[41,16169,16011],{"href":16010}," Copy the configuration file (",[18,16172,20],{},") to your hardware using its built in Web UI.\n",[28,16175,16176],{},[31,16177,16178],{},[1160,16179,16180,16181,16185],{},"The Device Agent must be running and the ",[41,16182,16184],{"href":16183},"\u002Fdocs\u002Fdevice-agent\u002Frunning#device-agent-command-line-options","command line flag"," for the Web UI must be enabled.",[31,16187,16188,16192,16193,273],{},[364,16189,16190,3939],{},[41,16191,16016],{"href":16015}," Download the configuration file directly into the hardware's ",[41,16194,16196],{"href":16195},"\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Fmanual#working-directory","Working Directory",[104,16198,16200],{"id":16199},"methods","Methods",[768,16202,15982],{"id":16203},"quick-connect-1",[14,16205,16206],{},"The Quick Connect method was introduced in FlowFuse v2.1. This is the fastest way to connect your hardware to the platform.",[14,16208,16209],{},"When registering your hardware you would have been presented the following dialog, with a one-time-passcode that the Device Agent can use to retrieve it's configuration:",[638,16211],{"src":15993,"width":15994,"style":16212},"margin: auto;",[14,16214,16215,16216,16220,16221,16224],{},"If you're no longer able to see that dialog, you can regenerate the configuration by following the ",[41,16217,16219],{"href":16218},"#regenerating-configurations","Regenerating Configurations"," steps, or clicking ",[364,16222,16223],{},"\"Finish Setup\""," on the Remote Instance's page:",[638,16226],{"src":16227,"width":16228,"style":16229},"\u002Fdocs\u002Fdevice-agent\u002Fimages\u002Ffinish-setup.png","750px","margin: auto; margin-bottom: 12px;",[14,16231,16232,16233,16236],{},"When the quick connect command has been run, the terminal window will report that the Remote Instance has connected to the platform and\nwill output a new ",[18,16234,16235],{},"command"," for you to use to start the Remote Instance agent with the new configuration.",[14,16238,16239],{},"NOTES",[28,16241,16242,16249],{},[31,16243,16244,16245,16248],{},"The Setup command is only valid for 24h. If you do not use it within this time, you will need to ",[41,16246,16247],{"href":16218},"regenerate"," it.",[31,16250,16251],{},"The 3 word One-Time-Code (OTC) contained in the Setup command is single use and is deleted immediately upon use.",[768,16253,16011],{"id":16254},"device-agent-web-ui",[638,16256],{"src":16257,"width":16258,"style":16229},"\u002Fdocs\u002Fdevice-agent\u002Fimages\u002Fdevice_gui.png","550px",[14,16260,16261,16262,16265],{},"If the Device Agent is running ",[41,16263,16264],{"href":16183},"with the Web UI enabled",", you can download the\nconfiguration file to the Remote Instance using the Web UI. This is useful if you don't\nhave direct access to the Remote Instance's file system. Once the configuration file is\ndownloaded, the device agent will automatically restart and load the configuration.",[768,16267,16016],{"id":16268},"manual-download",[14,16270,16271,16272,1706,16274,16276,16277],{},"Place the ",[364,16273,15633],{},[364,16275,16029],{}," file onto your hardware.\nin the ",[41,16278,16196],{"href":16195},[14,16280,16281,16282,16284,16285,3497,16288,16291],{},"By default, the device agent expects the configuration file to be named ",[18,16283,20],{},", if not, you will need to start the device agent with the ",[18,16286,16287],{},"-c",[41,16289,16290],{"href":16183},"Command Line Option"," and specify the path of the configuration file.",[14,16293,16294,16295],{},"The agent can then be started with the command: ",[59,16296,16297],{},"^global-install",[50,16299,16301],{"className":52,"code":16300,"language":54,"meta":55,"style":55},"flowfuse-device-agent\n",[18,16302,16303],{"__ignoreMap":55},[59,16304,16305],{"class":61,"line":62},[59,16306,16300],{"class":65},[14,16308,16309],{},"You will see the Device Agent start and perform a 'call-home' where it connects back\nto the platform to check what it should be running.",[768,16311,16313],{"id":16312},"additional-information","Additional Information",[14,16315,16316,16317,16319,16320,16322,16323,16326],{},"If you copy or download a ",[364,16318,16029],{}," file to your hardware,\nyou will see the Device Agent start and perform a 'call-home' where it connects back\nto the platform to auto register itself in the Team's Remote Instances.  If successful,\nthe real ",[364,16321,15633],{}," is generated and downloaded to the device.\nThe original ",[364,16324,16325],{},"Provisioning Configuration"," will be overwritten meaning subsequent\nruns will not need to perform the auto registration again.",[23,16328,16330],{"id":16329},"assign-the-remote-instance","Assign the Remote Instance",[14,16332,16333,16334,16336,16337,16339],{},"The next step is to assign the device to a Node-RED instance or application. Note, that if\nyou've followed ",[41,16335,16039],{"href":15898}," or\n",[41,16338,15908],{"href":15907}," to register your device, it will\nautomatically be assigned to an Application or Instance.",[104,16341,15721],{"id":16342},"applications",[14,16344,16345,16346,16348,16349,16353],{},"This step will permit you to push Snapshots to your Remote Instance via ",[41,16347,15454],{"href":15453},", or via a ",[41,16350,16352],{"href":16351},"\u002Fdocs\u002Fuser\u002Fsnapshots\u002F#application-owned-devices","Target Snapshot"," from the Application.",[768,16355,16357],{"id":16356},"assign-to-application","Assign to Application",[398,16359,16360,16364,16371],{},[31,16361,15925,16362,4576],{},[364,16363,12034],{},[31,16365,16366,16367,16370],{},"Open the dropdown menu to the right of the Remote Instance you want to assign and\nselect the ",[364,16368,16369],{},"Add to Application"," option.",[31,16372,16373,16374,9521],{},"Select the application in the dialog and click ",[364,16375,15627],{},[768,16377,16379],{"id":16378},"remove-from-application","Remove from Application",[14,16381,16382],{},"To remove the Remote Instance from an application:",[398,16384,16385,16389,16394],{},[31,16386,15925,16387,4576],{},[364,16388,12034],{},[31,16390,16391,16392,16370],{},"Open the dropdown menu to the right of the Remote Instance you want to remove and\nselect the ",[364,16393,16379],{},[31,16395,16396,16397,16370],{},"Confirm the action by clicking the ",[364,16398,16399],{},"Remove",[14,16401,16402],{},"The Remote Instance will stop running the current Node-RED flows. It will then wait\nuntil it is assigned to another application or instance.",[768,16404,16406],{"id":16405},"bulk-assigning-remote-instances-to-an-application","Bulk Assigning Remote Instances to an Application",[14,16408,16409],{},"If you have a large number of Remote Instances to assign to an application, you can use do them all at once:",[398,16411,16412,16419,16426],{},[31,16413,16414,16415,16418],{},"Select the Remote Instances you want to assign and open the ",[364,16416,16417],{},"Actions"," dropdown menu.",[31,16420,16421,16422,16425],{},"Select the ",[364,16423,16424],{},"Move to Application"," option and then select the application or instance you want to assign the Remote Instances to.",[31,16427,15596,16428,9521],{},[364,16429,16430],{},"Move",[1146,16432,16434],{"id":16433},"details","Details:",[28,16436,16437,16440],{},[31,16438,16439],{},"Remote Instances that are already assigned to the chosen application will not be changed or updated.",[31,16441,16442,16443],{},"For any Remote Instance that is moved by the operation:\n",[28,16444,16445,16448,16451],{},[31,16446,16447],{},"Remote Instances moved by the operation will have their target snapshot cleared, device group membership cleared and will be sent an update command.",[31,16449,16450],{},"Remote Instances in Fleet Mode will automatically apply the changes resulting in Remote Instances restarting with the basic starter snapshot flows.",[31,16452,16453,16454,16457],{},"Remote Instances in ",[41,16455,15697],{"href":16456},"\u002Fdocs\u002Fdevice-agent\u002Fquickstart\u002F#developer-mode"," will continue to run their current flows until they are switched to fleet mode at which point they will update accordingly.",[14,16459,16460],{},"NOTE: If you wish to keep the flows currently running on the Remote Instance, it must be in developer mode at the time of operation. Once the Remote Instance is moved, create a new snapshot with the \"Set as Target\" option checked.",[104,16462,16464],{"id":16463},"hosted-instances","Hosted Instances",[14,16466,16467],{},"This method establishes a deployment relationship where a Hosted Instance becomes the source for snapshot deployments to your Remote Instance(s).",[14,16469,16470,16473,16474,16478,16479,273],{},[364,16471,16472],{},"Important:"," This is a legacy feature; ",[41,16475,16477],{"href":16476},"\u002Fdocs\u002Fdevice-agent\u002Fregister#applications","assigning to Applications"," is the recommended approach as it provides better fleet management and DevOps Pipeline capabilities. For guidance, see ",[41,16480,16482],{"href":16481},"\u002Fdocs\u002Fuser\u002Fconcepts.md#when-to-use-instance-assignment-vs-devops-pipelines","when to use each approach",[768,16484,16486],{"id":16485},"assign-to-hosted-instance","Assign to Hosted Instance",[398,16488,16489,16493,16499],{},[31,16490,12031,16491,4576],{},[364,16492,12034],{},[31,16494,16495,16496,16370],{},"Open the dropdown menu to the right of the Remote Instance you want to assign and select the ",[364,16497,16498],{},"Add to Instance",[31,16500,16501,16502,9521],{},"Select the Hosted Instance in the dialog and click ",[364,16503,15627],{},[14,16505,16506,16508,16509,273],{},[364,16507,1798],{}," There are constraints on which instances can be assigned to each other. For detailed information, refer to ",[41,16510,16512],{"href":16511},"\u002Fdocs\u002Fuser\u002Fconcepts.md#assignment-rules","Assignment Rules and Constraints",[104,16514,16516],{"id":16515},"remove-from-hosted-instance","Remove from Hosted Instance",[14,16518,16519],{},"To remove the Remote Instance from a Node-RED instance:",[398,16521,16522,16526,16530],{},[31,16523,15925,16524,4576],{},[364,16525,12034],{},[31,16527,16391,16528,16370],{},[364,16529,16516],{},[31,16531,16396,16532,16370],{},[364,16533,16399],{},[14,16535,16536],{},"The Remote Instance will stop running the current Node-RED flows. It will then wait\nuntil it is assigned to another Hosted Instance or Application",[104,16538,16540],{"id":16539},"bulk-assigning-remote-instances-to-a-hosted-instance","Bulk Assigning Remote Instances to a Hosted Instance",[14,16542,16543],{},"If you have a number of Remote Instance to assign to an instance, you can do them all at once.",[398,16545,16546,16553,16559],{},[31,16547,16414,16548,16550,16551,15933],{},[364,16549,16417],{}," dropdown menu adjacent to the ",[364,16552,15920],{},[31,16554,16421,16555,16558],{},[364,16556,16557],{},"Move to Instance"," option and then select the instance you want to assign the Remote Instances to.",[31,16560,15596,16561,9521],{},[364,16562,16430],{},[768,16564,16434],{"id":16565},"details-1",[28,16567,16568,16571],{},[31,16569,16570],{},"Remote Instances that are already assigned to the chosen instance will not be changed or updated.",[31,16572,16442,16573],{},[28,16574,16575,16578,16581,16584],{},[31,16576,16577],{},"If the chosen instance has a target snapshot set, the newly assigned Remote Instances will inherit this.",[31,16579,16580],{},"If the chosen instance does not have a target snapshot set, newly assigned Remote Instances will have their target snapshot cleared.",[31,16582,16583],{},"Remote Instances in fleet mode will automatically apply the changes.",[31,16585,16586],{},"Remote Instances in developer mode will continue to run their current flows until they are switched to fleet mode at which point they will update accordingly.",[104,16588,16590],{"id":16589},"bulk-removing-remote-instances-from-an-application-or-instance","Bulk Removing Remote Instances from an Application or Instance",[14,16592,16593],{},"If you have a number of Remote Instances to remove from an application or instance, you can do them all at once.",[398,16595,16596,16603,16614],{},[31,16597,16598,16599,16550,16601,15933],{},"Select the Remote Instances you want to remove and open the ",[364,16600,16417],{},[364,16602,15920],{},[31,16604,16421,16605,16608,16609],{},[364,16606,16607],{},"Unassign"," option.\n",[398,16610,16611],{},[31,16612,16613],{},"Depending on where you are viewing Remote Instances, this may say \"Remove from Application\" or \"Remove from Instance\".",[31,16615,16616,16617,273],{},"Confirm the action by clicking ",[364,16618,16607],{},[768,16620,16434],{"id":16621},"details-2",[28,16623,16624,16627],{},[31,16625,16626],{},"Remote Instances that are already unassigned will not be changed or updated.",[31,16628,16629,16630],{},"For any Remote Instance that was previously assigned to an application or instance:\n",[28,16631,16632,16635,16638],{},[31,16633,16634],{},"Remote Instances in developer mode will be switched to fleet mode.",[31,16636,16637],{},"Remote Instances will have their target snapshot and Remote Instance group membership cleared.",[31,16639,16640],{},"Remote Instances will be informed of the changes resulting in their flows being cleared and the Remote Instance entering a stopped state waiting for a new assignment.",[23,16642,16219],{"id":16643},"regenerating-configurations",[14,16645,16646],{},"To regenerate Remote Instance configurations:",[398,16648,16649,16654,16660],{},[31,16650,16651,16652,4576],{},"Go to your team's or instance's ",[364,16653,12034],{},[31,16655,16656,16657,16370],{},"Open the dropdown menu to the right of the Remote Instance and select the\n",[364,16658,16659],{},"Regenerate Configuration",[31,16661,16662,16663,9521],{},"You will need to confirm this action as the existing configuration will be\nimmediately revoked. If the Remote Instance tries to use the old configuration it will\nfail to connect and will delete its local copy of the snapshot it was\nrunning. Click ",[364,16664,16659],{},[14,16666,16667,16668,16671],{},"You will then be shown the ",[364,16669,16670],{},"Remote Instance Configuration"," dialog again with a new\nsetup command and the manual configuration to copy or download.",[23,16673,16675],{"id":16674},"deleting-a-remote-instance","Deleting a Remote Instance",[14,16677,16678],{},"To delete a Remote Instance:",[398,16680,16681,16685,16690],{},[31,16682,16651,16683,4576],{},[364,16684,12034],{},[31,16686,16656,16687,16370],{},[364,16688,16689],{},"Delete Remote Instance",[31,16691,16396,16692,16370],{},[364,16693,16694],{},"Delete",[14,16696,16697],{},"The next time the Remote Instance attempts to connect to the platform it will find it is\nno longer authorised and will stop and delete its local copy of the flows it was running.",[23,16699,16701],{"id":16700},"node-red-settings","Node-RED Settings",[14,16703,16704],{},"Most Node-RED settings are managed by the platform as part of deploying an instance\nto the Remote Instance. However some settings can be overridden locally on the Remote Instance.",[104,16706,16708],{"id":16707},"https-configuration","HTTPS configuration",[14,16710,16711],{},[1160,16712,16713],{},"Available in Device Agent 0.10+",[14,16715,372,16716,16719,16720,16722,16723,273],{},[18,16717,16718],{},"https"," configuration option in ",[18,16721,20],{}," can be used to enable HTTPS within Node-RED. The values\nare passed through to the ",[41,16724,16727,16728,16730],{"href":16725,"rel":16726},"https:\u002F\u002Fnodered.org\u002Fdocs\u002Fuser-guide\u002Fruntime\u002Fconfiguration",[831],"Node-RED ",[18,16729,16718],{}," setting",[14,16732,372,16733,3012,16736,302,16739,16742],{},[18,16734,16735],{},"ca",[18,16737,16738],{},"key",[18,16740,16741],{},"cert"," properties can be used to provide custom certificates and keys.\nThe values should be set to the contents of the certificate\u002Fkey.",[14,16744,16745,16746,3012,16749,302,16752,16755],{},"Alternatively, the properties ",[18,16747,16748],{},"caPath",[18,16750,16751],{},"keyPath",[18,16753,16754],{},"certPath"," can be used instead\nto provide absolute paths to files containing the certificates\u002Fkeys.",[50,16757,16760],{"className":16758,"code":16759,"language":3504,"meta":55,"style":55},"language-yml shiki shiki-themes github-light github-dark","https:\n   keyPath: \u002Fopt\u002Fflowfuse-device\u002Fcerts\u002Fkey.pem\n   certPath: \u002Fopt\u002Fflowfuse-device\u002Fcerts\u002Fcert.pem\n   caPath: \u002Fopt\u002Fflowfuse-device\u002Fcerts\u002Fca.pem\n",[18,16761,16762,16768,16778,16788],{"__ignoreMap":55},[59,16763,16764,16766],{"class":61,"line":62},[59,16765,16718],{"class":174},[59,16767,196],{"class":178},[59,16769,16770,16773,16775],{"class":61,"line":77},[59,16771,16772],{"class":174},"   keyPath",[59,16774,179],{"class":178},[59,16776,16777],{"class":69},"\u002Fopt\u002Fflowfuse-device\u002Fcerts\u002Fkey.pem\n",[59,16779,16780,16783,16785],{"class":61,"line":88},[59,16781,16782],{"class":174},"   certPath",[59,16784,179],{"class":178},[59,16786,16787],{"class":69},"\u002Fopt\u002Fflowfuse-device\u002Fcerts\u002Fcert.pem\n",[59,16789,16790,16793,16795],{"class":61,"line":99},[59,16791,16792],{"class":174},"   caPath",[59,16794,179],{"class":178},[59,16796,16797],{"class":69},"\u002Fopt\u002Fflowfuse-device\u002Fcerts\u002Fca.pem\n",[104,16799,16801,577],{"id":16800},"httpstatic-configuration",[18,16802,16803],{},"httpStatic",[14,16805,16806],{},[1160,16807,16713],{},[14,16809,16810],{},"This option can be used to serve content from a local directory.",[14,16812,16813,16814,273],{},"If set to a path, the files in that directory will be served relative to ",[18,16815,3601],{},[50,16817,16819],{"className":16758,"code":16818,"language":3504,"meta":55,"style":55},"httpStatic: \u002Fopt\u002Fflowfuse-device\u002Fstatic-content\n",[18,16820,16821],{"__ignoreMap":55},[59,16822,16823,16825,16827],{"class":61,"line":62},[59,16824,16803],{"class":174},[59,16826,179],{"class":178},[59,16828,16829],{"class":69},"\u002Fopt\u002Fflowfuse-device\u002Fstatic-content\n",[14,16831,16832],{},"It is also possible to configure it with a list of directories and the corresponding\npath they should be served from.",[50,16834,16836],{"className":16758,"code":16835,"language":3504,"meta":55,"style":55},"httpStatic:\n  - path: \u002Fopt\u002Fflowfuse-device\u002Fstatic-content\u002Fimages\n    root: \u002Fimages\n  - path: \u002Fopt\u002Fflowfuse-device\u002Fstatic-content\u002Fjs\n    root: \u002Fjs\n",[18,16837,16838,16844,16856,16866,16877],{"__ignoreMap":55},[59,16839,16840,16842],{"class":61,"line":62},[59,16841,16803],{"class":174},[59,16843,196],{"class":178},[59,16845,16846,16848,16851,16853],{"class":61,"line":77},[59,16847,14050],{"class":178},[59,16849,16850],{"class":174},"path",[59,16852,179],{"class":178},[59,16854,16855],{"class":69},"\u002Fopt\u002Fflowfuse-device\u002Fstatic-content\u002Fimages\n",[59,16857,16858,16861,16863],{"class":61,"line":88},[59,16859,16860],{"class":174},"    root",[59,16862,179],{"class":178},[59,16864,16865],{"class":69},"\u002Fimages\n",[59,16867,16868,16870,16872,16874],{"class":61,"line":99},[59,16869,14050],{"class":178},[59,16871,16850],{"class":174},[59,16873,179],{"class":178},[59,16875,16876],{"class":69},"\u002Fopt\u002Fflowfuse-device\u002Fstatic-content\u002Fjs\n",[59,16878,16879,16881,16883],{"class":61,"line":156},[59,16880,16860],{"class":174},[59,16882,179],{"class":178},[59,16884,16885],{"class":69},"\u002Fjs\n",[104,16887,16889,577],{"id":16888},"localauth-configuration",[18,16890,16891],{},"localAuth",[14,16893,16894],{},[1160,16895,16896],{},"Available in Device Agent 3.20+",[14,16898,16899],{},"This option can be used to enable local login for the Node-RED editor.  This option is not recommended for day to day use.",[14,16901,16902],{},"It can be configured under the Remote Instance's Settings",[14,16904,16905,16908],{},[638,16906],{"alt":16907,"dataZoomable":55,"src":12015},"Local Auth settings for Remote Instance",[1160,16909,16907],{},[14,16911,16912,16913,9812],{},"Or by adding the following to the ",[18,16914,20],{},[50,16916,16918],{"className":16758,"code":16917,"language":3504,"meta":55,"style":55},"localAuth:\n    enabled: true\n    user: user-name\n    pass: $hashed-password\n",[18,16919,16920,16926,16935,16945],{"__ignoreMap":55},[59,16921,16922,16924],{"class":61,"line":62},[59,16923,16891],{"class":174},[59,16925,196],{"class":178},[59,16927,16928,16931,16933],{"class":61,"line":77},[59,16929,16930],{"class":174},"    enabled",[59,16932,179],{"class":178},[59,16934,3230],{"class":73},[59,16936,16937,16940,16942],{"class":61,"line":88},[59,16938,16939],{"class":174},"    user",[59,16941,179],{"class":178},[59,16943,16944],{"class":69},"user-name\n",[59,16946,16947,16950,16952],{"class":61,"line":99},[59,16948,16949],{"class":174},"    pass",[59,16951,179],{"class":178},[59,16953,16954],{"class":69},"$hashed-password\n",[14,16956,16957],{},"NOTE: The password should be hashed version generated using the Node-RED admin CLI. For example:",[50,16959,16961],{"className":52,"code":16960,"language":54,"meta":55,"style":55},"node-red admin hash-pw\n",[18,16962,16963],{"__ignoreMap":55},[59,16964,16965,16967,16970],{"class":61,"line":62},[59,16966,6745],{"class":65},[59,16968,16969],{"class":69}," admin",[59,16971,16972],{"class":69}," hash-pw\n",[14,16974,16975],{},"Alternatively, the password can be set in FlowFuse on the settings -> security tab of the Remote Instance.",[23,16977,12621],{"id":12620},[14,16979,16980],{},"If you have problems with the device agent the first thing to do is to enable the verbose logging mode.",[14,16982,16983,16984,16987],{},"To do this add a ",[18,16985,16986],{},"-v"," to the command line. This will present a lot more information about what the agent is doing.\nIt will show that is has connected to the FlowFuse instance and every time it checks in, it will also log all the\nlocal HTTP requests made when accessing the Node-RED Editor via the FlowFuse application.",[316,16989,16990],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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}",{"title":55,"searchDepth":77,"depth":77,"links":16992},[16993,16997,17000,17004,17011,17012,17013,17020],{"id":15912,"depth":77,"text":15913,"children":16994},[16995,16996],{"id":15919,"depth":88,"text":15920},{"id":15970,"depth":88,"text":15971},{"id":16023,"depth":77,"text":15908,"children":16998},[16999],{"id":16042,"depth":88,"text":16043},{"id":16137,"depth":77,"text":16138,"children":17001},[17002,17003],{"id":16141,"depth":88,"text":16142},{"id":16199,"depth":88,"text":16200},{"id":16329,"depth":77,"text":16330,"children":17005},[17006,17007,17008,17009,17010],{"id":16342,"depth":88,"text":15721},{"id":16463,"depth":88,"text":16464},{"id":16515,"depth":88,"text":16516},{"id":16539,"depth":88,"text":16540},{"id":16589,"depth":88,"text":16590},{"id":16643,"depth":77,"text":16219},{"id":16674,"depth":77,"text":16675},{"id":16700,"depth":77,"text":16701,"children":17014},[17015,17016,17018],{"id":16707,"depth":88,"text":16708},{"id":16800,"depth":88,"text":17017},"httpStatic configuration",{"id":16888,"depth":88,"text":17019},"localAuth configuration",{"id":12620,"depth":77,"text":12621},{},"device-agent\u002Fregister.md",{"title":44,"description":15875},"docs\u002Fdevice-agent\u002Fregister","iE837EDsMuQVlZLpRrVcU0eTkydAHV07QsK6meDh-oo",{"id":17027,"title":17028,"body":17029,"description":55,"extension":329,"layout":330,"meta":17528,"navGroup":330,"navOrder":156,"navTitle":17529,"navigation":187,"originalPath":17530,"path":11887,"redirect":330,"seo":17531,"stem":17532,"updated":337,"version":338,"__hash__":17533},"docs\u002Fdocs\u002Fdevice-agent\u002Frunning.md","Running the Device Agent",{"type":7,"value":17030,"toc":17508},[17031,17034,17037,17043,17046,17054,17063,17066,17070,17073,17077,17084,17088,17091,17097,17101,17106,17120,17125,17159,17163,17166,17170,17176,17254,17260,17270,17295,17299,17312,17319,17325,17329,17377,17382,17386,17422,17427,17430,17433,17436,17465,17469,17472,17505],[10,17032,17028],{"id":17033},"running-the-device-agent",[23,17035,11262],{"id":17036},"running",[14,17038,17039,17040,17042],{},"If the agent was installed as a global npm module, the command ",[18,17041,14986],{}," will be on the path.",[14,17044,17045],{},"If the default working directory and config file are being used, then the agent can be started with:",[50,17047,17048],{"className":52,"code":16300,"language":54,"meta":55,"style":55},[18,17049,17050],{"__ignoreMap":55},[59,17051,17052],{"class":61,"line":62},[59,17053,16300],{"class":65},[14,17055,17056,17057,17059,17060,273],{},"By default, Node-RED will listen to port ",[18,17058,14972],{},", you can change there using the options\ndetailed ",[41,17061,785],{"href":17062},"\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Fmanual#listen-port",[14,17064,17065],{},"This will start the agent, set the Remote Instance in the default of fleet mode, and connect to\nFlowFuse, waiting until a Target Snapshot has been assigned to it, or it is assigned\nto an Application.",[104,17067,17069],{"id":17068},"when-assigned-to-an-instance","When assigned to an instance:",[14,17071,17072],{},"Once the agent has been assigned a Target Snapshot, it will download the Snapshot and\ndeploy it to the Remote Instance.",[104,17074,17076],{"id":17075},"when-assigned-to-an-application","When assigned to an application:",[14,17078,17079,17080,17083],{},"Once the agent has been assigned to an application it starts up. If the device is new,\nit will get a default set of flows which can be edited directly.\nSee ",[41,17081,11933],{"href":17082},"\u002Fdocs\u002Fdevice-agent\u002Fdeploy#editing-the-node-red-flows-on-a-remote-instance-that-is-assigned-to-an-application"," for details.",[104,17085,17087],{"id":17086},"device-agent-command-line-options","Device Agent Command Line Options",[14,17089,17090],{},"The following command line options are available:",[50,17092,17095],{"className":17093,"code":17094,"language":3920},[3918],"Options\n\n  -c, --config file           Device configuration file. Default: device.yml\n  -d, --dir dir               Where the agent should store its state. Default: \u002Fopt\u002Fflowfuse-device\n  -i, --interval secs\n  -p, --port number\n  -m, --moduleCache           Use local npm module cache rather than install\n  --node-options string       Node.js command-line options to pass to the Node-RED process. Can be specified multiple times. You must use `=` between the option and its value.\n\nWeb UI Options\n\n  -w, --ui            Start the Web UI Server (optional, does not run by default)\n  --ui-host string    Web UI server host. Default: (0.0.0.0) (listen on all interfaces)\n  --ui-port number    Web UI server port. Default: 1879\n  --ui-user string    Web UI username. Required if --ui is specified\n  --ui-pass string    Web UI password. Required if --ui is specified\n  --ui-runtime mins   Time the Web UI server is permitted to run. Default: 10\n\nSetup command\n\n  -o, --otc string    Setup device using a one time code\n  --otc-no-start      Do not start the agent after setup\n  --otc-no-import     Do not ask to import Node-RED flows during setup\n  -u, --ff-url url    URL of FlowFuse. Required for setup\n\nGlobal Options\n\n  -h, --help       print out helpful usage information\n  --version        print out version information\n  -v, --verbose    turn on debugging output\n",[18,17096,17094],{"__ignoreMap":55},[104,17098,17100],{"id":17099},"command-line-examples","Command Line Examples",[14,17102,17103],{},[1160,17104,17105],{},"Start the agent with a different port number",[50,17107,17109],{"className":52,"code":17108,"language":54,"meta":55,"style":55},"flowfuse-device-agent -p 8080\n",[18,17110,17111],{"__ignoreMap":55},[59,17112,17113,17115,17117],{"class":61,"line":62},[59,17114,14986],{"class":65},[59,17116,8441],{"class":73},[59,17118,17119],{"class":73}," 8080\n",[14,17121,17122],{},[1160,17123,17124],{},"Start the agent with a different working directory and the Web UI enabled",[50,17126,17128],{"className":52,"code":17127,"language":54,"meta":55,"style":55},"flowfuse-device-agent -d \u002Fpath\u002Fto\u002Fworking\u002Fdirectory -w --ui-user admin --ui-pass password --ui-port 8081\n",[18,17129,17130],{"__ignoreMap":55},[59,17131,17132,17134,17136,17139,17142,17145,17147,17150,17153,17156],{"class":61,"line":62},[59,17133,14986],{"class":65},[59,17135,9743],{"class":73},[59,17137,17138],{"class":69}," \u002Fpath\u002Fto\u002Fworking\u002Fdirectory",[59,17140,17141],{"class":73}," -w",[59,17143,17144],{"class":73}," --ui-user",[59,17146,16969],{"class":69},[59,17148,17149],{"class":73}," --ui-pass",[59,17151,17152],{"class":69}," password",[59,17154,17155],{"class":73}," --ui-port",[59,17157,17158],{"class":73}," 8081\n",[23,17160,17162],{"id":17161},"configuring-nodejs-options","Configuring Node.js Options",[14,17164,17165],{},"Node.js command-line arguments can be passed to the Node-RED process started by the Device Agent. This is useful for memory-intensive workflows or certificate management.",[104,17167,17169],{"id":17168},"via-command-line","Via command line",[14,17171,15450,17172,17175],{},[18,17173,17174],{},"--node-options"," — it can be specified multiple times:",[50,17177,17179],{"className":52,"code":17178,"language":54,"meta":55,"style":55},"# Set a custom heap size limit\nflowfuse-device-agent --node-options='--max-old-space-size=256'\n\n# Enable system certificate authorities (Linux)\nflowfuse-device-agent --node-options='--use-openssl-ca'\n\n# Enable system certificate authorities (Windows\u002FmacOS)\nflowfuse-device-agent --node-options='--use-system-ca'\n\n# Combine multiple options\nflowfuse-device-agent --node-options='--max-old-space-size=256' --node-options='--use-openssl-ca'\n",[18,17180,17181,17186,17196,17200,17205,17214,17218,17223,17232,17236,17241],{"__ignoreMap":55},[59,17182,17183],{"class":61,"line":62},[59,17184,17185],{"class":3773},"# Set a custom heap size limit\n",[59,17187,17188,17190,17193],{"class":61,"line":77},[59,17189,14986],{"class":65},[59,17191,17192],{"class":73}," --node-options=",[59,17194,17195],{"class":69},"'--max-old-space-size=256'\n",[59,17197,17198],{"class":61,"line":88},[59,17199,188],{"emptyLinePlaceholder":187},[59,17201,17202],{"class":61,"line":99},[59,17203,17204],{"class":3773},"# Enable system certificate authorities (Linux)\n",[59,17206,17207,17209,17211],{"class":61,"line":156},[59,17208,14986],{"class":65},[59,17210,17192],{"class":73},[59,17212,17213],{"class":69},"'--use-openssl-ca'\n",[59,17215,17216],{"class":61,"line":216},[59,17217,188],{"emptyLinePlaceholder":187},[59,17219,17220],{"class":61,"line":224},[59,17221,17222],{"class":3773},"# Enable system certificate authorities (Windows\u002FmacOS)\n",[59,17224,17225,17227,17229],{"class":61,"line":233},[59,17226,14986],{"class":65},[59,17228,17192],{"class":73},[59,17230,17231],{"class":69},"'--use-system-ca'\n",[59,17233,17234],{"class":61,"line":241},[59,17235,188],{"emptyLinePlaceholder":187},[59,17237,17238],{"class":61,"line":249},[59,17239,17240],{"class":3773},"# Combine multiple options\n",[59,17242,17243,17245,17247,17250,17252],{"class":61,"line":257},[59,17244,14986],{"class":65},[59,17246,17192],{"class":73},[59,17248,17249],{"class":69},"'--max-old-space-size=256'",[59,17251,17192],{"class":73},[59,17253,17213],{"class":69},[104,17255,17257,17258],{"id":17256},"via-deviceyml","Via ",[18,17259,20],{},[14,17261,17262,17263,17266,17267,17269],{},"Add a ",[18,17264,17265],{},"nodeOptions"," array to the ",[18,17268,20],{}," configuration file:",[50,17271,17273],{"className":165,"code":17272,"language":167,"meta":55,"style":55},"nodeOptions:\n  - \"--max-old-space-size=256\"\n  - \"--use-openssl-ca\"\n",[18,17274,17275,17281,17288],{"__ignoreMap":55},[59,17276,17277,17279],{"class":61,"line":62},[59,17278,17265],{"class":174},[59,17280,196],{"class":178},[59,17282,17283,17285],{"class":61,"line":77},[59,17284,14050],{"class":178},[59,17286,17287],{"class":69},"\"--max-old-space-size=256\"\n",[59,17289,17290,17292],{"class":61,"line":88},[59,17291,14050],{"class":178},[59,17293,17294],{"class":69},"\"--use-openssl-ca\"\n",[23,17296,17298],{"id":17297},"running-behind-a-http-proxy","Running behind a HTTP Proxy",[14,17300,17301,17302,3012,17305,1706,17308,17311],{},"If the Remote Instance is behind a HTTP proxy, the agent can be configured to use the proxy by setting the ",[18,17303,17304],{},"http_proxy",[18,17306,17307],{},"https_proxy",[18,17309,17310],{},"all_proxy"," environment variables.",[14,17313,17314,17315,17318],{},"If necessary, the ",[18,17316,17317],{},"no_proxy"," environment variable can be used to specify a list of hosts that should not be accessed via the proxy.",[14,17320,17321,17322,17324],{},"For connecting to FlowFuse Cloud, the ",[18,17323,17307],{}," variable should be set to your proxy URL. This environment variable will be used by the agent for both the HTTP\nand MQTT connections.",[104,17326,17328],{"id":17327},"example-setting-the-proxy-environment-variables-on-linux","Example setting the proxy environment variables on Linux",[50,17330,17332],{"className":52,"code":17331,"language":54,"meta":55,"style":55},"# Set the https_proxy environment variable\nexport https_proxy=http:\u002F\u002Fmy-proxy:3128\n# Set the no_proxy environment variable to exclude local addresses and all hosts in the .mydomain.com domain\nexport no_proxy=localhost,127.0.0.1,.mydomain.com\n# Start the agent\nflowfuse-device-agent\n",[18,17333,17334,17339,17351,17356,17368,17373],{"__ignoreMap":55},[59,17335,17336],{"class":61,"line":62},[59,17337,17338],{"class":3773},"# Set the https_proxy environment variable\n",[59,17340,17341,17343,17346,17348],{"class":61,"line":77},[59,17342,13205],{"class":1372},[59,17344,17345],{"class":178}," https_proxy",[59,17347,1373],{"class":1372},[59,17349,17350],{"class":178},"http:\u002F\u002Fmy-proxy:3128\n",[59,17352,17353],{"class":61,"line":88},[59,17354,17355],{"class":3773},"# Set the no_proxy environment variable to exclude local addresses and all hosts in the .mydomain.com domain\n",[59,17357,17358,17360,17363,17365],{"class":61,"line":99},[59,17359,13205],{"class":1372},[59,17361,17362],{"class":178}," no_proxy",[59,17364,1373],{"class":1372},[59,17366,17367],{"class":178},"localhost,127.0.0.1,.mydomain.com\n",[59,17369,17370],{"class":61,"line":156},[59,17371,17372],{"class":3773},"# Start the agent\n",[59,17374,17375],{"class":61,"line":216},[59,17376,16300],{"class":65},[14,17378,17379],{},[1160,17380,17381],{},"To make these settings permanent, see the documentation for your Linux distribution.",[104,17383,17385],{"id":17384},"example-setting-the-proxy-environment-variables-on-windows","Example setting the proxy environment variables on Windows",[50,17387,17389],{"className":52,"code":17388,"language":54,"meta":55,"style":55},"# Set the https_proxy environment variable\nset https_proxy=http:\u002F\u002Fmy-proxy:3128\n# Set the no_proxy environment variable to exclude local addresses and all hosts in the .mydomain.com domain\nset no_proxy=localhost,127.0.0.1,.mydomain.com\n# Start the agent\nflowfuse-device-agent\n",[18,17390,17391,17395,17403,17407,17414,17418],{"__ignoreMap":55},[59,17392,17393],{"class":61,"line":62},[59,17394,17338],{"class":3773},[59,17396,17397,17400],{"class":61,"line":77},[59,17398,17399],{"class":73},"set",[59,17401,17402],{"class":69}," https_proxy=http:\u002F\u002Fmy-proxy:3128\n",[59,17404,17405],{"class":61,"line":88},[59,17406,17355],{"class":3773},[59,17408,17409,17411],{"class":61,"line":99},[59,17410,17399],{"class":73},[59,17412,17413],{"class":69}," no_proxy=localhost,127.0.0.1,.mydomain.com\n",[59,17415,17416],{"class":61,"line":156},[59,17417,17372],{"class":3773},[59,17419,17420],{"class":61,"line":216},[59,17421,16300],{"class":65},[14,17423,17424],{},[1160,17425,17426],{},"To make these settings permanent, see the documentation for your version of Windows.",[23,17428,314],{"id":17429},"running-with-no-access-to-npmjsorg",[14,17431,17432],{},"By default, the Device Agent will try and download the correct version of Node-RED and\nany nodes required to run the Snapshot that is assigned to run on the Remote Instance.",[14,17434,17435],{},"If the Remote Instance is being run on an offline network or security policies prevent the\nDevice Agent from connecting to npmjs.org then it can be configured to use a pre-cached\nset of modules.",[14,17437,17438,17439,17442,17443,17446,17447,17449,17450,17453,17454,17456,17457,17460,17461,17464],{},"You can enable this mode by adding ",[18,17440,17441],{},"-m"," to the command line or adding ",[18,17444,17445],{},"moduleCache: true","\nto the ",[18,17448,20],{}," file. This will cause the Device Agent to load the modules from the\n",[18,17451,17452],{},"module_cache"," directory in the Device Agents ",[41,17455,16196],{"href":16195}," (or whatever is set\nwith the ",[18,17458,17459],{},"-d"," option) (e.g. ",[18,17462,17463],{},"\u002Fopt\u002Fflowfuse-device\u002Fmodule_cache",".).",[104,17466,17468],{"id":17467},"creating-a-module-cache","Creating a module cache",[14,17470,17471],{},"To create a suitable module cache, the device must be assigned to a Remote Instance.  You will need to\ninstall the modules on a local device with access to npmjs.org, ensuring you use the same\nOS and Architecture as your target device, and then copy the modules on to your Remote Instance.",[398,17473,17474,17480,17483,17492,17498],{},[31,17475,17476,17477,17479],{},"From the Snapshot page, select the snapshot you want to deploy and select the option to download its ",[18,17478,7977],{}," file.",[31,17481,17482],{},"Place this file in an empty directory on your local device.",[31,17484,13259,17485,17488,17489,9704],{},[18,17486,17487],{},"npm install"," to install the modules. This will create a ",[18,17490,17491],{},"node_modules",[31,17493,17494,17495,17497],{},"On your target Remote Instance, create a directory called ",[18,17496,17452],{}," inside the Device Agent Configuration directory.",[31,17499,1985,17500,17502,17503,9704],{},[18,17501,17491],{}," directory from your local instance to the target instance so that it is under the ",[18,17504,17452],{},[316,17506,17507],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}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 .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 pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":55,"searchDepth":77,"depth":77,"links":17509},[17510,17516,17521,17525],{"id":17036,"depth":77,"text":11262,"children":17511},[17512,17513,17514,17515],{"id":17068,"depth":88,"text":17069},{"id":17075,"depth":88,"text":17076},{"id":17086,"depth":88,"text":17087},{"id":17099,"depth":88,"text":17100},{"id":17161,"depth":77,"text":17162,"children":17517},[17518,17519],{"id":17168,"depth":88,"text":17169},{"id":17256,"depth":88,"text":17520},"Via device.yml",{"id":17297,"depth":77,"text":17298,"children":17522},[17523,17524],{"id":17327,"depth":88,"text":17328},{"id":17384,"depth":88,"text":17385},{"id":17429,"depth":77,"text":314,"children":17526},[17527],{"id":17467,"depth":88,"text":17468},{},"Running the Agent","device-agent\u002Frunning.md",{"title":17028,"description":55},"docs\u002Fdevice-agent\u002Frunning","u_jjK5OrBAg3qk3z2xToF_btc9Qh1KTKCs2wweoO7_Q",{"id":17535,"title":17536,"body":17537,"description":17546,"extension":329,"layout":330,"meta":17627,"navGroup":330,"navOrder":88,"navTitle":17628,"navigation":187,"originalPath":17629,"path":17630,"redirect":330,"seo":17631,"stem":17632,"updated":337,"version":338,"__hash__":17633},"docs\u002Fdocs\u002Fhardware\u002Fctrlx-device-agent.md","ctrlX Device Agent App",{"type":7,"value":17538,"toc":17623},[17539,17542,17547,17551,17589,17593],[10,17540,17536],{"id":17541},"ctrlx-device-agent-app",[14,17543,17544],{},[364,17545,17546],{},"Currently not available - will be available soon",[23,17548,17550],{"id":17549},"installation-procedure","Installation Procedure",[398,17552,17553,17563,17570,17577,17583],{},[31,17554,17555,17556,17559,17560,273],{},"In the ctrlX CORE web interface, navigate to the window ",[364,17557,17558],{},"Settings"," ➔ ",[364,17561,17562],{},"Apps",[31,17564,17565,17566,17569],{},"Switch the ctrlX device to the ",[364,17567,17568],{},"Service"," mode.",[31,17571,17572,17573,17576],{},"In the app overview, navigate to the category ",[364,17574,17575],{},"Available apps",". This category displays all apps saved in the app storage on the ctrlX device and all apps provided via the ctrlX Store.",[31,17578,17579,17580,273],{},"Search for the FlowFuse Device Agent ctrlX App to be installed and click on the Installation button. If multiple app versions are provided for installation, a list of available versions and app sources will be displayed. In this case, select the desired app version from the list to start the installation. If only one app version is provided, the installation will start directly. After the installation, the app will be shown in the app overview, under the category ",[364,17581,17582],{},"Installed apps",[31,17584,17585,17586,17569],{},"Switch the ctrlX device back to the ",[364,17587,17588],{},"Operating",[23,17590,17592],{"id":17591},"device-agent-configuration-for-ctrlx","Device Agent Configuration for ctrlX",[398,17594,17595,17602,17611,17614,17620],{},[31,17596,17597,17598],{},"After successful installation, ",[41,17599,17601],{"href":17600},"\u002Fdocs\u002Fdevice-agent\u002Fregister\u002F#manual-setup","generate and download the \"Device Credentials\" in FlowFuse",[31,17603,17555,17604,17559,17606,17559,17608],{},[364,17605,17558],{},[364,17607,17562],{},[364,17609,17610],{},"Manage App Data",[31,17612,17613],{},"Select the folder \"FlowFuse Device Agent\"",[31,17615,17616,17617,17619],{},"Click on upload file and select the ",[364,17618,20],{}," configuration from step 1",[31,17621,17622],{},"The Device Agent connects automatically to your FlowFuse instance. Ensure your ctrlX has a network connection to your FlowFuse platform (e.g. FlowFuse Cloud) and to the npmjs registry.",{"title":55,"searchDepth":77,"depth":77,"links":17624},[17625,17626],{"id":17549,"depth":77,"text":17550},{"id":17591,"depth":77,"text":17592},{},"ctrlX - Device Agent","hardware\u002Fctrlx-device-agent.md","\u002Fdocs\u002Fhardware\u002Fctrlx-device-agent",{"title":17536,"description":17546},"docs\u002Fhardware\u002Fctrlx-device-agent","UxiINEDZoGcldVcl_gzgCuz8Wn1YfOeOqElPsVSNhFk",{"id":17635,"title":17636,"body":17637,"description":17708,"extension":329,"layout":330,"meta":17709,"navGroup":330,"navOrder":77,"navTitle":17710,"navigation":187,"originalPath":17711,"path":17712,"redirect":330,"seo":17713,"stem":17714,"updated":337,"version":338,"__hash__":17715},"docs\u002Fdocs\u002Fhardware\u002Fctrlx-node-red.md","ctrlX Node-RED App",{"type":7,"value":17638,"toc":17704},[17639,17642,17650,17652,17655,17684,17688],[10,17640,17636],{"id":17641},"ctrlx-node-red-app",[14,17643,17644,17645],{},"Learn more about the Node-RED App in the ",[41,17646,17649],{"href":17647,"rel":17648},"https:\u002F\u002Fdeveloper.community.boschrexroth.com\u002Ft5\u002FStore-and-How-to\u002FFlowFuse-Node-RED\u002Fba-p\u002F82135",[831],"ctrlX World Store",[23,17651,17550],{"id":17549},[14,17653,17654],{},"Follow these steps to install the Rexroth CtrlX App by FlowFuse on your ctrlX device:",[398,17656,17657,17663,17667,17671,17680],{},[31,17658,17555,17659,17559,17661,273],{},[364,17660,17558],{},[364,17662,17562],{},[31,17664,17565,17665,17569],{},[364,17666,17568],{},[31,17668,17572,17669,17576],{},[364,17670,17575],{},[31,17672,17673,17674,17677,17678,273],{},"Search for the ",[18,17675,17676],{},"Rexroth CtrlX App"," to be installed and click on the Install button. If multiple app versions are provided for installation, a list of available versions and app sources will be displayed. In this case, select the desired app version from the list to start the installation. If only one app version is provided, the installation will start directly. After the installation, the app will be shown in the app overview, under the category ",[364,17679,17582],{},[31,17681,17585,17682,17569],{},[364,17683,17588],{},[23,17685,17687],{"id":17686},"start-and-login","Start and Login",[398,17689,17690,17693,17698,17701],{},[31,17691,17692],{},"Open the Flow Editor from the Node-RED menu in your ctrlX.",[31,17694,17695,273],{},[364,17696,17697],{},"Log in using your ctrlX username and password",[31,17699,17700],{},"A successful login requires a valid license and user permission.",[31,17702,17703],{},"Create your Node-RED flow.",{"title":55,"searchDepth":77,"depth":77,"links":17705},[17706,17707],{"id":17549,"depth":77,"text":17550},{"id":17686,"depth":77,"text":17687},"Learn more about the Node-RED App in the ctrlX World Store",{},"ctrlX - Node-RED","hardware\u002Fctrlx-node-red.md","\u002Fdocs\u002Fhardware\u002Fctrlx-node-red",{"title":17636,"description":17708},"docs\u002Fhardware\u002Fctrlx-node-red","Uwa8TjVrnrAwvrMbet52QKtcm3nWltvBzxToEBfm4Rs",{"id":17717,"title":55,"body":17718,"description":55,"extension":329,"layout":532,"meta":17722,"navGroup":12179,"navOrder":77,"navTitle":17724,"navigation":187,"originalPath":17725,"path":17726,"redirect":17727,"seo":17729,"stem":17730,"updated":337,"version":338,"__hash__":17731},"docs\u002Fdocs\u002Fhardware\u002Findex.md",{"type":7,"value":17719,"toc":17720},[],{"title":55,"searchDepth":77,"depth":77,"links":17721},[],{"tags":17723},[4066],"Hardware Guides","hardware\u002FREADME.md","\u002Fdocs\u002Fhardware",{"to":17728},"\u002Fdocs\u002Fhardware\u002Fintroduction",{"description":55},"docs\u002Fhardware\u002Findex","XDeyNYcgOugXbJ1vYZVHZiRhxP_XTYBvgGyfdtR-Wx8",{"id":17733,"title":17734,"body":17735,"description":17798,"extension":329,"layout":330,"meta":17799,"navGroup":330,"navOrder":62,"navTitle":15465,"navigation":187,"originalPath":17800,"path":17728,"redirect":330,"seo":17801,"stem":17802,"updated":337,"version":338,"__hash__":17803},"docs\u002Fdocs\u002Fhardware\u002Fintroduction.md","Device Agent - Hardware",{"type":7,"value":17736,"toc":17794},[17737,17740,17747,17751,17759,17774,17778,17787],[10,17738,17734],{"id":17739},"device-agent-hardware",[14,17741,17742,17743,17746],{},"The FlowFuse Device Agent can be installed on any Mac, Linux, or Windows-supported device. If you want to learn more about how to install the Device Agent in general, ",[41,17744,17745],{"href":15512},"click here",". Nevertheless, this overview provides a guide for specific hardware platforms.",[23,17748,17750],{"id":17749},"official-flowfuse-hardware-partners","Official FlowFuse Hardware Partners",[14,17752,17753],{},[41,17754,17755],{"href":17647},[638,17756],{"src":17757,"width":17758,"height":14716},"https:\u002F\u002Fupload.wikimedia.org\u002Fwikipedia\u002Fcommons\u002F0\u002F0d\u002FLogo_of_Bosch_Rexroth_AG.svg",150,[28,17760,17761,17767],{},[31,17762,17763],{},[41,17764,17766],{"href":17765},"\u002Fdocs\u002Fhardware\u002Fctrlx-node-red.md","ctrlX - Node-RED App",[31,17768,17769,17773],{},[41,17770,17772],{"href":17771},"\u002Fdocs\u002Fhardware\u002Fctrlx-device-agent.md","ctrlX - Device Agent App"," - will be availble soon",[23,17775,17777],{"id":17776},"other-hardware-guides","Other Hardware Guides",[14,17779,17780],{},[41,17781,17783],{"href":17782},"\u002Fdocs\u002Fhardware\u002Fraspbian.md",[638,17784],{"src":17785,"width":17786,"height":3297},"https:\u002F\u002Fupload.wikimedia.org\u002Fwikipedia\u002Fde\u002Fc\u002Fcb\u002FRaspberry_Pi_Logo.svg",80,[28,17788,17789],{},[31,17790,17791],{},[41,17792,17793],{"href":17782},"Raspberry Pi",{"title":55,"searchDepth":77,"depth":77,"links":17795},[17796,17797],{"id":17749,"depth":77,"text":17750},{"id":17776,"depth":77,"text":17777},"The FlowFuse Device Agent can be installed on any Mac, Linux, or Windows-supported device. If you want to learn more about how to install the Device Agent in general, click here. Nevertheless, this overview provides a guide for specific hardware platforms.",{},"hardware\u002Fintroduction.md",{"title":17734,"description":17798},"docs\u002Fhardware\u002Fintroduction","hQ63lPnA_0uN4EAp5vk5VPYl7g7nsGF5mkOO_wPilKo",{"id":17805,"title":17793,"body":17806,"description":17813,"extension":329,"layout":330,"meta":17924,"navGroup":330,"navOrder":88,"navTitle":17925,"navigation":187,"originalPath":17926,"path":17927,"redirect":330,"seo":17928,"stem":17929,"updated":337,"version":338,"__hash__":17930},"docs\u002Fdocs\u002Fhardware\u002Fraspbian.md",{"type":7,"value":17807,"toc":17917},[17808,17811,17814,17818,17821,17843,17848,17859,17863,17866,17870,17873,17878,17881,17886,17890,17893,17898,17901,17906,17909,17914],[10,17809,17793],{"id":17810},"raspberry-pi",[14,17812,17813],{},"The Raspberry Pi section provides instructions for installing and setting up the FlowFuse Device Agent on your Raspberry Pi device, enabling seamless integration for efficient device management and automation.",[23,17815,17817],{"id":17816},"installing-the-device-agent","Installing the Device Agent",[14,17819,17820],{},"FlowFuse provides a script to install Node.JS, npm, and the FlowFuse Device Agent onto a Raspberry Pi.\nThis script won't work on ARMv6 builds as the standard Node.JS builds don't support it, as result Pi Zero's are not supported.",[50,17822,17826],{"className":17823,"code":17824,"language":17825,"meta":55,"style":55},"language-sh shiki shiki-themes github-light github-dark","bash \u003C(curl -sL https:\u002F\u002Fraw.githubusercontent.com\u002FFlowFuse\u002Fdevice-agent\u002Fmain\u002Fservice\u002Fraspbian-install-device-agent.sh)\n","sh",[18,17827,17828],{"__ignoreMap":55},[59,17829,17830,17832,17835,17837,17840],{"class":61,"line":62},[59,17831,54],{"class":65},[59,17833,17834],{"class":69}," \u003C(",[59,17836,1381],{"class":65},[59,17838,17839],{"class":73}," -sL",[59,17841,17842],{"class":69}," https:\u002F\u002Fraw.githubusercontent.com\u002FFlowFuse\u002Fdevice-agent\u002Fmain\u002Fservice\u002Fraspbian-install-device-agent.sh)\n",[14,17844,17845],{},[364,17846,17847],{},"This script will:",[398,17849,17850,17853,17856],{},[31,17851,17852],{},"Detect if Node.js is already installed, it will ensure it is at least v14. If less than v14 it will stop. If nothing is found it will install the Node.js 18 LTS release",[31,17854,17855],{},"Install the latest version of the FlowFuse Device Agent using npm.",[31,17857,17858],{},"Setup the FlowFuse Device Agent to run as a service",[23,17860,17862],{"id":17861},"running-as-a-service","Running as a service",[14,17864,17865],{},"You can run the device agent as a service, which means it can run in the background and be enabled to automatically start on boot. The install script will automatically set up the FlowFuse Device Agent to run as a service. The following commands can be useful for controlling the service or changing the default service settings.",[104,17867,17869],{"id":17868},"starting-the-service-on-boot-optional","Starting the service on boot (optional)",[14,17871,17872],{},"If you want Node-RED to run when the device is turned on, or re-booted, you can enable the service to autostart by running the command:",[14,17874,17875],{},[18,17876,17877],{},"sudo systemctl enable flowfuse-device-agent.service",[14,17879,17880],{},"To disable the service, run the command:",[14,17882,17883],{},[18,17884,17885],{},"sudo systemctl disable flowfuse-device-agent.service",[104,17887,17889],{"id":17888},"controlling-the-service","Controlling the service",[14,17891,17892],{},"You can start the service with the command:",[14,17894,17895],{},[18,17896,17897],{},"sudo systemctl start flowfuse-device-agent",[14,17899,17900],{},"You can check the current status with the command:",[14,17902,17903],{},[18,17904,17905],{},"sudo systemctl status flowfuse-device-agent",[14,17907,17908],{},"You can stop your with the command:",[14,17910,17911],{},[18,17912,17913],{},"sudo systemctl stop flowfuse-device-agent",[316,17915,17916],{},"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 .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":55,"searchDepth":77,"depth":77,"links":17918},[17919,17920],{"id":17816,"depth":77,"text":17817},{"id":17861,"depth":77,"text":17862,"children":17921},[17922,17923],{"id":17868,"depth":88,"text":17869},{"id":17888,"depth":88,"text":17889},{},"Raspberry Pi with Raspbian","hardware\u002Fraspbian.md","\u002Fdocs\u002Fhardware\u002Fraspbian",{"title":17793,"description":17813},"docs\u002Fhardware\u002Fraspbian","p4Ou2K_eb5f3ByFCZ7v7ApcMXksh9NJf1p0b5zAV1AQ",{"id":17932,"title":55,"body":17933,"description":55,"extension":329,"layout":330,"meta":18564,"navGroup":330,"navOrder":330,"navTitle":18565,"navigation":187,"originalPath":18566,"path":18567,"redirect":330,"seo":18568,"stem":18569,"updated":337,"version":338,"__hash__":18570},"docs\u002Fdocs\u002Findex.md",{"type":7,"value":17934,"toc":18557},[17935,17939,17943,17946,17954,17957,18078,18082,18085,18093],[17936,17937,17938],"script",{},"     \n   class IconChevronRight extends HTMLElement {\n      constructor() {\n         super();\n         this.attachShadow({ 'mode': 'open' })\n      }\n      \n      connectedCallback () {\n         this.shadowRoot.innerHTML = `\u003Csvg class=\"ff-icon ff-icon-sm\" xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\">\u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m8.25 4.5 7.5 7.5-7.5 7.5\" \u002F>\u003C\u002Fsvg>`\n      }\n   }\n   customElements.define('icon-chevron-right', IconChevronRight);\n",[10,17940,17942],{"id":17941},"flowfuse-documentation","FlowFuse Documentation",[14,17944,17945],{},"Welcome to the documentation for FlowFuse, an open-source, industrial data platform that enables engineers to build, manage, scale, and secure their Node-RED solutions.",[14,17947,17948,17949,17953],{},"It covers everything from setup, to usage, and development. All ",[41,17950,17952],{"href":17951},"\u002Fdocs\u002Fcontribute\u002Fintroduction\u002F","contributions"," are welcome.",[23,17955,17956],{"id":552},"Getting Started",[17958,17959,17964,17965,17964,18033],"div",{"className":17960},[17961,17962,17963],"ff-offering-tiles","grid-cols-1","sm:grid-cols-2","\n   ",[17958,17966,17970,17971,17970,17974,17970,17977,17964],{"className":17967},[17968,17969],"ff-tile","ff-offering-tile","\n      ",[17972,17973,534],"label",{},[14,17975,17976],{},"Run FlowFuse yourself on your own infrastructure.",[28,17978,17980,17981,17980,17996,17980,18006,17980,18015,17980,18024,17970],{"style":17979},"margin-top: 0; margin-bottom: 0;","\n         ",[31,17982,17985,17986,17980],{"className":17983},[17984],"ff-offering-cta","\n            ",[41,17987,17989,17990],{"href":17988},"\u002Fdocs\u002Fquick-start\u002F","\n               Quick Start Instructions\n               ",[17991,17992,17985],"icon-chevron-right",{"className":17993},[17994,17995],"ff-icon","ff-icon-sm",[31,17997,17985,17999,17980],{"className":17998},[17984],[41,18000,18002,18003],{"href":18001},"\u002Fdocs\u002Finstall\u002Fintroduction\u002F","\n               Install FlowFuse Self-Hosted\n               ",[17991,18004,17985],{"className":18005},[17994,17995],[31,18007,17985,18008,17980],{},[41,18009,18011,18012],{"href":18010},"\u002Fdocs\u002Fupgrade","\n               Upgrade Your FlowFuse Instance\n               ",[17991,18013,17985],{"className":18014},[17994,17995],[31,18016,17985,18017,17980],{},[41,18018,18020,18021],{"href":18019},"\u002Fdocs\u002Fupgrade\u002Fopen-source-to-premium\u002F","\n               Unlock Enterprise Features\n               ",[17991,18022,17985],{"className":18023},[17994,17995],[31,18025,17985,18026,17980],{},[41,18027,18029,18030],{"href":18028},"\u002Fdocs\u002Fadmin\u002Fintroduction\u002F","\n               Administering FlowFuse\n               ",[17991,18031,17985],{"className":18032},[17994,17995],[17958,18034,17970,18036,17970,18038,17970,18041,17964],{"className":18035},[17968,17969],[17972,18037,4067],{},[14,18039,18040],{},"Hosted solution, nothing to install anything, jump straight in.",[28,18042,17980,18043,17980,18053,17980,18061,17980,18069,17970],{"style":17979},[31,18044,18046],{"className":18045},[17984],[41,18047,18049,18050],{"href":18048},"https:\u002F\u002Fapp.flowfuse.com\u002Faccount\u002Fcreate","Sign Up for Free",[17991,18051],{"className":18052},[17994,17995],[31,18054,18055],{},[41,18056,18057,18058],{"href":55},"Upgrading Teams",[17991,18059],{"className":18060},[17994,17995],[31,18062,18063],{},[41,18064,3959,18066],{"href":18065},"\u002Fdocs\u002Fcloud\u002Fbilling\u002F",[17991,18067],{"className":18068},[17994,17995],[31,18070,18071],{},[41,18072,18074,18075],{"href":18073},"\u002Fdocs\u002Fcloud\u002Fintroduction\u002F#single-sign-on","Single Sign On",[17991,18076],{"className":18077},[17994,17995],[23,18079,18081],{"id":18080},"using-flowfuse","Using FlowFuse",[104,18083,17956],{"id":18084},"getting-started-1",[14,18086,18087,18088,18092],{},"Here are some quick reference links to our most popular topics. You can also view the full documentation available for FlowFuse in our ",[41,18089,17956],{"href":18090,"rel":18091},"https:\u002F\u002Fflowfuse.com\u002Fdocs\u002Fuser\u002Fintroduction\u002F",[831]," guide.",[17958,18094,17964,18098,17964,18137,17964,18161,17964,18181,17964,18217,17964,18237,18240,18270,18274,18277],{"className":18095},[18096,17962,18097],"ff-product-feature-tiles","md:grid-cols-2",[41,18099,17970,18103,17970,18129,17964],{"className":18100,"href":18102},[17968,18101],"ff-product-feature-tile","\u002Fdocs\u002Fuser\u002Finstance-settings\u002F",[17958,18104,17980,18107,17970],{"className":18105},[18106],"ff-product-feature-tile-decorator",[18108,18109,18112,18117,18121,18125],"svg",{"xmlns":18110,"viewBox":18111},"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","0 0 20 14.62",[16850,18113],{"className":18114,"d":18116},[18115],"st0","M0,7.31v-1.72c5.09,0,5.81-.94,6.44-1.77,.72-.94,1.46-1.67,3.88-1.67v1.72c-1.76,0-2.04,.37-2.51,.99-1.02,1.34-2.31,2.45-7.81,2.45Z",[16850,18118],{"className":18119,"d":18120},[18115],"M8.6,12.47c-2.9,0-3.47-1.58-3.88-2.73-.47-1.31-.87-2.43-4.72-2.43v-1.72c4.67,0,5.67,1.69,6.34,3.57,.38,1.06,.57,1.59,2.26,1.59v1.72Z",[16850,18122],{"className":18123,"d":18124},[18115],"M16.78,14.62h-6.88c-.95,0-1.72-.77-1.72-1.72v-2.58c0-.95,.77-1.72,1.72-1.72h6.88c.95,0,1.72,.77,1.72,1.72v2.58c0,.95-.77,1.72-1.72,1.72Zm0-1.72v0h0Zm0-2.58h-6.88v2.58h6.88v-2.58Z",[16850,18126],{"className":18127,"d":18128},[18115],"M18.28,6.02h-6.88c-.95,0-1.72-.77-1.72-1.72V1.72c0-.95,.77-1.72,1.72-1.72h6.88c.95,0,1.72,.77,1.72,1.72v2.58c0,.95-.77,1.72-1.72,1.72Zm0-1.72v0h0Zm0-2.58h-6.88v2.58h6.88V1.72Z",[17958,18130,17980,18131,17980,18134,17970],{},[17972,18132,18133],{},"Configuring Instances",[59,18135,18136],{},"Understand the options available for your Node-RED Instances",[41,18138,17970,18141,17970,18154,17964],{"className":18139,"href":18140},[17968,18101],"\u002Fdocs\u002Fuser\u002Fsnapshots\u002F",[17958,18142,17980,18144,17970],{"className":18143},[18106],[18108,18145,18150],{"className":18146,"xmlns":18110,"fill":18147,"viewBox":18148,"strokeWidth":8359,"stroke":18149},[17994],"none","0 0 24 24","currentColor",[16850,18151],{"strokeLineCap":18152,"strokeLineJoin":18152,"d":18153},"round","M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z",[17958,18155,17980,18156,17980,18158,17970],{},[17972,18157,11991],{},[59,18159,18160],{},"Version Control for your Node-RED Instances.",[41,18162,17970,18165,17970,18174,17964],{"className":18163,"href":18164},[17968,18101],"\u002Fdocs\u002Fuser\u002Fshared-library\u002F",[17958,18166,17980,18168,17970],{"className":18167},[18106],[18108,18169,18171],{"xmlns":18110,"fill":18147,"viewBox":18148,"strokeWidth":18170,"stroke":18149},"1.5",[16850,18172],{"strokeLineCap":18152,"strokeLineJoin":18152,"d":18173},"M2.25 12.75V12A2.25 2.25 0 0 1 4.5 9.75h15A2.25 2.25 0 0 1 21.75 12v.75m-8.69-6.44-2.12-2.12a1.5 1.5 0 0 0-1.061-.44H4.5A2.25 2.25 0 0 0 2.25 6v12a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9a2.25 2.25 0 0 0-2.25-2.25h-5.379a1.5 1.5 0 0 1-1.06-.44Z",[17958,18175,17980,18176,17980,18178,17970],{},[17972,18177,778],{},[59,18179,18180],{},"Centralized management of re-usable flows and functions.",[41,18182,17970,18184,17970,18210,17964],{"className":18183,"href":18140},[17968,18101],[17958,18185,17980,18187,17970],{"className":18186},[18106],[18108,18188,18194],{"className":18189,"xmlns":18110,"xmlnsXLink":18190,"x":18191,"y":18191,"viewBox":18148,"style":18192,"xmlSpace":18193},[17994],"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxlink","0px","enable-background:new 0 0 24 24;","preserve",[18195,18196,18198,18201,18204,18207],"g",{"className":18197},[18115],[16850,18199],{"d":18200},"M7,15.8 M7,3.1 M7,15.8V4.2C7,3.5,6.5,3,5.8,3H3.9C3.2,3,2.6,3.5,2.6,4.2v11.6",[16850,18202],{"d":18203},"M14,15.8 M14,3.1 M14,15.8V4.2C14,3.5,13.5,3,12.8,3h-1.9c-0.7,0-1.2,0.5-1.2,1.2v11.6",[16850,18205],{"d":18206},"M21,3.1 M21,15c0-2.7,0-10.8,0-10.8C21,3.5,20.5,3,19.8,3h-1.9c-0.7,0-1.2,0.5-1.2,1.2V11",[16850,18208],{"d":18209},"M17.2,22.5c-0.2,0-0.4-0.1-0.5-0.2c-0.3-0.3-0.3-0.8,0-1.1l2.5-2.5H3c-0.4,0-0.8-0.3-0.8-0.8s0.3-0.8,0.8-0.8h16.2l-2.5-2.5c-0.3-0.3-0.3-0.8,0-1.1s0.8-0.3,1.1,0l3.8,3.8c0.1,0.1,0.1,0.2,0.2,0.2c0,0.1,0.1,0.2,0.1,0.3s0,0.2-0.1,0.3c0,0.1-0.1,0.2-0.2,0.2l-3.8,3.8C17.6,22.4,17.4,22.5,17.2,22.5z",[17958,18211,17980,18212,17980,18214,17970],{},[17972,18213,15818],{},[59,18215,18216],{},"Deploy flows between Test, Staging & Production Environments.",[41,18218,17970,18221,17970,18230,17964],{"className":18219,"href":18220},[17968,18101],"\u002Fdocs\u002Fuser\u002Fdevice-groups\u002F",[17958,18222,17980,18224,17970],{"className":18223},[18106],[18108,18225,18227],{"className":18226,"xmlns":18110,"fill":18147,"viewBox":18148,"strokeWidth":8359,"stroke":18149},[17994],[16850,18228],{"strokeLineCap":18152,"strokeLineJoin":18152,"d":18229},"M8.25 3v1.5M4.5 8.25H3m18 0h-1.5M4.5 12H3m18 0h-1.5m-15 3.75H3m18 0h-1.5M8.25 19.5V21M12 3v1.5m0 15V21m3.75-18v1.5m0 15V21m-9-1.5h10.5a2.25 2.25 0 0 0 2.25-2.25V6.75a2.25 2.25 0 0 0-2.25-2.25H6.75A2.25 2.25 0 0 0 4.5 6.75v10.5a2.25 2.25 0 0 0 2.25 2.25Zm.75-12h9v9h-9v-9Z",[17958,18231,17980,18232,17980,18234,17970],{},[17972,18233,12034],{},[59,18235,18236],{},"Deploy flows remotely with FlowFuse \"Devices\".",[41,18238,17970],{"className":18239,"href":18220},[17968,18101],[17958,18241,18243],{"className":18242},[18106],[41,18244,17980,18246,18264,17964],{"className":18245,"href":18220},[17968,18101],[18108,18247,18249,18261],{"className":18248,"viewBox":18148,"fill":18147,"xmlns":18110},[17994],[18195,18250,18251,18254,18258],{},[16850,18252],{"d":18253,"fill":18149},"M16.2 11.63H11.63V16.2H16.2V11.63Z",[16850,18255],{"fillRule":18256,"clipRule":18256,"d":18257,"fill":18149},"evenodd","M8.27997 16.18V18.13C8.27997 18.5 8.42997 18.87 8.69997 19.14C8.96997 19.41 9.32997 19.56 9.70997 19.56H11.66V20.67C11.66 20.82 11.72 20.97 11.83 21.08C12.05 21.3 12.43 21.3 12.65 21.08C12.76 20.97 12.82 20.83 12.82 20.67V19.56H15.03V20.67C15.03 20.82 15.09 20.97 15.2 21.08C15.42 21.3 15.8 21.3 16.02 21.08C16.13 20.97 16.19 20.83 16.19 20.67V19.56H18.14C18.51 19.56 18.88 19.41 19.15 19.14C19.42 18.87 19.57 18.51 19.57 18.13V16.18H20.68C20.83 16.18 20.98 16.12 21.09 16.01C21.2 15.9 21.26 15.75 21.26 15.6C21.26 15.45 21.2 15.3 21.09 15.19C20.98 15.08 20.84 15.02 20.68 15.02H19.57V12.81H20.68C20.83 12.81 20.98 12.75 21.09 12.64C21.2 12.53 21.26 12.38 21.26 12.23C21.26 12.08 21.2 11.93 21.09 11.82C20.98 11.71 20.83 11.65 20.68 11.65H19.57V9.69996C19.57 9.32996 19.42 8.95996 19.15 8.68996C18.88 8.41996 18.52 8.26996 18.14 8.26996H12.82V7.15996C12.82 7.00996 12.76 6.85996 12.65 6.74996C12.43 6.52996 12.05 6.52996 11.83 6.74996C11.72 6.85996 11.66 6.99996 11.66 7.15996V8.26996H9.70997C9.32997 8.26996 8.96997 8.41996 8.69997 8.68996C8.43997 8.94996 8.27997 9.31996 8.27997 9.69996V11.65H7.16997C7.01997 11.65 6.86997 11.71 6.75997 11.82C6.64997 11.93 6.58997 12.08 6.58997 12.23C6.58997 12.38 6.64997 12.53 6.75997 12.64C6.86997 12.75 7.01997 12.81 7.16997 12.81H8.27997V16.18ZM18.4 18.39H9.43997V9.42996H18.4V18.39Z",[16850,18259],{"fillRule":18256,"clipRule":18256,"d":18260,"fill":18149},"M8.28997 15.02H6.07997V6.05996H15.04V8.26996H16.2V6.31996C16.2 5.94996 16.05 5.57996 15.78 5.30996C15.52 5.04996 15.15 4.88996 14.77 4.88996H12.82V3.77996C12.82 3.62996 12.76 3.47996 12.65 3.36996C12.43 3.14996 12.05 3.14996 11.83 3.36996C11.72 3.47996 11.66 3.61996 11.66 3.77996V4.88996H9.44997V3.77996C9.44997 3.62996 9.38997 3.47996 9.27997 3.36996C9.05997 3.14996 8.67997 3.14996 8.45997 3.36996C8.34997 3.47996 8.28997 3.61996 8.28997 3.77996V4.88996H6.33997C5.95997 4.88996 5.59997 5.03996 5.32997 5.30996C5.06997 5.56996 4.90997 5.93996 4.90997 6.31996V8.26996H3.79997C3.64997 8.26996 3.49997 8.32996 3.38997 8.43996C3.27997 8.54996 3.21997 8.69996 3.21997 8.84996C3.21997 8.99996 3.27997 9.14996 3.38997 9.25996C3.49997 9.36996 3.64997 9.42996 3.79997 9.42996H4.90997V11.64H3.79997C3.64997 11.64 3.49997 11.7 3.38997 11.81C3.27997 11.92 3.21997 12.06 3.21997 12.22C3.21997 12.38 3.27997 12.52 3.38997 12.63C3.49997 12.74 3.64997 12.8 3.79997 12.8H4.90997V14.75C4.90997 15.12 5.05997 15.49 5.32997 15.76C5.59997 16.03 5.95997 16.18 6.33997 16.18H8.27997V15.02H8.28997Z",[18262,18263],"defs",{},[50,18265,18268],{"className":18266,"code":18267,"language":3920},[3918],"  \u003C\u002Fdiv>\n  \u003Cdiv>\n     \u003Clabel>Device Groups\u003C\u002Flabel>\n     \u003Cspan>Manage large numbers of devices together.\u003C\u002Fspan>\n  \u003C\u002Fdiv>\n",[18,18269,18267],{"__ignoreMap":55},[104,18271,18273],{"id":18272},"advanced-features","Advanced Features",[14,18275,18276],{},"Once you're more comfortable with FlowFuse, you may want to explore some of our more advanced features that will help elevate your Node-RED experience even further.",[17958,18278,17964,18280,17964,18300,18304,18341,18345,18513,18515,18528,18530,18551,18554],{"className":18279},[18096,17962,18097],[41,18281,17970,18284,17970,18293,17964],{"className":18282,"href":18283},[17968,18101],"\u002Fdocs\u002Fuser\u002Fteam\u002F",[17958,18285,17980,18287,17970],{"className":18286},[18106],[18108,18288,18290],{"className":18289,"xmlns":18110,"fill":18147,"viewBox":18148,"strokeWidth":8359,"stroke":18149},[17994],[16850,18291],{"strokeLineCap":18152,"strokeLineJoin":18152,"d":18292},"M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z",[17958,18294,17980,18295,17980,18297,17970],{},[17972,18296,750],{},[59,18298,18299],{},"Host your Instances at a custom subdomain",[41,18301,17970],{"className":18302,"href":18303},[17968,18101],"\u002Fdocs\u002Fuser\u002Fcustom-hostnames\u002F",[17958,18305,18307,17964,18322],{"className":18306},[18106],[41,18308,17980,18310,18316,17964],{"className":18309,"href":18303},[17968,18101],[18108,18311,18313],{"className":18312,"xmlns":18110,"fill":18147,"viewBox":18148,"strokeWidth":8359,"stroke":18149},[17994],[16850,18314],{"strokeLineCap":18152,"strokeLineJoin":18152,"d":18315},"M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418",[50,18317,18320],{"className":18318,"code":18319,"language":3920},[3918],"  \u003C\u002Fdiv>\n  \u003Cdiv>\n     \u003Clabel>Custom Domains\u003C\u002Flabel>\n     \u003Cspan>Host your Instances at a custom subdomain\u003C\u002Fspan>\n  \u003C\u002Fdiv>\n",[18,18321,18319],{"__ignoreMap":55},[41,18323,17970,18325,17970,18334,17964],{"className":18324,"href":18220},[17968,18101],[17958,18326,17980,18328,17970],{"className":18327},[18106],[18108,18329,18331],{"className":18330,"xmlns":18110,"fill":18147,"viewBox":18148,"stroke":18149},[17994],[16850,18332],{"strokeLineCap":18152,"strokeLineJoin":18152,"d":18333},"M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125",[17958,18335,17980,18336,17980,18338,17970],{},[17972,18337,975],{},[59,18339,18340],{},"Automatically distribute workload across multiple copies of your Node-RED Instance.",[23,18342,18344],{"id":18343},"flowfuse-extras","FlowFuse Extras",[17958,18346,17964,18349,17964,18413,17964,18450,17964,18494],{"className":18347},[17961,17962,17963,18348],"lg:grid-cols-2",[17958,18350,17970,18352,17970,18355,18358,17964],{"className":18351},[17968,17969],[17972,18353,18354],{},"FlowFuse Node-RED Nodes",[14,18356,18357],{},"A complete set of FlowFuse-maintained nodes for data sharing between instances, MQTT messaging, AI\u002FONNX models, MCP agent integrations, and enterprise data features.",[28,18359,17980,18360,17980,18369,17980,18378,17980,18387,17980,18396,17980,18405,17970],{},[31,18361,18362],{},[41,18363,18365,18366],{"href":18364},"\u002Fdocs\u002Fuser\u002Fprojectnodes\u002F","FlowFuse Project Nodes",[17991,18367],{"className":18368},[17994,17995],[31,18370,18371],{},[41,18372,18374,18375],{"href":18373},"https:\u002F\u002Fflowfuse.com\u002Fnode-red\u002Fflowfuse\u002Fmcp\u002F","FlowFuse MCP Nodes",[17991,18376],{"className":18377},[17994,17995],[31,18379,18380],{},[41,18381,18383,18384],{"href":18382},"https:\u002F\u002Fflowfuse.com\u002Fnode-red\u002Fflowfuse\u002Fflowfuse-tables\u002F","FlowFuse Tables Nodes",[17991,18385],{"className":18386},[17994,17995],[31,18388,18389],{},[41,18390,18392,18393],{"href":18391},"https:\u002F\u002Fflowfuse.com\u002Fnode-red\u002Fflowfuse\u002Fai\u002F","FlowFuse AI Nodes",[17991,18394],{"className":18395},[17994,17995],[31,18397,18398],{},[41,18399,18401,18402],{"href":18400},"\u002Fdocs\u002Fuser\u002Fmqtt-nodes\u002F","FlowFuse MQTT Nodes",[17991,18403],{"className":18404},[17994,17995],[31,18406,18407],{},[41,18408,18409,18410],{"href":843},"See all the Nodes",[17991,18411],{"className":18412},[17994,17995],[17958,18414,17970,18416,17970,18419,18422,17964],{"className":18415},[17968,17969],[17972,18417,18418],{},"FlowFuse Dashboard",[14,18420,18421],{},"Create interactive, responsive and secure Dashboards in Node-RED.",[28,18423,17980,18424,17980,18433,17980,18441,17970],{},[31,18425,18426],{},[41,18427,18429,18430],{"href":18428},"https:\u002F\u002Fdashboard.flowfuse.com","Install FlowFuse Dashboard",[17991,18431],{"className":18432},[17994,17995],[31,18434,18435],{},[41,18436,18437,18438],{"href":18428},"Build Your First Dashboard",[17991,18439],{"className":18440},[17994,17995],[31,18442,18443],{},[41,18444,18446,18447],{"href":18445},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fuser\u002Fmulti-tenancy.html","Multi Tenant Dashboards",[17991,18448],{"className":18449},[17994,17995],[17958,18451,17970,18453,17970,18455,18458,17964],{"className":18452},[17968,17969],[17972,18454,15472],{},[14,18456,18457],{},"Manage thousands of Node-RED Instances remotely with the FlowFuse Device Agent.",[28,18459,17980,18460,17980,18468,17980,18477,17980,18486,17970],{},[31,18461,18462],{},[41,18463,14787,18465],{"href":18464},"\u002Fdocs\u002Fdevice-agent\u002Finstall\u002F",[17991,18466],{"className":18467},[17994,17995],[31,18469,18470],{},[41,18471,18473,18474],{"href":18472},"\u002Fdocs\u002Fdevice-agent\u002Fregister\u002F","Registering Devices in FlowFuse",[17991,18475],{"className":18476},[17994,17995],[31,18478,18479],{},[41,18480,18482,18483],{"href":18481},"\u002Fdocs\u002Fdevice-agent\u002Fdeploy\u002F","Deploying Flows to your Device",[17991,18484],{"className":18485},[17994,17995],[31,18487,18488],{},[41,18489,18490,18491],{"href":18481},"Editing Flows on your Device",[17991,18492],{"className":18493},[17994,17995],[17958,18495,17970,18497,17970,18500,18503,17964],{"className":18496},[17968,17969],[17972,18498,18499],{},"FlowFuse Assistant",[14,18501,18502],{},"AI in the Node-RED Editor to help build your flows.",[28,18504,17980,18505,17970],{},[31,18506,18507],{},[41,18508,18509,18510],{"href":1048},"Getting Started Guide",[17991,18511],{"className":18512},[17994,17995],[23,18514,4117],{"id":4116},[28,18516,18517,18522],{},[31,18518,18519],{},[41,18520,12621],{"href":18521},"\u002Fdocs\u002Fdebugging\u002F",[31,18523,18524],{},[41,18525,18527],{"href":18526},"\u002Fdocs\u002Fpremium-support\u002F","FlowFuse Cloud Support",[23,18529,7338],{"id":7352},[28,18531,18532,18539,18545],{},[31,18533,18534,18538],{},[41,18535,18537],{"href":18536},"\u002Fdocs\u002Fcontribute\u002Fintroduction#contributing-to-flowfuse","Useful Information"," - Learn the foundational concepts of how FlowFuse is built & structured.",[31,18540,18541,18544],{},[41,18542,7800],{"href":18543},"\u002Fdocs\u002Fcontribute\u002Fintroduction#development-setup"," - Configure your local development environment to contribute to FlowFuse.",[31,18546,18547,18550],{},[41,18548,7832],{"href":18549},"\u002Fdocs\u002Fcontribute\u002Fintroduction#testing"," - Understand our testing philosophy at FlowFuse.",[316,18552,18553],{},"\n.st0 {\n   fill: currentColor;\n   stroke-width: 1.5;\n   stroke-linecap: round;\n   stroke-linejoin: round;\n}\n",[316,18555,18556],{},"\n   a label {\n      cursor: pointer;\n   }\n   .ff-tile,\n   .ff-offering-tile ul li a {\n      gap: 6px;\n      border: 1px solid #e2e8f0;\n      border-radius: 0.5rem;\n   }\n   .ff-tile span {\n      display: block;\n      color: #374151;\n      font-size: 0.875rem;\n      font-weight: 300;\n      line-height: 1.25rem;\n   }\n   .ff-offering-tiles {\n      display: grid;\n      gap: 1rem;\n   }\n   .ff-offering-tile {\n      padding: 1rem;\n   }\n   .ff-offering-tile .ff-offering-cta {\n      \u002F* margin-bottom: 1.5rem; *\u002F\n   }\n   .ff-offering-tile label {\n      font-size: 1.25rem;\n      font-weight: 500;\n      color: #4b5563;\n      display: flex;\n      align-items: center;\n      gap: 6px;\n   }\n   .ff-offering-tile label img {\n      width: 32px;\n   }\n   .ff-offering-tile ul {\n      list-style: none;\n      padding: 0;\n      margin-bottom: 0;\n   }\n   .ff-offering-tile ul li {\n      padding-left: 0;\n   }\n   .ff-offering-tile ul li:last-child {\n      margin-bottom: 0;\n   }\n   .ff-offering-tile ul li a {\n      display: flex;\n      padding: 6px 9px;\n      text-decoration: none;\n      align-items: center;\n      justify-content: space-between;\n   }\n   .ff-offering-tile ul li a:hover,\n   .ff-product-feature-tile:hover {\n      cursor: pointer;\n      border: 1px solid #4F46E5;\n      text-decoration: none;\n   }\n   .ff-offering-tile ul li .ff-icon {\n      padding-right: 0;      \n   }\n   .ff-product-feature-tiles {\n      display: grid;\n      gap: 1rem;\n   }\n   .ff-product-feature-tile {\n      display: flex;\n      padding: 6px;\n      text-decoration: none;\n   }\n   .ff-product-feature-tile label,\n   .ff-product-feature-tile .ff-product-feature-tile-decorator {\n      color: #2463eb;\n   }\n   .ff-product-feature-tile .ff-icon {\n      width: 32px;\n      height: 32px;\n      stroke-width: 1.5px;\n   }\n   .ff-product-feature-tile-decorator {\n      background-color: #EEF2FF;\n      padding: 3px;\n      width: 42px;\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      flex-shrink: 0;\n   }\n",{"title":55,"searchDepth":77,"depth":77,"links":18558},[18559,18560],{"id":552,"depth":77,"text":17956},{"id":18080,"depth":77,"text":18081,"children":18561},[18562,18563],{"id":18084,"depth":88,"text":17956},{"id":18272,"depth":88,"text":18273},{},"Documentation","README.md","\u002Fdocs",{"description":55},"docs\u002Findex","-wKASYWqMbI6vOEm2yUt1Jl20CxhUjtODU3slE8xhwA",{"id":18572,"title":7820,"body":18573,"description":20261,"extension":329,"layout":330,"meta":20262,"navGroup":330,"navOrder":330,"navTitle":7820,"navigation":187,"originalPath":20263,"path":20264,"redirect":330,"seo":20265,"stem":20266,"updated":337,"version":338,"__hash__":20267},"docs\u002Fdocs\u002Finstall\u002Fconfiguration.md",{"type":7,"value":18574,"toc":20232},[18575,18577,18585,18588,18592,18729,18737,18740,18768,18772,18782,18806,18810,18839,18843,18911,18915,18918,18942,18946,19007,19011,19056,19060,19181,19185,19188,19191,19232,19236,19239,19360,19366,19370,19373,19376,19424,19428,19435,19438,19459,19464,19558,19562,19565,19573,19576,19650,19659,19663,19666,19708,19712,19721,19762,19765,19770,19775,19811,19815,19842,19846,19897,19903,19909,19913,19923,19972,19980,19993,19996,20000,20003,20009,20015,20037,20041,20044,20047,20060,20068,20075,20079,20143,20147,20150,20204,20208,20211],[10,18576,7820],{"id":8033},[14,18578,18579,18580,18582,18583,273],{},"The base configuration of the FlowFuse platform is provided in the file\n",[18,18581,9421],{},". This assumes the default install location\nof ",[18,18584,9172],{},[14,18586,18587],{},"To run a local install, you can use the default options. This section describes\nthe options available in the configuration file.",[23,18589,18591],{"id":18590},"server-configuration","Server configuration",[2289,18593,18594,18602],{},[2292,18595,18596],{},[2295,18597,18598,18600],{},[2298,18599,3541],{},[2298,18601,3348],{},[2305,18603,18604,18632,18645,18660,18670,18691,18706,18720],{},[2295,18605,18606,18610],{},[2310,18607,18608],{},[18,18609,9436],{},[2310,18611,18612,18613,18616,18617,18619,18620,18622,18623,18625,18626,18628,18629],{},"The address to serve the web ui on. This defaults to ",[18,18614,18615],{},"localhost"," which means the ui will only be available when browsing from the same server that is running the platform. To make it accessible to other devices on the network, set it to ",[18,18618,9440],{},". ",[662,18621],{},"NOTE: If ",[18,18624,9436],{}," is changed, please also update ",[18,18627,9444],{}," to match e.g.  ",[18,18630,18631],{},"http:\u002F\u002F[ip-address-of-host]:3000",[2295,18633,18634,18639],{},[2310,18635,18636],{},[18,18637,18638],{},"port",[2310,18640,18641,18642],{},"The TCP port the platform serves its web ui. Default: ",[18,18643,18644],{},"3000",[2295,18646,18647,18651],{},[2310,18648,18649],{},[18,18650,9444],{},[2310,18652,18653,18654,18656,18657,18659],{},"The url to access the platform. This defaults to ",[18,18655,2518],{}," which means a number of internally generated URLs will only work when browsing on the same device as is running the platform. To be able to access the platform remotely, replace ",[18,18658,18615],{}," with the ip address of the device running FlowFuse.",[2295,18661,18662,18667],{},[2310,18663,18664],{},[18,18665,18666],{},"domain",[2310,18668,18669],{},"The domain that instance names will be prepended to on Docker & Kubernetes platforms to create a hostname to access the instance. A wildcard DNS A record should point be configured to point to the FlowFuse entry IP Address.",[2295,18671,18672,18677],{},[2310,18673,18674],{},[18,18675,18676],{},"support_contact",[2310,18678,18679,18680,1706,18683,18686,18687,18690],{},"a URL or string with contact details for the administrator e.g ",[18,18681,18682],{},"mailto:support@example.com",[18,18684,18685],{},"https:\u002F\u002Fsupport.example.com"," . Defaults to the email address of the first admin user or ",[18,18688,18689],{},"the administrator"," if no email address set.",[2295,18692,18693,18698],{},[2310,18694,18695],{},[18,18696,18697],{},"create_admin",[2310,18699,18700,18701,18703,18704],{},"If set to ",[18,18702,3558],{}," will create a default admin user on first run, the username\u002Fpassword is written to the logs. Default: ",[18,18705,659],{},[2295,18707,18708,18713],{},[2310,18709,18710],{},[18,18711,18712],{},"create_admin_access_token",[2310,18714,18700,18715,18717,18718],{},[18,18716,3558],{}," an access token (ffpat) is created for the default admin user on first run. Its value is written to the logs. Default: ",[18,18719,659],{},[2295,18721,18722,18726],{},[2310,18723,18724],{},[18,18725,8099],{},[2310,18727,18728],{},"Can be used to pass in a license key for FlowFuse. Default not set",[14,18730,18731,18732,302,18734,18736],{},"NOTE: Changing the ",[18,18733,9444],{},[18,18735,18666],{}," after Node-RED instances have been created is possible, but the original hostname and domain must remain active in order to access the instances and for an them to be able to access the FlowFuse resources.",[14,18738,18739],{},"An example workflow would be:",[398,18741,18742,18745,18756,18759,18762,18765],{},[31,18743,18744],{},"Register new domain",[31,18746,18747,18748],{},"Set up DNS entries for:\n",[28,18749,18750,18753],{},[31,18751,18752],{},"A record for the forge app",[31,18754,18755],{},"wildcard A record for the domain",[31,18757,18758],{},"Leave the existing entries for the old domain in place",[31,18760,18761],{},"Stop the forge app",[31,18763,18764],{},"Edit the flowforge.yml to set the base_url and domain entries",[31,18766,18767],{},"Restart the forge app",[23,18769,18771],{"id":18770},"database-configuration","Database configuration",[14,18773,18774,18775,302,18778,18781],{},"FlowFuse supports ",[18,18776,18777],{},"sqlite",[18,18779,18780],{},"postgres"," databases.",[2289,18783,18784,18792],{},[2292,18785,18786],{},[2295,18787,18788,18790],{},[2298,18789,3541],{},[2298,18791,3348],{},[2305,18793,18794],{},[2295,18795,18796,18801],{},[2310,18797,18798],{},[18,18799,18800],{},"db.type",[2310,18802,18803,18804,273],{},"The type of database to use. Default: ",[18,18805,18777],{},[104,18807,18809],{"id":18808},"sqlite-configuration","SQLite configuration",[2289,18811,18812,18820],{},[2292,18813,18814],{},[2295,18815,18816,18818],{},[2298,18817,3541],{},[2298,18819,3348],{},[2305,18821,18822],{},[2295,18823,18824,18829],{},[2310,18825,18826],{},[18,18827,18828],{},"db.storage",[2310,18830,18831,18832,18835,18836,273],{},"Path to the SQLite Database file to use, relative to ",[18,18833,18834],{},"\u002Fopt\u002Fflowforge\u002Fvar\u002F",". Default: ",[18,18837,18838],{},"forge.db",[104,18840,18842],{"id":18841},"postgres-configuration","Postgres configuration",[2289,18844,18845,18853],{},[2292,18846,18847],{},[2295,18848,18849,18851],{},[2298,18850,3541],{},[2298,18852,3348],{},[2305,18854,18855,18867,18879,18889,18899],{},[2295,18856,18857,18862],{},[2310,18858,18859],{},[18,18860,18861],{},"db.host",[2310,18863,18864,18865,273],{},"Hostname of the Postgres Database. Default: ",[18,18866,18780],{},[2295,18868,18869,18874],{},[2310,18870,18871],{},[18,18872,18873],{},"db.database",[2310,18875,18876,18877,273],{},"Database name on Postgres Server. Default: ",[18,18878,6303],{},[2295,18880,18881,18886],{},[2310,18882,18883],{},[18,18884,18885],{},"db.user",[2310,18887,18888],{},"Username used when connecting to Postgres Server.",[2295,18890,18891,18896],{},[2310,18892,18893],{},[18,18894,18895],{},"db.password",[2310,18897,18898],{},"Password used when connecting to Postgres Server.",[2295,18900,18901,18906],{},[2310,18902,18903],{},[18,18904,18905],{},"db.ssl",[2310,18907,18908,18909],{},"Client should connect with SSL\u002FTLS. Default: ",[18,18910,659],{},[23,18912,18914],{"id":18913},"node-red-driver-configuration","Node-RED Driver configuration",[14,18916,18917],{},"This configures how Node-RED instances are run by the platform.",[2289,18919,18920,18928],{},[2292,18921,18922],{},[2295,18923,18924,18926],{},[2298,18925,3541],{},[2298,18927,3348],{},[2305,18929,18930],{},[2295,18931,18932,18937],{},[2310,18933,18934],{},[18,18935,18936],{},"driver.type",[2310,18938,18939,18940],{},"The type of deployment model to use. Default: ",[18,18941,6273],{},[104,18943,18945],{"id":18944},"localfs-driver-options","Localfs Driver options",[2289,18947,18948,18956],{},[2292,18949,18950],{},[2295,18951,18952,18954],{},[2298,18953,3541],{},[2298,18955,3348],{},[2305,18957,18958,18971,18985,18997],{},[2295,18959,18960,18965],{},[2310,18961,18962],{},[18,18963,18964],{},"driver.options.start_port",[2310,18966,18967,18968],{},"The port number to start assigning to Node-RED instances as they are created. Default: ",[18,18969,18970],{},"12080",[2295,18972,18973,18978],{},[2310,18974,18975],{},[18,18976,18977],{},"driver.options.node_path",[2310,18979,18980,18981,18984],{},"The path to find the node.js executable - useful if Node.js has been installed with ",[18,18982,18983],{},"nvm"," so isn't necessarily on the system path.",[2295,18986,18987,18992],{},[2310,18988,18989],{},[18,18990,18991],{},"driver.options.logPassthrough",[2310,18993,18994,18995],{},"Prints the Node-RED logs in JSON format to stdout of the nr-launcher process. Default: ",[18,18996,659],{},[2295,18998,18999,19004],{},[2310,19000,19001],{},[18,19002,19003],{},"driver.options.privateCA",[2310,19005,19006],{},"The fully qualified path to a pem file containing locally trusted CA cert chain. Default: not set",[104,19008,19010],{"id":19009},"docker-driver-options","Docker Driver options",[2289,19012,19013,19021],{},[2292,19014,19015],{},[2295,19016,19017,19019],{},[2298,19018,3541],{},[2298,19020,3348],{},[2305,19022,19023,19036,19047],{},[2295,19024,19025,19030],{},[2310,19026,19027],{},[18,19028,19029],{},"driver.options.socket",[2310,19031,19032,19033],{},"The path to the Docker control unix domain socket. Default ",[18,19034,19035],{},"\u002Fvar\u002Frun\u002Fdocker.sock",[2295,19037,19038,19042],{},[2310,19039,19040],{},[18,19041,18991],{},[2310,19043,19044,19045],{},"Prints the Node-RED logs in JSON format to stdout of the Instance containers. Default: ",[18,19046,659],{},[2295,19048,19049,19053],{},[2310,19050,19051],{},[18,19052,19003],{},[2310,19054,19055],{},"The fully qualified path to a pem file on the host machine containing locally trusted CA cert chain. Default: not set",[104,19057,19059],{"id":19058},"kubernetes-driver-options","Kubernetes Driver options",[2289,19061,19062,19070],{},[2292,19063,19064],{},[2295,19065,19066,19068],{},[2298,19067,3541],{},[2298,19069,3348],{},[2305,19071,19072,19084,19098,19111,19126,19139,19151,19161,19171],{},[2295,19073,19074,19079],{},[2310,19075,19076],{},[18,19077,19078],{},"driver.options.namespace",[2310,19080,19081,19082],{},"The namespace to run Node-RED instances in. Default: ",[18,19083,6303],{},[2295,19085,19086,19091],{},[2310,19087,19088],{},[18,19089,19090],{},"driver.options.cloudProvider",[2310,19092,19093,19094,19097],{},"Enables specific options for certain platforms e.g. ",[18,19095,19096],{},"aws",". Default: not set",[2295,19099,19100,19105],{},[2310,19101,19102],{},[18,19103,19104],{},"driver.options.projectSelector",[2310,19106,19107,19108],{},"A YAML object containing node annotations to use to filter which nodes Node-RED instances run on. Default: ",[18,19109,19110],{},"role: projects",[2295,19112,19113,19117],{},[2310,19114,19115],{},[18,19116,18991],{},[2310,19118,19119,19120,19123,19124],{},"Prints the Node-RED logs in JSON format to stdout of the instance pods. This should be set with the ",[18,19121,19122],{},"forge.logPassthrough=true"," Helm chart value. Default: ",[18,19125,659],{},[2295,19127,19128,19132],{},[2310,19129,19130],{},[18,19131,19003],{},[2310,19133,19134,19135,19138],{},"The name of a ConfigMap containing a file called ",[18,19136,19137],{},"certs.pem"," which holds locally trusted CA cert chain. Default: not set",[2295,19140,19141,19146],{},[2310,19142,19143],{},[18,19144,19145],{},"driver.options.customHostname.enabled",[2310,19147,19148,19149],{},"Enables the custom hostname feature. Default: ",[18,19150,659],{},[2295,19152,19153,19158],{},[2310,19154,19155],{},[18,19156,19157],{},"driver.options.customHostname.cnameTarget",[2310,19159,19160],{},"The hostname users should configure their DNS entries to point at. This value is required to enable this feature. Default: not set",[2295,19162,19163,19168],{},[2310,19164,19165],{},[18,19166,19167],{},"driver.options.customHostname.ingressClass",[2310,19169,19170],{},"The name of the Ingress Class that should be used for the custom hostname. Default: not set",[2295,19172,19173,19178],{},[2310,19174,19175],{},[18,19176,19177],{},"driver.options.customHostname.certManagerIssuer",[2310,19179,19180],{},"The name of the CertManager ClusterIssuer to provision HTTPS certificates for custom hostnames. Default: not set",[23,19182,19184],{"id":19183},"mqtt-broker-configuration","MQTT Broker configuration",[14,19186,19187],{},"By default, the platform runs without an MQTT broker. This restricts some features\nin the platform, such as the Project Nodes, Device Actions and Remote Device Editing.",[14,19189,19190],{},"If a broker has been setup in the platform, the following configuration is required:",[2289,19192,19193,19201],{},[2292,19194,19195],{},[2295,19196,19197,19199],{},[2298,19198,3541],{},[2298,19200,3348],{},[2305,19202,19203,19216],{},[2295,19204,19205,19210],{},[2310,19206,19207],{},[18,19208,19209],{},"broker.url",[2310,19211,19212,19213,273],{},"The url for the platform to access the broker. For example: ",[18,19214,19215],{},"mqtt:\u002F\u002Flocalhost:4800",[2295,19217,19218,19223],{},[2310,19219,19220],{},[18,19221,19222],{},"broker.public_url",[2310,19224,19225,19226,19228,19229,273],{},"The url used by devices to connect to the broker, if different to ",[18,19227,19209],{},". For example, this may require devices to use WebSockets instead: ",[18,19230,19231],{},"ws:\u002F\u002Flocalhost:4881",[23,19233,19235],{"id":19234},"email-configuration","Email configuration",[14,19237,19238],{},"By default, email is disabled. This restricts some features in the platform around\ninviting new users to join.",[2289,19240,19241,19249],{},[2292,19242,19243],{},[2295,19244,19245,19247],{},[2298,19246,3541],{},[2298,19248,3348],{},[2305,19250,19251,19263,19276,19288,19312,19324,19337,19349],{},[2295,19252,19253,19258],{},[2310,19254,19255],{},[18,19256,19257],{},"email.enabled",[2310,19259,19260,19261],{},"Enables the email sending functionality of the platform. Default: ",[18,19262,659],{},[2295,19264,19265,19270],{},[2310,19266,19267],{},[18,19268,19269],{},"email.from",[2310,19271,19272,19273],{},"Sets the address email will appear from. Default: ",[18,19274,19275],{},"\"FlowFuse\" \u003Cdonotreply@flowforge.com>",[2295,19277,19278,19283],{},[2310,19279,19280],{},[18,19281,19282],{},"email.smtp.host",[2310,19284,19285,19286],{},"Hostname of the SMTP server to send email through. Default: ",[18,19287,18615],{},[2295,19289,19290,19295],{},[2310,19291,19292],{},[18,19293,19294],{},"email.smtp.port",[2310,19296,19297,19298,19301,19302,19305,19306,3012,19308,19311],{},"Port of the SMTP server to send email through. Default: ",[18,19299,19300],{},"587"," if ",[18,19303,19304],{},"secure"," is ",[18,19307,659],{},[18,19309,19310],{},"465"," otherwise",[2295,19313,19314,19319],{},[2310,19315,19316],{},[18,19317,19318],{},"email.smtp.secure",[2310,19320,19321,19322],{},"Whether to use TLS to connect to the SMTP server. Default: ",[18,19323,659],{},[2295,19325,19326,19331],{},[2310,19327,19328],{},[18,19329,19330],{},"email.smtp.auth.user",[2310,19332,19333,19334],{},"Username to authenticate the connection with. Default: ",[18,19335,19336],{},"unset",[2295,19338,19339,19344],{},[2310,19340,19341],{},[18,19342,19343],{},"email.smtp.auth.pass",[2310,19345,19346,19347],{},"Password to authenticate the connection with. Default: ",[18,19348,19336],{},[2295,19350,19351,19355],{},[2310,19352,19353],{},[18,19354,8065],{},[2310,19356,19357,19358],{},"If set to true, it will log the full content of emails it tries to send. Default: ",[18,19359,659],{},[14,19361,4611,19362,19365],{},[41,19363,785],{"href":19364},"\u002Fdocs\u002Finstall\u002Femail_providers"," for example configuration with common email providers.",[104,19367,19369],{"id":19368},"aws-ses-email","AWS SES Email",[14,19371,19372],{},"There is also support for using AWS SES email, this is mainly intended to be used when deployed on AWS EKS.",[14,19374,19375],{},"This assumes that the instance is running with a Service Account that has a AWS Role with SES access enabled.",[2289,19377,19378,19386],{},[2292,19379,19380],{},[2295,19381,19382,19384],{},[2298,19383,3541],{},[2298,19385,3348],{},[2305,19387,19388,19400,19412],{},[2295,19389,19390,19395],{},[2310,19391,19392],{},[18,19393,19394],{},"email.ses.region",[2310,19396,19397,19398],{},"The AWS region to connect to. Default ",[18,19399,19336],{},[2295,19401,19402,19407],{},[2310,19403,19404],{},[18,19405,19406],{},"email.ses.sourceArn",[2310,19408,19409,19410],{},"The AWS ARN of a SES Identity to send email as. Default: ",[18,19411,19336],{},[2295,19413,19414,19419],{},[2310,19415,19416],{},[18,19417,19418],{},"email.ses.fromArn",[2310,19420,19421,19422],{},"The AWS ARN of a SES Identity to set as the from field. Default to value of ",[18,19423,19406],{},[23,19425,19427],{"id":19426},"telemetry-configuration","Telemetry configuration",[14,19429,19430,19431,16608,19433],{},"By default, the platform will send anonymous usage information back to us at FlowForge Inc.\nThis can be disabled via the Admin Settings in the UI, or turned off in the configuration file with the ",[18,19432,3552],{},[364,19434,2940],{},[14,19436,19437],{},"Additionally, you can configure your own instance of FlowFuse to report back to you on how users are using your instance of FlowFuse. FlowFuse supports integration with two different services:",[28,19439,19440,19449],{},[31,19441,19442,3497,19445,3501,19447,3505],{},[41,19443,405],{"href":3495,"rel":19444},[831],[1160,19446,3500],{},[18,19448,3504],{},[31,19450,19451,179,19454,3526,19456,19458],{},[41,19452,3522],{"href":3520,"rel":19453},[831],[1160,19455,3525],{},[18,19457,3504],{}," in the telemetry configuration",[14,19460,19461,19462],{},"For more information about this feature, see ",[41,19463,785],{"href":596},[2289,19465,19466,19474],{},[2292,19467,19468],{},[2295,19469,19470,19472],{},[2298,19471,3541],{},[2298,19473,3348],{},[2305,19475,19476,19486,19508,19518,19534,19546],{},[2295,19477,19478,19482],{},[2310,19479,19480],{},[18,19481,3552],{},[2310,19483,3555,19484],{},[18,19485,3558],{},[2295,19487,19488,19493],{},[2310,19489,19490],{},[18,19491,19492],{},"telemetry.frontend.posthog.apiurl",[2310,19494,19495,19496,19500,19501,19505,19506],{},"The API URL for PostHog, either '",[41,19497,19498],{"href":19498,"rel":19499},"https:\u002F\u002Fapp.posthog.com",[831],"' or '",[41,19502,19503],{"href":19503,"rel":19504},"https:\u002F\u002Feu.posthog.com",[831],"'. Default: ",[18,19507,19498],{},[2295,19509,19510,19514],{},[2310,19511,19512],{},[18,19513,3578],{},[2310,19515,3581,19516],{},[18,19517,3571],{},[2295,19519,19520,19524],{},[2310,19521,19522],{},[18,19523,3590],{},[2310,19525,3593,19526,3597,19528,3601,19530,3605,19532],{},[18,19527,3596],{},[18,19529,3600],{},[18,19531,3604],{},[18,19533,3558],{},[2295,19535,19536,19541],{},[2310,19537,19538],{},[18,19539,19540],{},"telemetry.frontend.google.tag",[2310,19542,19543,19544],{},"A Google Tag Manager ID. Default: ",[18,19545,3571],{},[2295,19547,19548,19553],{},[2310,19549,19550],{},[18,19551,19552],{},"telemetry.frontend.google.events",[2310,19554,19555,19556],{},"An object with keys matching the names of tag events to be enabled and any payload values. Default ",[18,19557,3571],{},[23,19559,19561],{"id":19560},"rate-limiting-configuration","Rate Limiting configuration",[14,19563,19564],{},"By default, rate limiting is disabled and the platform will not rate limit any requests.",[14,19566,19567,19568,8066,19571,273],{},"To enable rate limiting, you can set the ",[18,19569,19570],{},"rate_limits.enabled",[18,19572,3558],{},[14,19574,19575],{},"When enabled, all routes will be limited to 1000 requests per 1 minute window. These defaults can be adjusted by setting values in the configuration options listed below.",[2289,19577,19578,19586],{},[2292,19579,19580],{},[2295,19581,19582,19584],{},[2298,19583,3541],{},[2298,19585,3348],{},[2305,19587,19588,19599,19612,19626,19638],{},[2295,19589,19590,19594],{},[2310,19591,19592],{},[18,19593,19570],{},[2310,19595,19596,19597],{},"Enables rate limiting. Default: ",[18,19598,659],{},[2295,19600,19601,19606],{},[2310,19602,19603],{},[18,19604,19605],{},"rate_limits.global",[2310,19607,19608,19609,19611],{},"Enables rate limiting for all routes. Default: ",[18,19610,3558],{}," (defaults to all routes being rate limited)",[2295,19613,19614,19619],{},[2310,19615,19616],{},[18,19617,19618],{},"rate_limits.timeWindow",[2310,19620,19621,19622,19625],{},"The time window in which requests are counted. Default: ",[18,19623,19624],{},"60000"," (1 minute)",[2295,19627,19628,19633],{},[2310,19629,19630],{},[18,19631,19632],{},"rate_limits.max",[2310,19634,19635,19636],{},"The maximum number of requests allowed in the time window. Default: ",[18,19637,5687],{},[2295,19639,19640,19645],{},[2310,19641,19642],{},[18,19643,19644],{},"rate_limits.maxAnonymous",[2310,19646,19647,19648,660],{},"The maximum number of requests allowed in the time window for anonymous users. Default: not configured (defaults to ",[18,19649,19632],{},[14,19651,19652,19653,19658],{},"For additional options, see ",[41,19654,19657],{"href":19655,"rel":19656},"https:\u002F\u002Fgithub.com\u002Ffastify\u002Ffastify-rate-limit#options",[831],"fastify-rate-limit"," documentation.",[23,19660,19662],{"id":19661},"session-timeouts","Session timeouts",[14,19664,19665],{},"Allows control of the maximum user session life.",[2289,19667,19668,19676],{},[2292,19669,19670],{},[2295,19671,19672,19674],{},[2298,19673,3541],{},[2298,19675,3348],{},[2305,19677,19678,19692],{},[2295,19679,19680,19685],{},[2310,19681,19682],{},[18,19683,19684],{},"sessions.maxDuration",[2310,19686,19687,19688,19691],{},"The maximum number of seconds a user session can last. Default: ",[18,19689,19690],{},"604800"," (1 week)",[2295,19693,19694,19699],{},[2310,19695,19696],{},[18,19697,19698],{},"sessions.maxIdleDuration",[2310,19700,19701,19702,18835,19704,19707],{},"The maximum number of seconds a session can be idle. Must be less than ",[18,19703,19684],{},[18,19705,19706],{},"115200"," (32 hours)",[23,19709,19711],{"id":19710},"support-configuration","Support configuration",[14,19713,19714,19715,19720],{},"It is possible to add a ",[41,19716,19719],{"href":19717,"rel":19718},"https:\u002F\u002Fknowledge.hubspot.com\u002Fchatflows\u002Fcreate-a-live-chat",[831],"HubSpot Support Widget"," into FlowFuse. This will appear as a floating chat bubble on the bottom-right corner of the screen. To enable this, you'll need to provide the",[2289,19722,19723,19731],{},[2292,19724,19725],{},[2295,19726,19727,19729],{},[2298,19728,3541],{},[2298,19730,3348],{},[2305,19732,19733,19745],{},[2295,19734,19735,19740],{},[2310,19736,19737],{},[18,19738,19739],{},"support.enabled",[2310,19741,19742,19743],{},"Enables the chat support widget in the UI. Default: ",[18,19744,659],{},[2295,19746,19747,19752],{},[2310,19748,19749],{},[18,19750,19751],{},"support.frontend.hubspot.trackingcode",[2310,19753,19754,19755,18835,19760],{},"The numerical identifier within your ",[41,19756,19759],{"href":19757,"rel":19758},"https:\u002F\u002Fknowledge.hubspot.com\u002Fconversations\u002Fchat-widget-is-not-appearing-on-your-pages",[831],"HubSpot Tracking Code",[18,19761,3571],{},[23,19763,19184],{"id":19764},"mqtt-broker-configuration-1",[14,19766,9528,19767,9534],{},[41,19768,9533],{"href":9531,"rel":19769},[831],[14,19771,9537,19772,19774],{},[1160,19773,9540],{}," component - the platform will work without the\nbroker, but some features will not be available.",[2289,19776,19777,19785],{},[2292,19778,19779],{},[2295,19780,19781,19783],{},[2298,19782,3541],{},[2298,19784,3348],{},[2305,19786,19787,19799],{},[2295,19788,19789,19793],{},[2310,19790,19791],{},[18,19792,19209],{},[2310,19794,19795,19796,273],{},"The full url to the platform broker. This is used by the platform and Node-RED instances to connect to the broker. For example: ",[18,19797,19798],{},"mqtt:\u002F\u002Flocalhost:1883",[2295,19800,19801,19805],{},[2310,19802,19803],{},[18,19804,19222],{},[2310,19806,19807,19808],{},"If set, this is the url provided to Devices to connect to the broker with. When running in a Docker or K8S environment, this url should be the externally addressable url the broker is provided on. This could be via WebSockets, for example: ",[18,19809,19810],{},"ws:\u002F\u002Fexample.com:1884",[23,19812,19814],{"id":19813},"ai-configuration","AI Configuration",[2289,19816,19817,19825],{},[2292,19818,19819],{},[2295,19820,19821,19823],{},[2298,19822,3541],{},[2298,19824,3348],{},[2305,19826,19827],{},[2295,19828,19829,19834],{},[2310,19830,19831],{},[18,19832,19833],{},"ai.enabled",[2310,19835,19836,19837,19839,19840],{},"Enables all AI features on the platform (still requires an Enterprise License). When set to ",[18,19838,659],{},", all AI functionality is disabled regardless of individual feature configuration (assistant, expert, inline completions, snapshot descriptions). Default: ",[18,19841,3558],{},[23,19843,19845],{"id":19844},"ff-tables","FF Tables",[2289,19847,19848,19856],{},[2292,19849,19850],{},[2295,19851,19852,19854],{},[2298,19853,3541],{},[2298,19855,3348],{},[2305,19857,19858,19870,19887],{},[2295,19859,19860,19865],{},[2310,19861,19862],{},[18,19863,19864],{},"tables.enables",[2310,19866,19867,19868],{},"Enables the FlowFuse Tables feature. Default: ",[18,19869,659],{},[2295,19871,19872,19877],{},[2310,19873,19874],{},[18,19875,19876],{},"tables.driver.type",[2310,19878,19879,19880,1706,19883,19886],{},"Selects the FF Tables driver to use (",[18,19881,19882],{},"postgres-localfs",[18,19884,19885],{},"postgres-supavisor","). Default: not set",[2295,19888,19889,19894],{},[2310,19890,19891],{},[18,19892,19893],{},"tables.driver.options",[2310,19895,19896],{},"Configuration options for the driver. Details depend on the driver used. Default: not set",[14,19898,19899,19900,19902],{},"The options for the ",[18,19901,19882],{}," driver require connection details for an admin user on a Postgres instance e.g.",[50,19904,19907],{"className":19905,"code":19906,"language":3920},[3918],"database:\n  user: root\n  password: password\n  host: localhost\n  port: 5432\n  database: postgres\n  ssl: false\n\n",[18,19908,19906],{"__ignoreMap":55},[23,19910,19912],{"id":19911},"logging-configuration","Logging configuration",[14,19914,19915,19916,19919,19920],{},"By default the forge app is set to ",[18,19917,19918],{},"info"," level logging, with the HTTP routes logged at ",[18,19921,19922],{},"warn",[2289,19924,19925,19933],{},[2292,19926,19927],{},[2295,19928,19929,19931],{},[2298,19930,3541],{},[2298,19932,3348],{},[2305,19934,19935,19947,19959],{},[2295,19936,19937,19942],{},[2310,19938,19939],{},[18,19940,19941],{},"logging.level",[2310,19943,19944,19945],{},"Change the default logging level. Default: ",[18,19946,19918],{},[2295,19948,19949,19954],{},[2310,19950,19951],{},[18,19952,19953],{},"logging.http",[2310,19955,19956,19957],{},"Change the default HTTP route logging level. Default: ",[18,19958,19922],{},[2295,19960,19961,19966],{},[2310,19962,19963],{},[18,19964,19965],{},"logging.pretty",[2310,19967,19968,19969,19971],{},"Enable\u002FDisable pretty-printing of the log output. Default: ",[18,19970,659],{}," - see below",[14,19973,19974,19975,470,19977,19979],{},"Setting ",[18,19976,19953],{},[18,19978,19918],{}," will log every HTTP request and response details.",[14,19981,372,19982,19985,19986,19988,19989,19992],{},[18,19983,19984],{},"pretty"," option controls the formatting of the log output. When running in developer\nmode, (for example, if ",[18,19987,3755],{}," is set to ",[18,19990,19991],{},"developer","), then pretty formatting is enabled\nby default. This makes the logs more human-readable.",[14,19994,19995],{},"Otherwise, the log output is JSON formatted for consumption by other tools.",[23,19997,19999],{"id":19998},"file-storage","File storage",[14,20001,20002],{},"FlowFuse includes a service that can be used by Node-RED instances to read and write files\nin their flows as well as providing persistent storage for flow context information.",[14,20004,20005,20006,273],{},"Details of configuring the File Storage service are available ",[41,20007,785],{"href":20008},"\u002Fdocs\u002Finstall\u002Ffile-storage",[14,20010,20011,20012,20014],{},"The main ",[18,20013,576],{}," file needs to contain the following properties so it\ncan access the File server.",[2289,20016,20017,20025],{},[2292,20018,20019],{},[2295,20020,20021,20023],{},[2298,20022,3541],{},[2298,20024,3348],{},[2305,20026,20027],{},[2295,20028,20029,20034],{},[2310,20030,20031],{},[18,20032,20033],{},"fileStore.url",[2310,20035,20036],{},"The URL of the FlowFuse File Server to use. Default: not set",[23,20038,20040],{"id":20039},"enabling-persistent-file-storage-file-nodes","Enabling Persistent File Storage - File Nodes",[14,20042,20043],{},"These nodes are enabled by default on the FlowFuse Cloud platform. If you're\nrunning a self-hosted environment you should follow the next steps.",[14,20045,20046],{},"FlowFuse file nodes replace the core Node-RED file nodes. To make use of these\nnodes, the FlowFuse platform Administrator must ensure the core file nodes are\nnot loaded.",[14,20048,20049,20050,20053,20054,20057,20058,1497],{},"This is done by adding ",[18,20051,20052],{},"10-file.js"," in the ",[364,20055,20056],{},"Exclude nodes by filename","\nsection of your instance settings under the ",[364,20059,4799],{},[14,20061,20062,20063,20067],{},"This setting is modifiable only by a Team owner and only if it has not been\nlocked in the ",[41,20064,20066],{"href":20065},"\u002Fdocs\u002Fuser\u002Fconcepts.md#template","template"," by the platform Administrator.",[14,20069,20070,20074],{},[41,20071,20073],{"href":20072},"\u002Fdocs\u002Fuser\u002Ffilenodes.md","Click here",", to learn more about the usage of the FlowFuse File Nodes.",[23,20076,20078],{"id":20077},"content-security-policy","Content Security Policy",[2289,20080,20081,20089],{},[2292,20082,20083],{},[2295,20084,20085,20087],{},[2298,20086,3541],{},[2298,20088,3348],{},[2305,20090,20091,20107,20121,20133],{},[2295,20092,20093,20098],{},[2310,20094,20095],{},[18,20096,20097],{},"content_security_policy.enabled",[2310,20099,20100,20101,20104,20105],{},"Enabled ",[18,20102,20103],{},"Content-Security-Policy"," headers. Default: ",[18,20106,659],{},[2295,20108,20109,20114],{},[2310,20110,20111],{},[18,20112,20113],{},"content_security_policy.directives",[2310,20115,20116,20117],{},"Overrides the default set of directives, supplied as a JSON object defined by HelmetJS ",[41,20118,785],{"href":20119,"rel":20120},"https:\u002F\u002Fhelmetjs.github.io\u002F#content-security-policy",[831],[2295,20122,20123,20128],{},[2310,20124,20125],{},[18,20126,20127],{},"content_security_policy.report_only",[2310,20129,20130,20131],{},"Enables reporting only mode. Default: ",[18,20132,659],{},[2295,20134,20135,20140],{},[2310,20136,20137],{},[18,20138,20139],{},"content_security_policy.report_uri",[2310,20141,20142],{},"Provides at URI for reporting to be sent to if enabled",[23,20144,20146],{"id":20145},"npm-registry","NPM Registry",[14,20148,20149],{},"These settings enable per team Node-RED private catalogue generation",[2289,20151,20152,20160],{},[2292,20153,20154],{},[2295,20155,20156,20158],{},[2298,20157,3541],{},[2298,20159,3348],{},[2305,20161,20162,20174,20184,20194],{},[2295,20163,20164,20169],{},[2310,20165,20166],{},[18,20167,20168],{},"npmRegistry.enabled",[2310,20170,20171,20172],{},"Enables NPM Registry support. Default: ",[18,20173,659],{},[2295,20175,20176,20181],{},[2310,20177,20178],{},[18,20179,20180],{},"npmRegistry.url",[2310,20182,20183],{},"The URL for the Verdaccio NPM Registry. Default: none",[2295,20185,20186,20191],{},[2310,20187,20188],{},[18,20189,20190],{},"npmRegistry.admin.username",[2310,20192,20193],{},"Username for Verdaccio admin user",[2295,20195,20196,20201],{},[2310,20197,20198],{},[18,20199,20200],{},"npmRegistry.admin.password",[2310,20202,20203],{},"Password for Verdaccio admin user",[23,20205,20207],{"id":20206},"blueprint-updates","BluePrint Updates",[14,20209,20210],{},"For Licenses instances these options control how new BluePrints are imported",[14,20212,20213,20216,20217,20219,20222,20223,20225,20228,20229],{},[18,20214,20215],{},"blueprintImport.enabled"," | Enables the import of new BluePrints from FlowFuse. Default: ",[18,20218,3558],{},[18,20220,20221],{},"blueprintImport.export"," | Enables the API endpoint to export the local BluePrints. Default: ",[18,20224,659],{},[18,20226,20227],{},"blueprintImport.url"," | The URL to import BluePrints from. Default: ",[18,20230,20231],{},"https:\u002F\u002Fapp.flowfuse.com\u002Fapi\u002Fv1\u002Fflow-blueprints\u002Fexport-public",{"title":55,"searchDepth":77,"depth":77,"links":20233},[20234,20235,20239,20244,20245,20248,20249,20250,20251,20252,20253,20254,20255,20256,20257,20258,20259,20260],{"id":18590,"depth":77,"text":18591},{"id":18770,"depth":77,"text":18771,"children":20236},[20237,20238],{"id":18808,"depth":88,"text":18809},{"id":18841,"depth":88,"text":18842},{"id":18913,"depth":77,"text":18914,"children":20240},[20241,20242,20243],{"id":18944,"depth":88,"text":18945},{"id":19009,"depth":88,"text":19010},{"id":19058,"depth":88,"text":19059},{"id":19183,"depth":77,"text":19184},{"id":19234,"depth":77,"text":19235,"children":20246},[20247],{"id":19368,"depth":88,"text":19369},{"id":19426,"depth":77,"text":19427},{"id":19560,"depth":77,"text":19561},{"id":19661,"depth":77,"text":19662},{"id":19710,"depth":77,"text":19711},{"id":19764,"depth":77,"text":19184},{"id":19813,"depth":77,"text":19814},{"id":19844,"depth":77,"text":19845},{"id":19911,"depth":77,"text":19912},{"id":19998,"depth":77,"text":19999},{"id":20039,"depth":77,"text":20040},{"id":20077,"depth":77,"text":20078},{"id":20145,"depth":77,"text":20146},{"id":20206,"depth":77,"text":20207},"The base configuration of the FlowFuse platform is provided in the file\n\u002Fopt\u002Fflowforge\u002Fetc\u002Fflowforge.yml. This assumes the default install location\nof \u002Fopt\u002Fflowforge.",{},"install\u002Fconfiguration.md","\u002Fdocs\u002Finstall\u002Fconfiguration",{"title":7820,"description":20261},"docs\u002Finstall\u002Fconfiguration","Hq8BvgLTb51fA12gfQGw4vpTrIWW4JVbXpLeRjgNLuk",{"id":20269,"title":20270,"body":20271,"description":20278,"extension":329,"layout":330,"meta":21006,"navGroup":330,"navOrder":330,"navTitle":20270,"navigation":187,"originalPath":21007,"path":21008,"redirect":330,"seo":21009,"stem":21010,"updated":337,"version":338,"__hash__":21011},"docs\u002Fdocs\u002Finstall\u002Fdns-setup.md","DNS Setup",{"type":7,"value":20272,"toc":20995},[20273,20276,20279,20282,20291,20301,20305,20308,20317,20320,20345,20349,20352,20356,20359,20363,20366,20369,20373,20376,20379,20383,20394,20517,20521,20527,20646,20648,20651,20654,20661,20682,20690,20704,20715,20718,20721,20751,20754,20782,20785,20814,20817,20873,20876,20894,20898,20901,20911,20926,20932,20935,20950,20957,20961,20964,20972,20985,20992],[10,20274,20270],{"id":20275},"dns-setup",[14,20277,20278],{},"When running FlowFuse on Docker or Kubernetes you will need to be able to setup an entry in a DNS server.",[14,20280,20281],{},"This is because FlowFuse uses hostname based routing to know which Node-RED instance you want to access.",[14,20283,20284,20285,20287,20288,15415],{},"By default the instance name is used in combination with a supplied domain. In this document we will use ",[18,20286,1312],{}," as the domain. (It doesn't need to be a \"whole\" domain, it could also be a sub domain of one you already own. e.g. ",[18,20289,20290],{},"ff.example.com",[14,20292,20293,20294,20297,20298,20300],{},"If you are running Docker\u002FKubernetes on the same machine as the DNS server and Web Browser do not use ",[18,20295,20296],{},"127.0.0.1"," as the IP address to point the wild card domain at. This is because the host names will be looked up by the Web Browser, the Forge Application, and the Node-RED instances. The last 2 are running in containers and ",[18,20299,20296],{}," will resolve to the container, not the entry point.",[23,20302,20304],{"id":20303},"production","Production",[14,20306,20307],{},"For a production deployment you will need to have access to modify DNS, if you are not sure how to set up DNS records talk to whoever manages your DNS.",[14,20309,20310,20311,20316],{},"As mentioned earlier you will need them to create a ",[41,20312,20315],{"href":20313,"rel":20314},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FWildcard_DNS_record",[831],"wildcard DNS"," entry that points to either the Docker host machine or the Kubernetes Nodes which are running the Ingress Controller.",[14,20318,20319],{},"This can be either:",[28,20321,20322,20336],{},[31,20323,20324,20325,20328,20329,20332,20333],{},"a ",[18,20326,20327],{},"A"," (and ",[18,20330,20331],{},"AAAA"," for IPv6) record pointing to an IP address e.g. ",[18,20334,20335],{},"*.example.com 8600 A 192.0.2.1",[31,20337,20324,20338,20341,20342],{},[18,20339,20340],{},"CNAME"," record pointing to the hostname of the entry point e.g. ",[18,20343,20344],{},"*.ff.example.com 8600 CNAME forge.example.com",[104,20346,20348],{"id":20347},"aws-alb-ingress","AWS ALB Ingress",[14,20350,20351],{},"When using AWS ALB (Application Load Balancer) as an Ingress Controller for FlowFuse deployed into an EKS cluster then you would create a wildcard CNAME entry pointing to the hostname of the ALB",[104,20353,20355],{"id":20354},"digital-ocean","Digital Ocean",[14,20357,20358],{},"You should create an A record pointing to the public IP address of the Load Balancer created when you install the Traefik Helm Chart.",[23,20360,20362],{"id":20361},"local-testing-and-development","Local Testing and Development",[14,20364,20365],{},"For development and testing we probably only need to set up DNS entries for the developers local machine. The easiest way to do this is to use an application called dnsmasq.",[14,20367,20368],{},"Dnsmasq is a tool that can be used as a DNS caching proxy (and a DHCP server, but we don't need that). We can set it up to point to an upstream DNS server to resolve all normal addresses, but we can also give it a list of hostname\u002FIP address pairs to use locally.",[104,20370,20372],{"id":20371},"dnsmasq","DNSMasq",[14,20374,20375],{},"Setting up dnsmasq is not too complex, what is harder is setting it up in a way that works well with the network configuration on a laptop that might move between different networks and expects to get its default DNS configuration automatically assigned by DHCP.",[14,20377,20378],{},"The following headings cover how to do this on a number of different operating systems",[768,20380,20382],{"id":20381},"ubuntu","Ubuntu",[14,20384,20385,20386,20389,20390,20393],{},"For Docker on Linux you can use ",[18,20387,20388],{},"172.17.0.1"," as the address for the domain which is the IP address assigned to the ",[18,20391,20392],{},"docker0"," interface.",[50,20395,20397],{"className":52,"code":20396,"language":54,"meta":55,"style":55},"sudo apt-get install dnsmasq\nsudo echo \"bind-interfaces\" >> \u002Fetc\u002Fdnsmasq.conf\nsudo echo \"no-resolv\" >> \u002Fetc\u002Fdnsmasq.conf\nsudo echo \"conf-dir=\u002Fetc\u002Fdnsmasq.d\" >> \u002Fetc\u002Fdnsmasq.conf\nsudo echo \"address=\u002Fexample.com\u002F172.17.0.1\" > \u002Fetc\u002Fdnsmasq.d\u002F02-flowforge.conf\nsudo service dnsmasq restart\nsudo echo \"DNS=127.0.0.1\" >> \u002Fetc\u002Fsystemd\u002Fresolved.conf\nsudo echo \"DOMAINS=~example.com\" >> \u002Fetc\u002Fsystemd\u002Fresolved.conf\nsudo service systemd-resolved restart\n",[18,20398,20399,20411,20427,20440,20453,20468,20479,20493,20506],{"__ignoreMap":55},[59,20400,20401,20403,20406,20408],{"class":61,"line":62},[59,20402,9188],{"class":65},[59,20404,20405],{"class":69}," apt-get",[59,20407,7956],{"class":69},[59,20409,20410],{"class":69}," dnsmasq\n",[59,20412,20413,20415,20418,20421,20424],{"class":61,"line":77},[59,20414,9188],{"class":65},[59,20416,20417],{"class":69}," echo",[59,20419,20420],{"class":69}," \"bind-interfaces\"",[59,20422,20423],{"class":1372}," >>",[59,20425,20426],{"class":69}," \u002Fetc\u002Fdnsmasq.conf\n",[59,20428,20429,20431,20433,20436,20438],{"class":61,"line":88},[59,20430,9188],{"class":65},[59,20432,20417],{"class":69},[59,20434,20435],{"class":69}," \"no-resolv\"",[59,20437,20423],{"class":1372},[59,20439,20426],{"class":69},[59,20441,20442,20444,20446,20449,20451],{"class":61,"line":99},[59,20443,9188],{"class":65},[59,20445,20417],{"class":69},[59,20447,20448],{"class":69}," \"conf-dir=\u002Fetc\u002Fdnsmasq.d\"",[59,20450,20423],{"class":1372},[59,20452,20426],{"class":69},[59,20454,20455,20457,20459,20462,20465],{"class":61,"line":156},[59,20456,9188],{"class":65},[59,20458,20417],{"class":69},[59,20460,20461],{"class":69}," \"address=\u002Fexample.com\u002F172.17.0.1\"",[59,20463,20464],{"class":1372}," >",[59,20466,20467],{"class":69}," \u002Fetc\u002Fdnsmasq.d\u002F02-flowforge.conf\n",[59,20469,20470,20472,20474,20477],{"class":61,"line":216},[59,20471,9188],{"class":65},[59,20473,9801],{"class":69},[59,20475,20476],{"class":69}," dnsmasq",[59,20478,12772],{"class":69},[59,20480,20481,20483,20485,20488,20490],{"class":61,"line":224},[59,20482,9188],{"class":65},[59,20484,20417],{"class":69},[59,20486,20487],{"class":69}," \"DNS=127.0.0.1\"",[59,20489,20423],{"class":1372},[59,20491,20492],{"class":69}," \u002Fetc\u002Fsystemd\u002Fresolved.conf\n",[59,20494,20495,20497,20499,20502,20504],{"class":61,"line":233},[59,20496,9188],{"class":65},[59,20498,20417],{"class":69},[59,20500,20501],{"class":69}," \"DOMAINS=~example.com\"",[59,20503,20423],{"class":1372},[59,20505,20492],{"class":69},[59,20507,20508,20510,20512,20515],{"class":61,"line":241},[59,20509,9188],{"class":65},[59,20511,9801],{"class":69},[59,20513,20514],{"class":69}," systemd-resolved",[59,20516,12772],{"class":69},[768,20518,20520],{"id":20519},"fedora","Fedora",[14,20522,20385,20523,20389,20525,20393],{},[18,20524,20388],{},[18,20526,20392],{},[50,20528,20530],{"className":52,"code":20529,"language":54,"meta":55,"style":55},"sudo dnf install dnsmasq\nsudo echo \"bind-interfaces\" >> \u002Fetc\u002Fdnsmasq.conf\nsudo echo \"no-resolv\" >> \u002Fetc\u002Fdnsmasq.conf\nsudo echo \"conf-dir=\u002Fetc\u002Fdnsmasq.d\" >> \u002Fetc\u002Fdnsmasq.conf\nsudo echo \"address=\u002Fexample.com\u002F172.17.0.1\" > \u002Fetc\u002Fdnsmasq.d\u002F02-flowforge.conf\nsudo systemctl enable dnsmasq.service\nsudo service dnsmasq restart\nsudo echo \"DNS=127.0.0.1\" >> \u002Fetc\u002Fsystemd\u002Fresolved.conf\nsudo echo \"DOMAINS=~example.com\" >> \u002Fetc\u002Fsystemd\u002Fresolved.conf\nsudo service systemd-resolved restart\n",[18,20531,20532,20543,20555,20567,20579,20591,20602,20612,20624,20636],{"__ignoreMap":55},[59,20533,20534,20536,20539,20541],{"class":61,"line":62},[59,20535,9188],{"class":65},[59,20537,20538],{"class":69}," dnf",[59,20540,7956],{"class":69},[59,20542,20410],{"class":69},[59,20544,20545,20547,20549,20551,20553],{"class":61,"line":77},[59,20546,9188],{"class":65},[59,20548,20417],{"class":69},[59,20550,20420],{"class":69},[59,20552,20423],{"class":1372},[59,20554,20426],{"class":69},[59,20556,20557,20559,20561,20563,20565],{"class":61,"line":88},[59,20558,9188],{"class":65},[59,20560,20417],{"class":69},[59,20562,20435],{"class":69},[59,20564,20423],{"class":1372},[59,20566,20426],{"class":69},[59,20568,20569,20571,20573,20575,20577],{"class":61,"line":99},[59,20570,9188],{"class":65},[59,20572,20417],{"class":69},[59,20574,20448],{"class":69},[59,20576,20423],{"class":1372},[59,20578,20426],{"class":69},[59,20580,20581,20583,20585,20587,20589],{"class":61,"line":156},[59,20582,9188],{"class":65},[59,20584,20417],{"class":69},[59,20586,20461],{"class":69},[59,20588,20464],{"class":1372},[59,20590,20467],{"class":69},[59,20592,20593,20595,20597,20599],{"class":61,"line":216},[59,20594,9188],{"class":65},[59,20596,12651],{"class":69},[59,20598,15085],{"class":69},[59,20600,20601],{"class":69}," dnsmasq.service\n",[59,20603,20604,20606,20608,20610],{"class":61,"line":224},[59,20605,9188],{"class":65},[59,20607,9801],{"class":69},[59,20609,20476],{"class":69},[59,20611,12772],{"class":69},[59,20613,20614,20616,20618,20620,20622],{"class":61,"line":233},[59,20615,9188],{"class":65},[59,20617,20417],{"class":69},[59,20619,20487],{"class":69},[59,20621,20423],{"class":1372},[59,20623,20492],{"class":69},[59,20625,20626,20628,20630,20632,20634],{"class":61,"line":241},[59,20627,9188],{"class":65},[59,20629,20417],{"class":69},[59,20631,20501],{"class":69},[59,20633,20423],{"class":1372},[59,20635,20492],{"class":69},[59,20637,20638,20640,20642,20644],{"class":61,"line":249},[59,20639,9188],{"class":65},[59,20641,9801],{"class":69},[59,20643,20514],{"class":69},[59,20645,12772],{"class":69},[768,20647,9136],{"id":12283},[14,20649,20650],{},"Unfortunately dnsmasq will not run on Windows and I have not found something similar yet.",[768,20652,9142],{"id":20653},"macos",[14,20655,20656,20657,20660],{},"On MacOS you can alias a private IP address to the loop back interface e.g. ",[18,20658,20659],{},"10.128.0.1"," with",[50,20662,20664],{"className":52,"code":20663,"language":54,"meta":55,"style":55},"sudo ifconfig lo0 alias 10.128.0.1\n",[18,20665,20666],{"__ignoreMap":55},[59,20667,20668,20670,20673,20676,20679],{"class":61,"line":62},[59,20669,9188],{"class":65},[59,20671,20672],{"class":69}," ifconfig",[59,20674,20675],{"class":69}," lo0",[59,20677,20678],{"class":69}," alias",[59,20680,20681],{"class":73}," 10.128.0.1\n",[14,20683,20684,20685],{},"You will need install dnsmasq using ",[41,20686,20689],{"href":20687,"rel":20688},"https:\u002F\u002Fdocs.brew.sh\u002FInstallation",[831],"homebrew",[50,20691,20693],{"className":52,"code":20692,"language":54,"meta":55,"style":55},"brew install dnsmasq\n",[18,20694,20695],{"__ignoreMap":55},[59,20696,20697,20700,20702],{"class":61,"line":62},[59,20698,20699],{"class":65},"brew",[59,20701,7956],{"class":69},[59,20703,20410],{"class":69},[14,20705,20706,20707,20710,20711,20714],{},"It appears that the install location differs based on the Apple Hardware. For Intel hardware Macs it's in ",[18,20708,20709],{},"\u002Fusr\u002Flocal"," and for M1 hardware it's ",[18,20712,20713],{},"\u002Fopt\u002Fhomebrew",". Please check where it installed things when the previous command has completed.",[14,20716,20717],{},"Then edit a configuration file",[14,20719,20720],{},"M1 mac",[50,20722,20724],{"className":52,"code":20723,"language":54,"meta":55,"style":55},"echo \"conf-dir=\u002Fopt\u002Fhomebrew\u002Fetc\u002Fdnsmasq.d\" >> \u002Fopt\u002Fhomebrew\u002Fetc\u002Fdnsmasq.conf\necho \"address=\u002Fexample.com\u002F10.128.0.1\" > \u002Fopt\u002Fhomebrew\u002Fetc\u002Fdnsmasq.d\u002Fff.conf\n",[18,20725,20726,20739],{"__ignoreMap":55},[59,20727,20728,20731,20734,20736],{"class":61,"line":62},[59,20729,20730],{"class":73},"echo",[59,20732,20733],{"class":69}," \"conf-dir=\u002Fopt\u002Fhomebrew\u002Fetc\u002Fdnsmasq.d\"",[59,20735,20423],{"class":1372},[59,20737,20738],{"class":69}," \u002Fopt\u002Fhomebrew\u002Fetc\u002Fdnsmasq.conf\n",[59,20740,20741,20743,20746,20748],{"class":61,"line":77},[59,20742,20730],{"class":73},[59,20744,20745],{"class":69}," \"address=\u002Fexample.com\u002F10.128.0.1\"",[59,20747,20464],{"class":1372},[59,20749,20750],{"class":69}," \u002Fopt\u002Fhomebrew\u002Fetc\u002Fdnsmasq.d\u002Fff.conf\n",[14,20752,20753],{},"Intel mac",[50,20755,20757],{"className":52,"code":20756,"language":54,"meta":55,"style":55},"echo \"conf-dir=\u002Fusr\u002Flocal\u002Fetc\u002Fdnsmasq.d\" >> \u002Fusr\u002Flocal\u002Fetc\u002Fdnsmasq.conf\necho \"address=\u002Fexample.com\u002F10.128.0.1\" > \u002Fusr\u002Flocal\u002Fetc\u002Fdnsmasq.d\u002Fff.conf\n",[18,20758,20759,20771],{"__ignoreMap":55},[59,20760,20761,20763,20766,20768],{"class":61,"line":62},[59,20762,20730],{"class":73},[59,20764,20765],{"class":69}," \"conf-dir=\u002Fusr\u002Flocal\u002Fetc\u002Fdnsmasq.d\"",[59,20767,20423],{"class":1372},[59,20769,20770],{"class":69}," \u002Fusr\u002Flocal\u002Fetc\u002Fdnsmasq.conf\n",[59,20772,20773,20775,20777,20779],{"class":61,"line":77},[59,20774,20730],{"class":73},[59,20776,20745],{"class":69},[59,20778,20464],{"class":1372},[59,20780,20781],{"class":69}," \u002Fusr\u002Flocal\u002Fetc\u002Fdnsmasq.d\u002Fff.conf\n",[14,20783,20784],{},"Set dnsmasq to run as a service",[50,20786,20788],{"className":52,"code":20787,"language":54,"meta":55,"style":55},"sudo brew services start dnsmasq\nsudo dscacheutil -flushcache\n",[18,20789,20790,20804],{"__ignoreMap":55},[59,20791,20792,20794,20797,20800,20802],{"class":61,"line":62},[59,20793,9188],{"class":65},[59,20795,20796],{"class":69}," brew",[59,20798,20799],{"class":69}," services",[59,20801,12654],{"class":69},[59,20803,20410],{"class":69},[59,20805,20806,20808,20811],{"class":61,"line":77},[59,20807,9188],{"class":65},[59,20809,20810],{"class":69}," dscacheutil",[59,20812,20813],{"class":73}," -flushcache\n",[14,20815,20816],{},"Tell MacOS to use dnsmasq for our test domain",[50,20818,20820],{"className":52,"code":20819,"language":54,"meta":55,"style":55},"sudo mkdir -p \u002Fetc\u002Fresolver\nsudo tee \u002Fetc\u002Fresolver\u002Fexample.com > \u002Fdev\u002Fnull \u003C\u003CEOF\nnameserver 127.0.0.1\ndomain example.com\nsearch_order 1\nEOF\n",[18,20821,20822,20833,20854,20859,20864,20869],{"__ignoreMap":55},[59,20823,20824,20826,20828,20830],{"class":61,"line":62},[59,20825,9188],{"class":65},[59,20827,9191],{"class":69},[59,20829,8441],{"class":73},[59,20831,20832],{"class":69}," \u002Fetc\u002Fresolver\n",[59,20834,20835,20837,20840,20843,20845,20848,20851],{"class":61,"line":77},[59,20836,9188],{"class":65},[59,20838,20839],{"class":69}," tee",[59,20841,20842],{"class":69}," \u002Fetc\u002Fresolver\u002Fexample.com",[59,20844,20464],{"class":1372},[59,20846,20847],{"class":69}," \u002Fdev\u002Fnull",[59,20849,20850],{"class":1372}," \u003C\u003C",[59,20852,20853],{"class":69},"EOF\n",[59,20855,20856],{"class":61,"line":88},[59,20857,20858],{"class":69},"nameserver 127.0.0.1\n",[59,20860,20861],{"class":61,"line":99},[59,20862,20863],{"class":69},"domain example.com\n",[59,20865,20866],{"class":61,"line":156},[59,20867,20868],{"class":69},"search_order 1\n",[59,20870,20871],{"class":61,"line":216},[59,20872,20853],{"class":69},[14,20874,20875],{},"And finally kick the MacOS resolver so it sees the updates",[50,20877,20879],{"className":52,"code":20878,"language":54,"meta":55,"style":55},"sudo killall -HUP mDNSResponder\n",[18,20880,20881],{"__ignoreMap":55},[59,20882,20883,20885,20888,20891],{"class":61,"line":62},[59,20884,9188],{"class":65},[59,20886,20887],{"class":69}," killall",[59,20889,20890],{"class":73}," -HUP",[59,20892,20893],{"class":69}," mDNSResponder\n",[104,20895,20897],{"id":20896},"pi-hole","Pi Hole",[14,20899,20900],{},"Pi Hole is a package that bundles dnsmasq as an image to run on a Raspberry Pi (or in Docker container e.g. on your NAS). Its main use is to block advertisements embedded in web pages. But since in its normal configuration it is already handling all the local DNS traffic, making us work with FlowFuse is possible and means you do not need to change any settings on your development\u002Ftest machine.",[14,20902,20903,20904,20907,20908],{},"Create the following file in ",[18,20905,20906],{},"\u002Fetc\u002Fdnsmasq.d"," called ",[18,20909,20910],{},"02-flowforge.conf",[50,20912,20914],{"className":52,"code":20913,"language":54,"meta":55,"style":55},"address=\u002Fexample.com\u002F192.168.0.22\n",[18,20915,20916],{"__ignoreMap":55},[59,20917,20918,20921,20923],{"class":61,"line":62},[59,20919,20920],{"class":178},"address",[59,20922,1373],{"class":1372},[59,20924,20925],{"class":69},"\u002Fexample.com\u002F192.168.0.22\n",[14,20927,20928,20929,20931],{},"Where 192.168.0.22 is the ipv4 address of the Docker host machine or a Kubernetes node. And ",[18,20930,1312],{}," is the domain to use.",[14,20933,20934],{},"After making the change you will probably need to restart things with:",[50,20936,20938],{"className":52,"code":20937,"language":54,"meta":55,"style":55},"sudo pihole restartdns\n",[18,20939,20940],{"__ignoreMap":55},[59,20941,20942,20944,20947],{"class":61,"line":62},[59,20943,9188],{"class":65},[59,20945,20946],{"class":69}," pihole",[59,20948,20949],{"class":69}," restartdns\n",[14,20951,20952,20953,20956],{},"If running Pi Hole in Docker then you will need to create the file on the host and mount it to the ",[18,20954,20955],{},"\u002Fetc\u002Fdnsmasq.d\u002F02-flowforge.conf"," location.",[104,20958,20960],{"id":20959},"no-local-dns-server","No Local DNS server",[14,20962,20963],{},"If you really can't run dnsmasq then there is a possible alternative.",[14,20965,20966,20967,273],{},"A really useful service called sslip.io allows you to test FlowFuse even when you cannot use dnsmasq. You can read more about ",[41,20968,20971],{"href":20969,"rel":20970},"https:\u002F\u002Fsslip.io",[831],"sslip.io on their web site",[14,20973,20974,20975,20977,20978,20980,20981,20984],{},"You will need to set the ",[18,20976,18666],{}," entry in the ",[18,20979,576],{}," configuration file to the following pattern ",[18,20982,20983],{},"172.17.0.1.sslip.io",". Don't forget to replace the IP address with the correct one for your the machine FlowFuse is running on.",[14,20986,20987,20988,20991],{},"This will work because the ",[18,20989,20990],{},"sslip.io"," domain is set up to always return the IP address embedded in the hostname queried.",[316,20993,20994],{},"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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":55,"searchDepth":77,"depth":77,"links":20996},[20997,21001],{"id":20303,"depth":77,"text":20304,"children":20998},[20999,21000],{"id":20347,"depth":88,"text":20348},{"id":20354,"depth":88,"text":20355},{"id":20361,"depth":77,"text":20362,"children":21002},[21003,21004,21005],{"id":20371,"depth":88,"text":20372},{"id":20896,"depth":88,"text":20897},{"id":20959,"depth":88,"text":20960},{},"install\u002Fdns-setup.md","\u002Fdocs\u002Finstall\u002Fdns-setup",{"title":20270,"description":20278},"docs\u002Finstall\u002Fdns-setup","p18H9GnilB-BEDT1wm7YIbrU5tB35Y9fzVid3BwIPcw",{"id":21013,"title":21014,"body":21015,"description":21185,"extension":329,"layout":330,"meta":21186,"navGroup":330,"navOrder":330,"navTitle":21187,"navigation":187,"originalPath":21188,"path":21189,"redirect":330,"seo":21190,"stem":21191,"updated":337,"version":338,"__hash__":21192},"docs\u002Fdocs\u002Finstall\u002Fdocker\u002Faws-marketplace.md","Installing FlowFuse from AWS Market Place",{"type":7,"value":21016,"toc":21177},[21017,21020,21027,21030,21032,21036,21039,21052,21055,21061,21064,21068,21071,21074,21085,21088,21092,21095,21098,21121,21129,21135,21138,21144,21147,21153,21156,21159,21165,21167],[10,21018,21014],{"id":21019},"installing-flowfuse-from-aws-market-place",[14,21021,21022,21023,273],{},"We have packaged the Docker Compose build of FlowFuse as a AWS EC2 AMI. It can be found on the AWS Market Place ",[41,21024,785],{"href":21025,"rel":21026},"https:\u002F\u002Faws.amazon.com\u002Fmarketplace\u002Fpp\u002Fprodview-3ycrknfg67rug",[831],[14,21028,21029],{},"This will allow you to quickly spin up an AWS EC2 Machine with everything pre-installed.",[23,21031,26],{"id":25},[104,21033,21035],{"id":21034},"dns","DNS",[14,21037,21038],{},"To make use of the AWS Machine image you will need a DNS Domain to host both the FlowFuse application and the Node-RED instances you create.",[14,21040,21041,21042,21044,21045,21048,21049,273],{},"For example if you use ",[18,21043,20290],{}," then the FlowFuse application will run on ",[18,21046,21047],{},"https:\u002F\u002Fforge.ff.example.com"," and Node-RED would be on ",[18,21050,21051],{},"https:\u002F\u002F[instance-name].ff.example.com",[14,21053,21054],{},"You will need to set up a wildcard DNS entry that points the whole domain to the Public IP address of the Droplet.",[50,21056,21059],{"className":21057,"code":21058,"language":3920},[3918],"*.ff.example.com 3600 A xxx.xxx.xxx.xxx\n",[18,21060,21058],{"__ignoreMap":55},[14,21062,21063],{},"The Public IP address of the EC2 machine will be listed in the AWS Console once it has started and at the start of the Setup Wizard when you first SSH into the machine.",[104,21065,21067],{"id":21066},"smtp-server-optional","SMTP Server (Optional)",[14,21069,21070],{},"This is used to send invites to new user and to enable password resets.",[14,21072,21073],{},"You will need to know the following:",[28,21075,21076,21079,21082],{},[31,21077,21078],{},"SMTP Server hostname",[31,21080,21081],{},"Port (Default 587)",[31,21083,21084],{},"Username\u002FPassword",[14,21086,21087],{},"If you don't already have a SMTP server you can grab one from an email delivery service like SparkPost, Sendgrid, Mailgun, etc.",[23,21089,21091],{"id":21090},"setup","Setup",[14,21093,21094],{},"After the EC2 Machine has been created and you have set up the DNS entry you will need to connect to the machine to and enter the domain name",[14,21096,21097],{},"You can do this in 2 ways",[398,21099,21100,21109],{},[31,21101,21102,21103],{},"With SSH, either using keys or password depending on what Authentication mechanism you picked at creation time",[50,21104,21107],{"className":21105,"code":21106,"language":3920},[3918],"ssh -i aws-ssh.pem ubuntu@xxx.xxx.xxx.xxx\n",[18,21108,21106],{"__ignoreMap":55},[31,21110,21111,21112,470,21115,21117],{},"Using the \"Connect\" button on the EC2 console. Be sure to change the username from ",[18,21113,21114],{},"root",[18,21116,20381],{},[638,21118],{"alt":21119,"src":21120},"AWS EC2 Console","\u002Fdocs\u002Finstall\u002Fimages\u002Faws-console-connect.png",[14,21122,21123,21124,1706,21126,21128],{},"Once logged in you will be presented with a wizard to set the domain and confirm by entering ",[18,21125,8359],{},[18,21127,3158],{}," to enter the domain again.",[14,21130,21131],{},[638,21132],{"alt":21133,"src":21134},"Digital Ocean Wizard","\u002Fdocs\u002Finstall\u002Fimages\u002Fdo-wizard.png",[14,21136,21137],{},"The wizard will then ask about setting up a SMTP server to allow FlowFuse to send email. This used to invite users or reset passwords,\nit is optional.",[14,21139,21140],{},[638,21141],{"alt":21142,"src":21143},"Digital Ocean Wizard SMTP","\u002Fdocs\u002Finstall\u002Fimages\u002Fdo-wizard-smtp.png",[14,21145,21146],{},"The wizard will then update the configuration files and start FlowFuse",[14,21148,21149],{},[638,21150],{"alt":21151,"src":21152},"Digital Ocean Direct to Setup","\u002Fdocs\u002Finstall\u002Fimages\u002Fdo-direct-to-setup.png",[14,21154,21155],{},"It will then present a link to complete setup in the browser and a Username and Password to log in as the default admin user. You will be asked to change the password when you use it for the first time.",[14,21157,21158],{},"You can now close the console connection to the droplet.",[14,21160,21161,21162],{},"Details of how to complete this steps are ",[41,21163,785],{"href":21164},"\u002Fdocs\u002Finstall\u002Ffirst-run",[23,21166,9856],{"id":9855},[14,21168,21169,21170,21174,21175],{},"You can follow the normal FlowFuse Docker ",[41,21171,21173],{"href":21172},"\u002Fdocs\u002Finstall\u002Fdocker#upgrade","upgrade instructions",", the install directory is ",[18,21176,9172],{},{"title":55,"searchDepth":77,"depth":77,"links":21178},[21179,21183,21184],{"id":25,"depth":77,"text":26,"children":21180},[21181,21182],{"id":21034,"depth":88,"text":21035},{"id":21066,"depth":88,"text":21067},{"id":21090,"depth":77,"text":21091},{"id":9855,"depth":77,"text":9856},"We have packaged the Docker Compose build of FlowFuse as a AWS EC2 AMI. It can be found on the AWS Market Place here.",{},"Docker from AWS Market Place","install\u002Fdocker\u002Faws-marketplace.md","\u002Fdocs\u002Finstall\u002Fdocker\u002Faws-marketplace",{"title":21014,"description":21185},"docs\u002Finstall\u002Fdocker\u002Faws-marketplace","exR2TcEYRPCbt0VtYSzb3PVo1EBu0nk_TYqL3EZ9-JY",{"id":21194,"title":21195,"body":21196,"description":21340,"extension":329,"layout":330,"meta":21341,"navGroup":330,"navOrder":330,"navTitle":21342,"navigation":187,"originalPath":21343,"path":21344,"redirect":330,"seo":21345,"stem":21346,"updated":337,"version":338,"__hash__":21347},"docs\u002Fdocs\u002Finstall\u002Fdocker\u002Fdigital-ocean.md","1-Click Digital Ocean Droplet Install",{"type":7,"value":21197,"toc":21332},[21198,21201,21209,21217,21219,21221,21224,21232,21234,21239,21242,21248,21250,21252,21254,21262,21270,21272,21275,21277,21295,21301,21305,21307,21311,21313,21317,21320,21324,21326],[10,21199,21195],{"id":21200},"_1-click-digital-ocean-droplet-install",[14,21202,21203,21204,273],{},"We have packaged the Docker Compose build of FlowFuse as a Digital Ocean Droplet. It can be found in the ",[41,21205,21208],{"href":21206,"rel":21207},"https:\u002F\u002Fmarketplace.digitalocean.com\u002Fapps\u002Fflowfuse?refcode=fb23e438a477",[831],"Digital Ocean Market Place",[14,21210,21211,21212],{},"You can go straight to the ",[41,21213,21216],{"href":21214,"rel":21215},"https:\u002F\u002Fmarketplace.digitalocean.com\u002Fapps\u002Fflowfuse?refcode=fb23e438a477&action=deploy",[831],"deployment wizard",[23,21218,26],{"id":25},[104,21220,21035],{"id":21034},[14,21222,21223],{},"To make use of this Droplet you will need a DNS Domain to host both the FlowFuse application and the Node-RED instances you create.",[14,21225,21041,21226,21044,21228,21048,21230,273],{},[18,21227,20290],{},[18,21229,21047],{},[18,21231,21051],{},[14,21233,21054],{},[50,21235,21237],{"className":21236,"code":21058,"language":3920},[3918],[18,21238,21058],{"__ignoreMap":55},[14,21240,21241],{},"The Public IP address of the Droplet will be listed in the UI when it has been created",[14,21243,21244],{},[638,21245],{"alt":21246,"src":21247},"Digital Ocean Droplet IP address ","\u002Fdocs\u002Finstall\u002Fimages\u002Fdo-public-ip.png",[104,21249,21067],{"id":21066},[14,21251,21070],{},[14,21253,21073],{},[28,21255,21256,21258,21260],{},[31,21257,21078],{},[31,21259,21081],{},[31,21261,21084],{},[14,21263,21264,21265,273],{},"If you don't already have a SMTP server you can grab one from an email delivery service like\nSparkPost, Sendgrid, Mailgun, or any ",[41,21266,21269],{"href":21267,"rel":21268},"https:\u002F\u002Fmarketplace.digitalocean.com\u002Fcategory\u002Femail",[831],"other email solution provided by Digital Ocean",[23,21271,21091],{"id":21090},[14,21273,21274],{},"After the Droplet has been created and you have set up the DNS entry you will need to connect to the droplet to and enter the domain name",[14,21276,21097],{},[398,21278,21279,21288],{},[31,21280,21281,21282],{},"With SSH, either using keys or password depending on what Authentication mechanism you picked at creation time\n",[50,21283,21286],{"className":21284,"code":21285,"language":3920},[3918],"ssh -i digital-ocean-ssh.key root@xxx.xxx.xxx.xxx\n",[18,21287,21285],{"__ignoreMap":55},[31,21289,21290,21291],{},"Opening a Console from the Web Interface\n",[638,21292],{"alt":21293,"src":21294},"Digital Ocean Console","\u002Fdocs\u002Finstall\u002Fimages\u002Fdo-droplet-console.png",[14,21296,21123,21297,1706,21299,21128],{},[18,21298,8359],{},[18,21300,3158],{},[14,21302,21303],{},[638,21304],{"alt":21133,"src":21134},[14,21306,21137],{},[14,21308,21309],{},[638,21310],{"alt":21142,"src":21143},[14,21312,21146],{},[14,21314,21315],{},[638,21316],{"alt":21151,"src":21152},[14,21318,21319],{},"It will then present a link to complete setup in the browser. You can now close the console connection to the droplet.",[14,21321,21161,21322],{},[41,21323,785],{"href":21164},[23,21325,9856],{"id":9855},[14,21327,21169,21328,21174,21330],{},[41,21329,21173],{"href":21172},[18,21331,9172],{},{"title":55,"searchDepth":77,"depth":77,"links":21333},[21334,21338,21339],{"id":25,"depth":77,"text":26,"children":21335},[21336,21337],{"id":21034,"depth":88,"text":21035},{"id":21066,"depth":88,"text":21067},{"id":21090,"depth":77,"text":21091},{"id":9855,"depth":77,"text":9856},"We have packaged the Docker Compose build of FlowFuse as a Digital Ocean Droplet. It can be found in the Digital Ocean Market Place.",{},"Docker on Digital Ocean","install\u002Fdocker\u002Fdigital-ocean.md","\u002Fdocs\u002Finstall\u002Fdocker\u002Fdigital-ocean",{"title":21195,"description":21340},"docs\u002Finstall\u002Fdocker\u002Fdigital-ocean","Kx5WA7mxUOMKpII8JRwAx3MU9LAiTcSKlTvyUovakwc",{"id":21349,"title":55,"body":21350,"description":55,"extension":329,"layout":330,"meta":22748,"navGroup":330,"navOrder":330,"navTitle":9554,"navigation":187,"originalPath":22749,"path":22750,"redirect":330,"seo":22751,"stem":22752,"updated":337,"version":338,"__hash__":22753},"docs\u002Fdocs\u002Finstall\u002Fdocker\u002Findex.md",{"type":7,"value":21351,"toc":22723},[21352,21355,21357,21360,21366,21370,21415,21417,21420,21450,21453,21475,21477,21480,21491,21494,21508,21512,21515,21517,21520,21531,21537,21544,21549,21551,21555,21562,21595,21599,21605,21615,21628,21646,21652,21656,21662,21665,21669,21674,21685,21698,21706,21711,21715,21718,21739,21742,21750,21779,21871,21875,21880,21888,21892,21921,21925,21944,21947,21965,21978,21980,21983,21988,21991,21993,22006,22124,22175,22179,22183,22186,22223,22230,22234,22240,22246,22249,22252,22255,22374,22378,22394,22441,22444,22458,22462,22465,22468,22471,22474,22503,22506,22549,22552,22618,22621,22636,22640,22648,22651,22660,22663,22667,22670,22673,22680,22683,22686,22691,22693,22720],[17936,21353,21354],{},"     \n   class ChecklistItem extends HTMLElement {\n\n      static observedAttributes = [\"type\", \"task\"];\n\n      constructor() {\n         super();   \n         this.type = 'required'\n         this.task = ''\n      }\n\n      attributeChangedCallback(name, oldValue, newValue) {\n        if (name === \"type\") {\n            this.type = newValue;\n        } else if (name === \"task\") {\n            this.task = newValue;\n        }\n      }\n      \n      connectedCallback () {\n        const iconRequired = `\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\">\u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z\" \u002F>\u003C\u002Fsvg>`\n        const iconRecommended = `\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\">\u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 12.75 11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 0 1-1.043 3.296 3.745 3.745 0 0 1-3.296 1.043A3.745 3.745 0 0 1 12 21c-1.268 0-2.39-.63-3.068-1.593a3.746 3.746 0 0 1-3.296-1.043 3.745 3.745 0 0 1-1.043-3.296A3.745 3.745 0 0 1 3 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 0 1 1.043-3.296 3.746 3.746 0 0 1 3.296-1.043A3.746 3.746 0 0 1 12 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 0 1 3.296 1.043 3.746 3.746 0 0 1 1.043 3.296A3.745 3.745 0 0 1 21 12Z\" \u002F>\u003C\u002Fsvg>`\n        const iconOptional = `\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\">\u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M8.625 12a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H8.25m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H12m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0h-.375M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z\" \u002F>\u003C\u002Fsvg>`\n        \n        let icon = iconRequired\n        let tooltip = \"Required for Operation\"\n        if (this.type === 'recommended') {\n          icon = iconRecommended\n          tooltip = \"Recommended for Production\"\n        } else if (this.type === 'optional') {\n          icon = iconOptional\n          tooltip = \"Optional\"\n        }\n        this.innerHTML = `\u003Cdiv class=\"checklist-item checklist-item--${this.type}\">\u003Cspan class=\"tooltip\" data-tooltip=\"${tooltip}\">\u003Cspan class=\"checklist-item-status\">${icon}\u003C\u002Fspan>\u003Cspan>${this.task}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fdiv>`\n      }\n   }\n   \n   customElements.define('checklist-item', ChecklistItem);\n",[10,21356,5],{"id":12},[14,21358,21359],{},"This guide walks you through detailed set up of FlowFuse Platform on a Docker container environment using Docker Compose. Typically suited for small\u002Fmedium on premise deployments.\nBy the end, you will have a fully functioning FlowFuse instance running in a Docker container.",[14,21361,21362,21363,273],{},"The following guide walks through a full production-ready deployment. If you want to install FlowFuse for evaluation purposes, please refer to the ",[41,21364,15506],{"href":21365},"\u002Fdocs\u002Fquick-start",[23,21367,21369],{"id":21368},"checklist","Checklist",[17958,21371,21376,21377,21376,21399],{"className":21372},[21373,21374,21375],"grid","grid-cols-2","gap-8","\n  ",[17958,21378,21380,21381,21380,21383,21376],{"className":21379},[21368],"\n    ",[17972,21382,26],{},[17958,21384,17970,21385,17970,21389,17970,21392,17970,21396,21380],{},[21386,21387],"checklist-item",{"task":21388},"Domain Name",[21386,21390],{"task":21391},"Docker Engine & Docker Compose",[21386,21393],{"type":21394,"task":21395},"recommended","Setup Dedicated Database",[21386,21397],{"type":21394,"task":21398},"Prepare TLS Certificates",[17958,21400,21380,21402,21380,21404,21376],{"className":21401},[21368],[17972,21403,13547],{},[17958,21405,17970,21406,17970,21409,17970,21412,21380],{},[21386,21407],{"task":21408},"Download FlowFuse",[21386,21410],{"task":21411},"Configure FlowFuse",[21386,21413],{"type":21394,"task":21414},"Enable HTTPS",[23,21416,26],{"id":25},[14,21418,21419],{},"Before you begin, ensure you have the following:",[398,21421,21422,21431],{},[31,21423,21424,21427,21428,660],{},[364,21425,21426],{},"Domain Name & DNS:"," A domain name that you own and can configure DNS settings for (explained in ",[41,21429,21035],{"href":21430},"#dns",[31,21432,21433,3497,21436,302,21441,21445,21446,21449],{},[364,21434,21435],{},"Install Docker:",[41,21437,21440],{"href":21438,"rel":21439},"https:\u002F\u002Fdocs.docker.com\u002Fengine\u002F",[831],"Docker Engine",[41,21442,162],{"href":21443,"rel":21444},"https:\u002F\u002Fdocs.docker.com\u002Fcompose\u002Finstall\u002F",[831]," (in ",[18,21447,21448],{},"2.23.1"," version or higher) must be installed on your system (either as a standalone binary or as docker plugin).",[14,21451,21452],{},"For a production-ready environment, we also recommend:",[28,21454,21455,21466],{},[31,21456,21457,21460,21461,21465],{},[364,21458,21459],{},"Database:"," Prepare dedicated database on a external database server (see ",[41,21462,21464],{"href":21463},"#how-to-use-external-database-server%3F","FAQ"," for more details)",[31,21467,21468,21471,21472,660],{},[364,21469,21470],{},"TLS Certification:"," Prepare TLS certificate for your domain and configure FlowFuse platform to use it (see ",[41,21473,21414],{"href":21474},"#enable-https-(optional)",[104,21476,12208],{"id":12207},[14,21478,21479],{},"To run the FlowFuse platform smoothly, we recommend the following minimum hardware specs:",[28,21481,21482,21485,21488],{},[31,21483,21484],{},"CPU: 2 cores",[31,21486,21487],{},"Memory: 4 GB RAM",[31,21489,21490],{},"Storage: 20 GB disk space\nEach Node-RED instance you host will uses 0.1 CPU cores and 256 MB of memory by default. This parameters can be adjusted in admin area of FlowFuse platform. Keep this in mind when sizing your hardware, especially if plan to create multiple hosted instances.",[14,21492,21493],{},"When deploying the FlowFuse platform behind a firewall or proxy, ensure that the following ports are open and forwards a traffic to the server where FlowFuse is installed:",[28,21495,21496,21503],{},[31,21497,21498,21499,21502],{},"Port ",[18,21500,21501],{},"80"," (HTTP)",[31,21504,21498,21505,21507],{},[18,21506,4487],{}," (HTTPS)",[768,21509,21511],{"id":21510},"aws-elastic-container-service","AWS Elastic Container Service",[14,21513,21514],{},"At this time we do not support deploying FlowFuse to AWS's Elastic Container Service.",[104,21516,21035],{"id":21034},[14,21518,21519],{},"The orchestration uses an instance of Nginx to route requests to each Node-RED instance. To do this it needs each instance to have a unique hostname, to generate this the instance name is prepended to a supplied domain.",[14,21521,21522,21523,21527,21528,273],{},"To make this work you will need to configure a DNS server to map a ",[41,21524,21526],{"href":20313,"rel":21525},[831],"wildcard domain entry"," to the IP address of the host running Docker. e.g ",[18,21529,21530],{},"*.example.com",[14,21532,21533,21534],{},"The FlowFuse Application will be hosted on ",[18,21535,21536],{},"http:\u002F\u002Fforge.example.com",[14,21538,21539,21541,21542,15415],{},[364,21540,1760],{}," At this moment FlowFuse platform is not capable to run on localhost. You must point your domain to the external IP address of the host machine, not the loopback address (",[18,21543,20296],{},[14,21545,21546,21547,273],{},"Notes on how to setup DNS can be found ",[41,21548,785],{"href":21008},[23,21550,9164],{"id":9163},[104,21552,21554],{"id":21553},"download-installation-files","Download installation files",[14,21556,21557,21558,21561],{},"Download the latest version of the FlowFuse Docker Compose file and example ",[18,21559,21560],{},".env"," file used for installation configuration:",[50,21563,21565],{"className":52,"code":21564,"language":54,"meta":55,"style":55},"curl -L -o docker-compose.yml https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdocker-compose\u002Freleases\u002Flatest\u002Fdownload\u002Fdocker-compose.yml\ncurl -L -o .env https:\u002F\u002Fraw.githubusercontent.com\u002FFlowFuse\u002Fdocker-compose\u002Frefs\u002Fheads\u002Fmain\u002F.env.example\n",[18,21566,21567,21581],{"__ignoreMap":55},[59,21568,21569,21571,21573,21575,21578],{"class":61,"line":62},[59,21570,1381],{"class":65},[59,21572,15015],{"class":73},[59,21574,15021],{"class":73},[59,21576,21577],{"class":69}," docker-compose.yml",[59,21579,21580],{"class":69}," https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdocker-compose\u002Freleases\u002Flatest\u002Fdownload\u002Fdocker-compose.yml\n",[59,21582,21583,21585,21587,21589,21592],{"class":61,"line":77},[59,21584,1381],{"class":65},[59,21586,15015],{"class":73},[59,21588,15021],{"class":73},[59,21590,21591],{"class":69}," .env",[59,21593,21594],{"class":69}," https:\u002F\u002Fraw.githubusercontent.com\u002FFlowFuse\u002Fdocker-compose\u002Frefs\u002Fheads\u002Fmain\u002F.env.example\n",[104,21596,21598],{"id":21597},"configure","Configure",[14,21600,21601,21602,21604],{},"Installation configuration is done via the ",[18,21603,21560],{}," file.\nThe minimal configuration required is the domain name you will be using for the platform.",[14,21606,21607,21608,21610,21611,21614],{},"Edit downloaded ",[18,21609,21560],{}," file with the editor of your choice and update the ",[18,21612,21613],{},"DOMAIN"," variable with the domain you will be using for the platform.",[14,21616,21617,21618,21621,21622,21624,21625,21627],{},"Alternatevily, use ",[18,21619,21620],{},"sed"," to update the ",[18,21623,21613],{}," variable in the ",[18,21626,21560],{}," file:",[50,21629,21631],{"className":52,"code":21630,"language":54,"meta":55,"style":55},"sed -i 's\u002F^DOMAIN=.*\u002FDOMAIN=example.com\u002F' .env\n",[18,21632,21633],{"__ignoreMap":55},[59,21634,21635,21637,21640,21643],{"class":61,"line":62},[59,21636,21620],{"class":65},[59,21638,21639],{"class":73}," -i",[59,21641,21642],{"class":69}," 's\u002F^DOMAIN=.*\u002FDOMAIN=example.com\u002F'",[59,21644,21645],{"class":69}," .env\n",[14,21647,21648,21649,21651],{},"Please note that once set, the ",[18,21650,21613],{}," value should not be changed as it is used as part of the configuration stored in the database of each Node-RED instance. The ability to migrate to different domain is on the feature backlog.",[104,21653,21655],{"id":21654},"enable-https-optional","Enable HTTPS (optional)",[14,21657,21658,21659,21661],{},"If you want to serve the FlowFuse platform and Node-RED instances over TLS you will need to obtain a wildcard TLS certificate for the domain you are using eg ",[18,21660,21530],{},". If you are running on an Internet facing machine you can use our configuration files to generate it atomatically.",[14,21663,21664],{},"Otherwise you will need to contact a SSL Certificate vendor and configure Nginx manually.",[768,21666,21668],{"id":21667},"automatic-tls-certificate","Automatic TLS Certificate",[14,21670,21671],{},[364,21672,21673],{},"Note: Automatic TLS generation is possible only for the publicly available servers",[14,21675,4848,21676,21678,21679,21682,21683,3939],{},[18,21677,21560],{}," file, set the ",[18,21680,21681],{},"TLS_ENABLED"," variable to ",[18,21684,3558],{},[50,21686,21688],{"className":52,"code":21687,"language":54,"meta":55,"style":55},"TLS_ENABLED=true\n",[18,21689,21690],{"__ignoreMap":55},[59,21691,21692,21694,21696],{"class":61,"line":62},[59,21693,21681],{"class":178},[59,21695,1373],{"class":1372},[59,21697,3230],{"class":69},[14,21699,21700,21701,21705],{},"Proceed to the ",[41,21702,21704],{"href":21703},"#start-flowfuse-platform","next paragraph"," to start the platform with automatically generated TLS certificate.",[1110,21707,21708],{},[14,21709,21710],{},"When using automatic TLS certificate generation, the platform will take a few minutes to generate them on the first platform startup.\nFor a short period of time browsers may report untrusted certificate warning. This is expected behavior and should resolve itself once the certificate is generated.",[768,21712,21714],{"id":21713},"custom-tls-certificate","Custom TLS Certificate",[14,21716,21717],{},"If you have own TLS certificate, you can use it in FlowFuse platform installation as well. As mentioned before, the certificate must be a wildcard one for the domain you are using.",[17958,21719,21723,21727],{"className":21720},[21721,21722],"ff-callout","ff-callout--note",[14,21724,1760],{"className":21725},[21726],"ff-callout__title",[17958,21728,21731],{"className":21729},[21730],"ff-callout__content",[14,21732,21733,21734,21738],{},"If your TLS certificate is issued by a private Certificate Authority, additional configuration is required so that Hosted Instances trust the CA. See ",[41,21735,21737],{"href":21736},"#what-additional-configuration-is-required-when-the-tls-certificate-is-issued-by-a-private-certificate-authority%3F","What additional configuration is required when the TLS certificate is issued by a private Certificate Authority?"," for the step-by-step instructions.",[14,21740,21741],{},"To configure FlowFuse platform with your certificate, you need to have:",[28,21743,21744,21747],{},[31,21745,21746],{},"certificate key file",[31,21748,21749],{},"certificate's full chain (server certificate and intermediate certificates bundled into single file)",[14,21751,21752,21753,21755,21756,3012,21758,302,21761,21764,21765,21767,21768,7666,21770,21772,21773,21775,21776,21778],{},"To add your certificate to the platform, edit the ",[18,21754,21560],{}," file downloaded earlier and set values for ",[18,21757,21681],{},[18,21759,21760],{},"TLS_CERTIFICATE",[18,21762,21763],{},"TLS_KEY"," variables. ",[18,21766,21681],{}," variable should be set to ",[18,21769,3558],{},[18,21771,21760],{}," should contain the full chain of the certificate while ",[18,21774,21763],{}," should contain the key file.\nExample of ",[18,21777,21560],{}," file with the custom TLS certificate configuration:",[50,21780,21782],{"className":52,"code":21781,"language":54,"meta":55,"style":55},"TLS_ENABLED=true\nTLS_CERTIFICATE=\"\n-----BEGIN CERTIFICATE-----\nMIIFfzCCBKegAwIBAgISA0\n...\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIFfzCCBKegAwIBAgISA0\n...\n-----END CERTIFICATE-----\n\"\nTLS_KEY=\"\n-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD\n...\n-----END PRIVATE KEY-----\n\"\n",[18,21783,21784,21792,21801,21806,21811,21815,21820,21824,21828,21832,21836,21840,21848,21853,21858,21862,21867],{"__ignoreMap":55},[59,21785,21786,21788,21790],{"class":61,"line":62},[59,21787,21681],{"class":178},[59,21789,1373],{"class":1372},[59,21791,3230],{"class":69},[59,21793,21794,21796,21798],{"class":61,"line":77},[59,21795,21760],{"class":178},[59,21797,1373],{"class":1372},[59,21799,21800],{"class":69},"\"\n",[59,21802,21803],{"class":61,"line":88},[59,21804,21805],{"class":69},"-----BEGIN CERTIFICATE-----\n",[59,21807,21808],{"class":61,"line":99},[59,21809,21810],{"class":69},"MIIFfzCCBKegAwIBAgISA0\n",[59,21812,21813],{"class":61,"line":156},[59,21814,5847],{"class":69},[59,21816,21817],{"class":61,"line":216},[59,21818,21819],{"class":69},"-----END CERTIFICATE-----\n",[59,21821,21822],{"class":61,"line":224},[59,21823,21805],{"class":69},[59,21825,21826],{"class":61,"line":233},[59,21827,21810],{"class":69},[59,21829,21830],{"class":61,"line":241},[59,21831,5847],{"class":69},[59,21833,21834],{"class":61,"line":249},[59,21835,21819],{"class":69},[59,21837,21838],{"class":61,"line":257},[59,21839,21800],{"class":69},[59,21841,21842,21844,21846],{"class":61,"line":3137},[59,21843,21763],{"class":178},[59,21845,1373],{"class":1372},[59,21847,21800],{"class":69},[59,21849,21850],{"class":61,"line":3150},[59,21851,21852],{"class":69},"-----BEGIN PRIVATE KEY-----\n",[59,21854,21855],{"class":61,"line":3163},[59,21856,21857],{"class":69},"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD\n",[59,21859,21860],{"class":61,"line":3176},[59,21861,5847],{"class":69},[59,21863,21864],{"class":61,"line":3187},[59,21865,21866],{"class":69},"-----END PRIVATE KEY-----\n",[59,21868,21869],{"class":61,"line":3193},[59,21870,21800],{"class":69},[23,21872,21874],{"id":21873},"start-flowfuse-platform","Start FlowFuse platform",[14,21876,21877],{},[364,21878,21879],{},"Note: Make sure all configuration are done above before proceeding.",[14,21881,21882],{},[364,21883,21884,21885,21887],{},"Note: Commands must be executed within the same directory where the Docker Compose and ",[18,21886,21560],{}," files are located.",[768,21889,21891],{"id":21890},"with-automatic-tls-certificate-generation","With automatic TLS certificate generation",[50,21893,21895],{"className":52,"code":21894,"language":54,"meta":55,"style":55},"docker compose --profile autotls -p flowfuse up -d\n",[18,21896,21897],{"__ignoreMap":55},[59,21898,21899,21901,21904,21907,21910,21912,21915,21918],{"class":61,"line":62},[59,21900,66],{"class":65},[59,21902,21903],{"class":69}," compose",[59,21905,21906],{"class":73}," --profile",[59,21908,21909],{"class":69}," autotls",[59,21911,8441],{"class":73},[59,21913,21914],{"class":69}," flowfuse",[59,21916,21917],{"class":69}," up",[59,21919,21920],{"class":73}," -d\n",[768,21922,21924],{"id":21923},"in-all-other-scenarios-including-custom-tls-certificate","In all other scenarios, including custom TLS certificate",[50,21926,21928],{"className":52,"code":21927,"language":54,"meta":55,"style":55},"docker compose -p flowfuse up -d\n",[18,21929,21930],{"__ignoreMap":55},[59,21931,21932,21934,21936,21938,21940,21942],{"class":61,"line":62},[59,21933,66],{"class":65},[59,21935,21903],{"class":69},[59,21937,8441],{"class":73},[59,21939,21914],{"class":69},[59,21941,21917],{"class":69},[59,21943,21920],{"class":73},[14,21945,21946],{},"The platform will take a few minutes to start up. You can check the status of the containers by running:",[50,21948,21950],{"className":52,"code":21949,"language":54,"meta":55,"style":55},"docker compose -p flowfuse ps\n",[18,21951,21952],{"__ignoreMap":55},[59,21953,21954,21956,21958,21960,21962],{"class":61,"line":62},[59,21955,66],{"class":65},[59,21957,21903],{"class":69},[59,21959,8441],{"class":73},[59,21961,21914],{"class":69},[59,21963,21964],{"class":69}," ps\n",[14,21966,21967,21968,21971,21972,21974,21975,21977],{},"Visit ",[18,21969,21970],{},"forge.example.com"," (replace ",[18,21973,1312],{}," with the domain configured in the ",[18,21976,21560],{}," file) in your browser to access the FlowFuse platform.",[23,21979,584],{"id":9513},[14,21981,21982],{},"The first time you access the platform in your browser, it will take you through\ncreating an administrator for the platform and other configuration options.",[14,21984,21985,21986,273],{},"For more information, follow ",[41,21987,2587],{"href":21164},[14,21989,21990],{},"Once you have finished setting up the admin user there are some Docker specific items to consider.",[23,21992,9856],{"id":9855},[14,21994,21995],{},[364,21996,21997,21998,22001,22002],{},"Note: If you are upgrading from version ",[18,21999,22000],{},"2.10.0"," or lower, please follow ",[41,22003,2587],{"href":22004,"rel":22005},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdocker-compose\u002Fblob\u002Fmain\u002FUPGRADE.md",[831],[398,22007,22008,22026,22057,22077,22101,22118],{},[31,22009,22010,22011,22025],{},"Find the Docker Compose project name:\n",[50,22012,22014],{"className":52,"code":22013,"language":54,"meta":55,"style":55},"docker compose ls\n",[18,22015,22016],{"__ignoreMap":55},[59,22017,22018,22020,22022],{"class":61,"line":62},[59,22019,66],{"class":65},[59,22021,21903],{"class":69},[59,22023,22024],{"class":69}," ls\n","\nThe output will show the project name, as well as the location of Docker Compose file used for creating the project.",[31,22027,22028,22029,22032,22033],{},"Stop the existing project (replace ",[18,22030,22031],{},"$projectName"," with your project name):\n",[50,22034,22036],{"className":52,"code":22035,"language":54,"meta":55,"style":55},"docker compose -p $projectName down --rmi all\n",[18,22037,22038],{"__ignoreMap":55},[59,22039,22040,22042,22044,22046,22049,22051,22054],{"class":61,"line":62},[59,22041,66],{"class":65},[59,22043,21903],{"class":69},[59,22045,8441],{"class":73},[59,22047,22048],{"class":178}," $projectName ",[59,22050,6999],{"class":69},[59,22052,22053],{"class":73}," --rmi",[59,22055,22056],{"class":69}," all\n",[31,22058,22059,22060],{},"Download the latest Docker Compose files:\n",[50,22061,22063],{"className":52,"code":22062,"language":54,"meta":55,"style":55},"curl -L -o docker-compose.yml https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdocker-compose\u002Freleases\u002Flatest\u002Fdownload\u002Fdocker-compose.yml\n",[18,22064,22065],{"__ignoreMap":55},[59,22066,22067,22069,22071,22073,22075],{"class":61,"line":62},[59,22068,1381],{"class":65},[59,22070,15015],{"class":73},[59,22072,15021],{"class":73},[59,22074,21577],{"class":69},[59,22076,21580],{"class":69},[31,22078,10264,22079,22081,22082,22084,22085],{},[18,22080,21560],{}," file is present and contains your installation-specific configuration. Download an example ",[18,22083,21560],{}," file if needed:\n",[50,22086,22088],{"className":52,"code":22087,"language":54,"meta":55,"style":55},"curl -o .env.example https:\u002F\u002Fraw.githubusercontent.com\u002FFlowFuse\u002Fdocker-compose\u002Frefs\u002Fheads\u002Fmain\u002F.env.example\n",[18,22089,22090],{"__ignoreMap":55},[59,22091,22092,22094,22096,22099],{"class":61,"line":62},[59,22093,1381],{"class":65},[59,22095,15021],{"class":73},[59,22097,22098],{"class":69}," .env.example",[59,22100,21594],{"class":69},[31,22102,22103,22104],{},"Pull the latest version of the default stack container (and any other stacks you have configured)\n",[50,22105,22107],{"className":52,"code":22106,"language":54,"meta":55,"style":55},"docker pull flowfuse\u002Fnode-red:latest\n",[18,22108,22109],{"__ignoreMap":55},[59,22110,22111,22113,22115],{"class":61,"line":62},[59,22112,66],{"class":65},[59,22114,9695],{"class":69},[59,22116,22117],{"class":69}," flowfuse\u002Fnode-red:latest\n",[31,22119,22120,22121,22123],{},"Start the project depending on the TLS configuration (replace ",[18,22122,22031],{}," with your project name):",[28,22125,22126,22153],{},[31,22127,22128,22129],{},"automatic TLS:\n",[50,22130,22132],{"className":52,"code":22131,"language":54,"meta":55,"style":55},"docker compose --profile autossl -p $projectName up -d\n",[18,22133,22134],{"__ignoreMap":55},[59,22135,22136,22138,22140,22142,22145,22147,22149,22151],{"class":61,"line":62},[59,22137,66],{"class":65},[59,22139,21903],{"class":69},[59,22141,21906],{"class":73},[59,22143,22144],{"class":69}," autossl",[59,22146,8441],{"class":73},[59,22148,22048],{"class":178},[59,22150,6993],{"class":69},[59,22152,21920],{"class":73},[31,22154,22155,22156],{},"any other scenario:\n",[50,22157,22159],{"className":52,"code":22158,"language":54,"meta":55,"style":55},"docker compose -p $projectName up -d\n",[18,22160,22161],{"__ignoreMap":55},[59,22162,22163,22165,22167,22169,22171,22173],{"class":61,"line":62},[59,22164,66],{"class":65},[59,22166,21903],{"class":69},[59,22168,8441],{"class":73},[59,22170,22048],{"class":178},[59,22172,6993],{"class":69},[59,22174,21920],{"class":73},[23,22176,22178],{"id":22177},"common-questions","Common Questions",[104,22180,22182],{"id":22181},"how-to-use-external-database-server","How to use external database server?",[14,22184,22185],{},"FlowFuse platform uses PostgreSQL database to store its data. By default, the database is created and managed by the Docker Compose.\nIf you want to use an external database server, you need to:",[28,22187,22188,22207],{},[31,22189,22190,22191,302,22193,22196,22197,302,22200,20053,22203,22206],{},"on your database server, create ",[18,22192,6303],{},[18,22194,22195],{},"ff-context"," databases as well as a user with access to both of them (see ",[18,22198,22199],{},"configs.postgres_db_setup",[18,22201,22202],{},"configs.postgres_context_setup",[18,22204,22205],{},"docker-compose.yml"," file for the reference)",[31,22208,22209,22210,22212,22213,3012,22216,3012,22219,22222],{},"configure the connection to the database in the ",[18,22211,21560],{}," file. Set the ",[18,22214,22215],{},"DB_HOST",[18,22217,22218],{},"DB_USER",[18,22220,22221],{},"DB_PASSWORD"," variables to the connection details of the external database server",[14,22224,22225,22226,22229],{},"Once ready, ",[41,22227,22228],{"href":21703},"start the application"," .",[104,22231,22233],{"id":22232},"how-can-i-provide-my-own-tls-certificate","How can I provide my own TLS certificate?",[14,22235,22236,22237,22239],{},"If you have your own TLS certificate, you can use it in FlowFuse platform installation as well. See ",[41,22238,21414],{"href":21474}," section for more details.",[14,22241,22242,22243,21738],{},"Additionally, if your TLS certificate is issued by a private Certificate Authority, you will need to perform some additional configuration to make hosted Node-RED instances trust the CA. See ",[41,22244,22245],{"href":21736},"What additional configuration is required when the TLS certificate is issued by a private Certificate Authority",[104,22247,21737],{"id":22248},"what-additional-configuration-is-required-when-the-tls-certificate-is-issued-by-a-private-certificate-authority",[14,22250,22251],{},"When the FlowFuse platform is configured with a TLS certificate issued by a private Certificate Authority (CA), hosted Node-RED instances will not trust the CA by default. As a result, opening the editor for a hosted instance will fail with a certificate trust error.",[14,22253,22254],{},"To make hosted Node-RED instances trust the private CA, follow the steps below:",[398,22256,22257,22291,22346,22368],{},[31,22258,22259,22262,22263,22265,22266,22269,22270,22273,22274,3939,22277],{},[364,22260,22261],{},"Update the environment file."," In the ",[18,22264,21560],{}," file created during the installation process, set ",[18,22267,22268],{},"DOCKER_DRIVER_PRIVATE_CA_PATH"," to the path of your Certificate Authority certificate file. This must be the path to the file stored on the Docker host server. For example, if the ",[18,22271,22272],{},"ca.pem"," file is located at ",[18,22275,22276],{},"\u002Fusr\u002Flocal\u002Fssl\u002Fca.pem",[50,22278,22280],{"className":52,"code":22279,"language":54,"meta":55,"style":55},"DOCKER_DRIVER_PRIVATE_CA_PATH=\"\u002Fusr\u002Flocal\u002Fssl\u002Fca.pem\"\n",[18,22281,22282],{"__ignoreMap":55},[59,22283,22284,22286,22288],{"class":61,"line":62},[59,22285,22268],{"class":178},[59,22287,1373],{"class":1372},[59,22289,22290],{"class":69},"\"\u002Fusr\u002Flocal\u002Fssl\u002Fca.pem\"\n",[31,22292,22293,22262,22296,22298,22299,22302,22303,22306,22307,22309,22310],{},[364,22294,22295],{},"Modify the Compose file.",[18,22297,22205],{}," file used to create the FlowFuse Docker stack, uncomment the ",[18,22300,22301],{},"NODE_EXTRA_CA_CERTS"," environment variable and the corresponding volume mount on the ",[18,22304,22305],{},"forge"," service so the certificate is passed correctly to the platform. You can apply both changes with a single ",[18,22308,21620],{}," command:",[50,22311,22313],{"className":52,"code":22312,"language":54,"meta":55,"style":55},"sed -i.bak -E -e 's\u002F^#([[:space:]]*- \"NODE_EXTRA_CA_CERTS=)\u002F\\1\u002F' -e 's\u002F^#([[:space:]]*- \\$\\{DOCKER_DRIVER_PRIVATE_CA_PATH\\})\u002F\\1\u002F' docker-compose.yml && rm docker-compose.yml.bak\n",[18,22314,22315],{"__ignoreMap":55},[59,22316,22317,22319,22322,22325,22327,22330,22332,22335,22337,22340,22343],{"class":61,"line":62},[59,22318,21620],{"class":65},[59,22320,22321],{"class":73}," -i.bak",[59,22323,22324],{"class":73}," -E",[59,22326,8453],{"class":73},[59,22328,22329],{"class":69}," 's\u002F^#([[:space:]]*- \"NODE_EXTRA_CA_CERTS=)\u002F\\1\u002F'",[59,22331,8453],{"class":73},[59,22333,22334],{"class":69}," 's\u002F^#([[:space:]]*- \\$\\{DOCKER_DRIVER_PRIVATE_CA_PATH\\})\u002F\\1\u002F'",[59,22336,21577],{"class":69},[59,22338,22339],{"class":178}," && ",[59,22341,22342],{"class":65},"rm",[59,22344,22345],{"class":69}," docker-compose.yml.bak\n",[31,22347,22348,22351,22352],{},[364,22349,22350],{},"Restart the Forge service"," to apply the changes:",[50,22353,22355],{"className":52,"code":22354,"language":54,"meta":55,"style":55},"docker compose restart forge\n",[18,22356,22357],{"__ignoreMap":55},[59,22358,22359,22361,22363,22365],{"class":61,"line":62},[59,22360,66],{"class":65},[59,22362,21903],{"class":69},[59,22364,12696],{"class":69},[59,22366,22367],{"class":69}," forge\n",[31,22369,22370,22373],{},[364,22371,22372],{},"Apply changes to running instances."," Once the platform is online, manually suspend and start all running hosted instances so they pick up the new TLS settings.",[104,22375,22377],{"id":22376},"i-would-like-to-invite-my-team-members-to-the-platform-with-e-mail-how-can-i-do-that","I would like to invite my team members to the platform with e-mail, how can I do that?",[14,22379,22380,22381,22384,22385,22387,22388,22390,22391,22393],{},"In order to configure FlowFuse platform with external e-mail server, you need to adjust ",[18,22382,22383],{},"EMAIL_*"," variables in the ",[18,22386,21560],{}," file.\nFind the ",[18,22389,21560],{}," file end edit ",[18,22392,19235],{}," section with following details:",[28,22395,22396,22405,22411,22419,22427,22433],{},[31,22397,22398,22401,22402,22404],{},[18,22399,22400],{},"EMAIL_ENABLED"," - set to ",[18,22403,3558],{}," to enable e-mail functionality",[31,22406,22407,22410],{},[18,22408,22409],{},"EMAIL_HOST"," - provide SMTP server host",[31,22412,22413,22416,22417,660],{},[18,22414,22415],{},"EMAIL_PORT"," - provide SMTP server port (default is ",[18,22418,19300],{},[31,22420,22421,22401,22424,22426],{},[18,22422,22423],{},"EMAIL_SECURE",[18,22425,3558],{}," if the connection should be secured",[31,22428,22429,22432],{},[18,22430,22431],{},"EMAIL_USER"," - provide SMTP server username",[31,22434,22435,22438,22439],{},[18,22436,22437],{},"EMAIL_PASSWORD"," - provide SMTP for the user defined in ",[18,22440,22431],{},[14,22442,22443],{},"Restart the core application to apply the changes:",[50,22445,22446],{"className":52,"code":22354,"language":54,"meta":55,"style":55},[18,22447,22448],{"__ignoreMap":55},[59,22449,22450,22452,22454,22456],{"class":61,"line":62},[59,22451,66],{"class":65},[59,22453,21903],{"class":69},[59,22455,12696],{"class":69},[59,22457,22367],{"class":69},[104,22459,22461],{"id":22460},"connection-refused-error","Connection Refused error",[14,22463,22464],{},"After starting the platform, I can't access it in the browser - I see \"Connection Refused error\"",[14,22466,22467],{},"If you are using the Digital Ocean Docker Droplet to host FlowFuse you will need to ensure that port 80 & 443 are opened in the UFW firewall before starting.",[14,22469,22470],{},"FlowFuse platform is running on ports 80 and 443, so you need to open these ports in the firewall. Below are examples of commands to open these ports:",[14,22472,22473],{},"Ubuntu:",[50,22475,22477],{"className":52,"code":22476,"language":54,"meta":55,"style":55},"sudo ufw apply http\nsudo ufw apply https\n",[18,22478,22479,22492],{"__ignoreMap":55},[59,22480,22481,22483,22486,22489],{"class":61,"line":62},[59,22482,9188],{"class":65},[59,22484,22485],{"class":69}," ufw",[59,22487,22488],{"class":69}," apply",[59,22490,22491],{"class":69}," http\n",[59,22493,22494,22496,22498,22500],{"class":61,"line":77},[59,22495,9188],{"class":65},[59,22497,22485],{"class":69},[59,22499,22488],{"class":69},[59,22501,22502],{"class":69}," https\n",[14,22504,22505],{},"CentOS:",[50,22507,22509],{"className":52,"code":22508,"language":54,"meta":55,"style":55},"sudo firewall-cmd --zone=public --add-service=http --permanent\nsudo firewall-cmd --zone=public --add-service=https --permanent\nsudo firewall-cmd --reload\n",[18,22510,22511,22527,22540],{"__ignoreMap":55},[59,22512,22513,22515,22518,22521,22524],{"class":61,"line":62},[59,22514,9188],{"class":65},[59,22516,22517],{"class":69}," firewall-cmd",[59,22519,22520],{"class":73}," --zone=public",[59,22522,22523],{"class":73}," --add-service=http",[59,22525,22526],{"class":73}," --permanent\n",[59,22528,22529,22531,22533,22535,22538],{"class":61,"line":77},[59,22530,9188],{"class":65},[59,22532,22517],{"class":69},[59,22534,22520],{"class":73},[59,22536,22537],{"class":73}," --add-service=https",[59,22539,22526],{"class":73},[59,22541,22542,22544,22546],{"class":61,"line":88},[59,22543,9188],{"class":65},[59,22545,22517],{"class":69},[59,22547,22548],{"class":73}," --reload\n",[14,22550,22551],{},"Windows (command prompt):",[50,22553,22555],{"className":52,"code":22554,"language":54,"meta":55,"style":55},"netsh advfirewall firewall add rule name=\"Open Port 80\" dir=in action=allow protocol=TCP localport=80\nnetsh advfirewall firewall add rule name=\"Open Port 443\" dir=in action=allow protocol=TCP localport=443\n",[18,22556,22557,22592],{"__ignoreMap":55},[59,22558,22559,22562,22565,22568,22571,22574,22577,22580,22583,22586,22589],{"class":61,"line":62},[59,22560,22561],{"class":65},"netsh",[59,22563,22564],{"class":69}," advfirewall",[59,22566,22567],{"class":69}," firewall",[59,22569,22570],{"class":69}," add",[59,22572,22573],{"class":69}," rule",[59,22575,22576],{"class":69}," name=\"Open Port 80\"",[59,22578,22579],{"class":69}," dir=in",[59,22581,22582],{"class":69}," action=allow",[59,22584,22585],{"class":69}," protocol=TCP",[59,22587,22588],{"class":69}," localport=",[59,22590,22591],{"class":73},"80\n",[59,22593,22594,22596,22598,22600,22602,22604,22607,22609,22611,22613,22615],{"class":61,"line":77},[59,22595,22561],{"class":65},[59,22597,22564],{"class":69},[59,22599,22567],{"class":69},[59,22601,22570],{"class":69},[59,22603,22573],{"class":69},[59,22605,22606],{"class":69}," name=\"Open Port 443\"",[59,22608,22579],{"class":69},[59,22610,22582],{"class":69},[59,22612,22585],{"class":69},[59,22614,22588],{"class":69},[59,22616,22617],{"class":73},"443\n",[14,22619,22620],{},"Windows (PowerShell):",[50,22622,22624],{"className":13142,"code":22623,"language":12293,"meta":55,"style":55},"New-NetFireWallRule -DisplayName 'WSL 8080TCP' -Direction Inbound -LocalPort 8080 -Action Allow -Protocol TCP\nNew-NetFireWallRule -DisplayName 'WSL 8080TCP' -Direction Outbound -LocalPort 8080 -Action Allow -Protocol TCP\n",[18,22625,22626,22631],{"__ignoreMap":55},[59,22627,22628],{"class":61,"line":62},[59,22629,22630],{},"New-NetFireWallRule -DisplayName 'WSL 8080TCP' -Direction Inbound -LocalPort 8080 -Action Allow -Protocol TCP\n",[59,22632,22633],{"class":61,"line":77},[59,22634,22635],{},"New-NetFireWallRule -DisplayName 'WSL 8080TCP' -Direction Outbound -LocalPort 8080 -Action Allow -Protocol TCP\n",[104,22637,22639],{"id":22638},"i-installed-flowfuse-on-windows-with-wsl2-application-is-running-but-i-cant-access-it-in-the-browser","I installed FlowFuse on Windows with WSL2, application is running but I can't access it in the browser",[14,22641,22642,22643,22647],{},"Next to ",[41,22644,22646],{"href":22645},"#connection-refused-error","opening the ports in the firewall",",\nyou need to configure port forwarding from Windows host to WSL2 server.",[14,22649,22650],{},"To forward traffic from an external IP to your container, run the following PowerShell command (administrator privileges required):",[50,22652,22654],{"className":13142,"code":22653,"language":12293,"meta":55,"style":55},"netsh interface portproxy add v4tov4 listenport=80 listenaddress=0.0.0.0 connectport=80 connectaddress=127.0.0.1\n",[18,22655,22656],{"__ignoreMap":55},[59,22657,22658],{"class":61,"line":62},[59,22659,22653],{},[14,22661,22662],{},"This command forwards traffic from port 80 on your external IP address to port 80 on your localhost, where the Nginx Proxy container is listening for connections.",[104,22664,22666],{"id":22665},"how-can-i-enable-persistent-storage-for-node-red-instances","How can I enable persistent storage for Node-RED instances?",[14,22668,22669],{},"Node-RED instances running in Docker do not have direct access to a persistent file system to store files or use for storing context data.",[14,22671,22672],{},"FlowFuse includes a File Storage service that can be enabled to provide persistent storage.",[14,22674,22675,22676,22679],{},"To disable the default File nodes, edit the Template and add ",[18,22677,22678],{},"10-file.js,23-watch.js"," to the \"Exclude nodes by filename\" section",[638,22681],{"src":22682,"width":15952},"\u002Fdocs\u002Finstall\u002Fimages\u002Ffile-node-template.png",[14,22684,22685],{},"FlowFuse Docker Compose files includes FlowFuse File Storage component by default and starts it along with the platform.",[14,22687,22688,22689,273],{},"Full details on configuring the File Storage service are available ",[41,22690,785],{"href":20008},[23,22692,9954],{"id":9953},[28,22694,22695,22705,22711],{},[31,22696,22697,22698,22701,22702,22704],{},"Bring the services down with ",[18,22699,22700],{},"docker compose -p flowfuse down -v"," (note the extra ",[18,22703,16986],{}," to delete all the volumes, only include this if you do not want to reuse this install)",[31,22706,15450,22707,22710],{},[18,22708,22709],{},"docker images"," to list container images",[31,22712,15450,22713,22716,22717],{},[18,22714,22715],{},"docker rmi [imagename]:[tag]"," to remove all images that start with ",[18,22718,22719],{},"flowfuse\u002F",[316,22721,22722],{},"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 .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":55,"searchDepth":77,"depth":77,"links":22724},[22725,22726,22730,22735,22736,22737,22738,22747],{"id":21368,"depth":77,"text":21369},{"id":25,"depth":77,"text":26,"children":22727},[22728,22729],{"id":12207,"depth":88,"text":12208},{"id":21034,"depth":88,"text":21035},{"id":9163,"depth":77,"text":9164,"children":22731},[22732,22733,22734],{"id":21553,"depth":88,"text":21554},{"id":21597,"depth":88,"text":21598},{"id":21654,"depth":88,"text":21655},{"id":21873,"depth":77,"text":21874},{"id":9513,"depth":77,"text":584},{"id":9855,"depth":77,"text":9856},{"id":22177,"depth":77,"text":22178,"children":22739},[22740,22741,22742,22743,22744,22745,22746],{"id":22181,"depth":88,"text":22182},{"id":22232,"depth":88,"text":22233},{"id":22248,"depth":88,"text":21737},{"id":22376,"depth":88,"text":22377},{"id":22460,"depth":88,"text":22461},{"id":22638,"depth":88,"text":22639},{"id":22665,"depth":88,"text":22666},{"id":9953,"depth":77,"text":9954},{},"install\u002Fdocker\u002FREADME.md","\u002Fdocs\u002Finstall\u002Fdocker",{"description":55},"docs\u002Finstall\u002Fdocker\u002Findex","I8viJ68fPz9_SFdS6gRA5N7Ff3fpovOw71ojMjpH_IU",{"id":22755,"title":1134,"body":22756,"description":22763,"extension":329,"layout":330,"meta":22972,"navGroup":330,"navOrder":330,"navTitle":22973,"navigation":187,"originalPath":22974,"path":22975,"redirect":330,"seo":22976,"stem":22977,"updated":337,"version":338,"__hash__":22978},"docs\u002Fdocs\u002Finstall\u002Fdocker\u002Fstacks.md",{"type":7,"value":22757,"toc":22969},[22758,22761,22764,22767,22791,22805,22809,22837,22846,22849,22887,22890,22923,22926,22949,22959,22966],[10,22759,1134],{"id":22760},"docker-stacks",[14,22762,22763],{},"A Stack defines a set of platform configuration options that will get\napplied to each Node-RED instance when created.",[14,22765,22766],{},"For container based deployment models, this covers three things:",[28,22768,22769,22776,22782],{},[31,22770,22771,22773,22774,273],{},[18,22772,10054],{}," - the amount of memory (in MB) to limit container to. Recommended minimum: ",[18,22775,10062],{},[31,22777,22778,22781],{},[18,22779,22780],{},"cpu"," - a value between 1 and 100 that is the % of a CPU core the container should be allowed to consume.",[31,22783,22784,22787,22788],{},[18,22785,22786],{},"container location"," - this is the fully qualified name of the container to use. The default container built when following the install instructions is named ",[18,22789,22790],{},"flowfuse\u002Fnode-red:latest",[14,22792,22793,22794,20053,22797,22799,22800,273],{},"If you wish to use different Node-RED version, you need to specify the name of the container and the version you want to use. For example, if you want to use Node-RED v3.1.x, you should enter ",[18,22795,22796],{},"flowfuse\u002Fnode-red:latest-3.1.x",[18,22798,22786],{}," section of the Stack configuration.\nFull list of available pre-built containers can be found on ",[41,22801,22804],{"href":22802,"rel":22803},"https:\u002F\u002Fhub.docker.com\u002Fr\u002Fflowfuse\u002Fnode-red\u002Ftags",[831],"Docker Hub",[23,22806,22808],{"id":22807},"creating-own-containers","Creating Own Containers",[14,22810,22811,22812,302,22815,22817,22818,302,22820,20053,22822,22827,22828,22832,22833,22836],{},"As mentioned in the previous paragraph, we encourage to use our pre-built containers in your stacks.\nHowever, if you want to create your own container, you can do so by creating a ",[18,22813,22814],{},"Dockerfile",[18,22816,7977],{}," files.\nThere is an example ",[18,22819,22814],{},[18,22821,7977],{},[41,22823,22826],{"href":22824,"rel":22825},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdocker-compose\u002Ftree\u002Fmain\u002Fnode-red-container",[831],"node-red-container","\ndirectory of the ",[41,22829,161],{"href":22830,"rel":22831},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdocker-compose",[831]," project. This will start with ",[18,22834,22835],{},"nodered\u002Fnode-red:latest","\nas it's base and then add the required FlowFuse components.",[14,22838,22839,22840,22845],{},"Builds of this container for amd64, arm64 and armv7 are built for every release and published to Docker hub as ",[41,22841,22844],{"href":22842,"rel":22843},"https:\u002F\u002Fhub.docker.com\u002Fr\u002Fflowfuse\u002Fnode-red",[831],"flowfuse\u002Fnode-red",". These can be used as a base to build custom stacks.",[14,22847,22848],{},"If you wanted to pin at Node-RED v3.0.2 you would change the first line to:",[50,22850,22853],{"className":22851,"code":22852,"language":66,"meta":55,"style":55},"language-docker shiki shiki-themes github-light github-dark","FROM nodered\u002Fnode-red:3.0.2\n\nARG REGISTRY\nRUN if [[ ! -z \"$REGISTRY\" ]] ; then npm config set @flowfuse:registry \"$REGISTRY\"; fi\n\nCOPY package.json \u002Fdata\n...\n",[18,22854,22855,22860,22864,22869,22874,22878,22883],{"__ignoreMap":55},[59,22856,22857],{"class":61,"line":62},[59,22858,22859],{},"FROM nodered\u002Fnode-red:3.0.2\n",[59,22861,22862],{"class":61,"line":77},[59,22863,188],{"emptyLinePlaceholder":187},[59,22865,22866],{"class":61,"line":88},[59,22867,22868],{},"ARG REGISTRY\n",[59,22870,22871],{"class":61,"line":99},[59,22872,22873],{},"RUN if [[ ! -z \"$REGISTRY\" ]] ; then npm config set @flowfuse:registry \"$REGISTRY\"; fi\n",[59,22875,22876],{"class":61,"line":156},[59,22877,188],{"emptyLinePlaceholder":187},[59,22879,22880],{"class":61,"line":216},[59,22881,22882],{},"COPY package.json \u002Fdata\n",[59,22884,22885],{"class":61,"line":224},[59,22886,5847],{},[14,22888,22889],{},"To add nodes to the default image you can extend the supplied container.\nThe following Dockerfile will install the node-red-dashboard",[50,22891,22893],{"className":22851,"code":22892,"language":66,"meta":55,"style":55},"FROM flowfuse\u002Fnode-red\n\nWORKDIR \u002Fusr\u002Fsrc\u002Fnode-red\nRUN npm install node-red-dashboard\n\nWORKDIR \u002Fusr\u002Fsrc\u002Fflowforge-nr-launcher\n",[18,22894,22895,22900,22904,22909,22914,22918],{"__ignoreMap":55},[59,22896,22897],{"class":61,"line":62},[59,22898,22899],{},"FROM flowfuse\u002Fnode-red\n",[59,22901,22902],{"class":61,"line":77},[59,22903,188],{"emptyLinePlaceholder":187},[59,22905,22906],{"class":61,"line":88},[59,22907,22908],{},"WORKDIR \u002Fusr\u002Fsrc\u002Fnode-red\n",[59,22910,22911],{"class":61,"line":99},[59,22912,22913],{},"RUN npm install node-red-dashboard\n",[59,22915,22916],{"class":61,"line":156},[59,22917,188],{"emptyLinePlaceholder":187},[59,22919,22920],{"class":61,"line":216},[59,22921,22922],{},"WORKDIR \u002Fusr\u002Fsrc\u002Fflowforge-nr-launcher\n",[14,22924,22925],{},"To build the container run the following:",[50,22927,22931],{"className":22928,"code":22929,"language":22930,"meta":55,"style":55},"language-shell shiki shiki-themes github-light github-dark","docker build node-red-container\u002FDockerfile-dashboard -t flowfuse\u002Fnode-red-dashboard:3.0.2\n","shell",[18,22932,22933],{"__ignoreMap":55},[59,22934,22935,22937,22940,22943,22946],{"class":61,"line":62},[59,22936,66],{"class":65},[59,22938,22939],{"class":69}," build",[59,22941,22942],{"class":69}," node-red-container\u002FDockerfile-dashboard",[59,22944,22945],{"class":73}," -t",[59,22947,22948],{"class":69}," flowfuse\u002Fnode-red-dashboard:3.0.2\n",[14,22950,22951,22952,20053,22955,22958],{},"You would then enter ",[18,22953,22954],{},"flowfuse\u002Fnode-red-dashboard:3.0.2",[18,22956,22957],{},"container"," section\nof the Stack configuration.",[14,22960,22961,22962,273],{},"Stacks can be changed on a per instance basis, see also the\n",[41,22963,22965],{"href":22964},"\u002Fdocs\u002Fuser\u002Fchangestack","user stack documentation",[316,22967,22968],{},"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}",{"title":55,"searchDepth":77,"depth":77,"links":22970},[22971],{"id":22807,"depth":77,"text":22808},{},"Add Project Stacks on Docker","install\u002Fdocker\u002Fstacks.md","\u002Fdocs\u002Finstall\u002Fdocker\u002Fstacks",{"title":1134,"description":22763},"docs\u002Finstall\u002Fdocker\u002Fstacks","FTaGES6kxZ5qv7qt2V5ovW4MGCqNagnw3gT9kzuCFgo",{"id":22980,"title":22981,"body":22982,"description":22989,"extension":329,"layout":330,"meta":23516,"navGroup":330,"navOrder":330,"navTitle":23517,"navigation":187,"originalPath":23518,"path":23519,"redirect":330,"seo":23520,"stem":23521,"updated":337,"version":338,"__hash__":23522},"docs\u002Fdocs\u002Finstall\u002Fdocker\u002Fwindows-docker-ce.md","Using Docker on Windows",{"type":7,"value":22983,"toc":23506},[22984,22987,22990,22994,23001,23006,23009,23013,23020,23024,23027,23031,23039,23043,23046,23049,23053,23056,23058,23061,23072,23076,23079,23105,23112,23119,23133,23137,23140,23497,23503],[10,22985,22981],{"id":22986},"using-docker-on-windows",[14,22988,22989],{},"While Docker is inherently a Linux-based technology and we recommend running FlowFuse on Linux when ever possible, there are several ways to run Docker on Windows.\nBelow, we outline the primary methods available, along with recommendations based on specific needs and use cases.",[104,22991,22993],{"id":22992},"docker-desktop","Docker Desktop",[14,22995,22996,23000],{},[41,22997,22993],{"href":22998,"rel":22999},"https:\u002F\u002Fdocs.docker.com\u002Fdesktop\u002Finstall\u002Fwindows-install\u002F",[831]," is the most straightforward option for running Docker on Windows, offering a complete Docker environment with GUI support.\nIt’s well-suited for users seeking ease of setup and use.",[14,23002,23003],{},[364,23004,23005],{},"Recommendation:",[14,23007,23008],{},"Use Docker Desktop if licensing is not a constraint for your organization, as it provides a user-friendly, fully-integrated Docker experience on Windows.",[104,23010,23012],{"id":23011},"rancher-desktop","Rancher Desktop",[14,23014,23015,23019],{},[41,23016,23012],{"href":23017,"rel":23018},"https:\u002F\u002Francherdesktop.io\u002F",[831]," is a free, open-source alternative to Docker Desktop, providing a similar experience but without the licensing concerns.\nRancher Desktop includes both container management and Kubernetes support, making it a flexible choice for containerized workloads on Windows.",[14,23021,23022],{},[364,23023,23005],{},[14,23025,23026],{},"Consider Rancher Desktop if you prefer an open-source tool with no licensing restrictions.",[104,23028,23030],{"id":23029},"windows-subsystem-for-linux-wsl2","Windows Subsystem for Linux (WSL2)",[14,23032,23033,23038],{},[41,23034,23037],{"href":23035,"rel":23036},"https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fwindows\u002Fwsl\u002Finstall",[831],"WSL2"," enables users to run a Linux environment directly on Windows.\nWSL2 supports running Docker Engine without the need for a GUI, making it ideal for headless configurations or for those looking to operate Docker from a Linux command line on Windows.",[14,23040,23041],{},[364,23042,23005],{},[14,23044,23045],{},"WSL2 is best suited for advanced users who are comfortable with command-line tools and who want to run Docker Engine directly within a Linux environment on Windows.",[14,23047,23048],{},"For the installation of Docker Engine using WSL2, refer to the next paragraph.",[23,23050,23052],{"id":23051},"how-to-install-docker-engine-docker-ce-on-windows-using-wsl2","How to Install Docker Engine (Docker CE) on Windows using WSL2",[14,23054,23055],{},"This guide explains how to install Docker Engine (Docker CE) on Windows using Windows Subsystem for Linux (WSL2).",[104,23057,26],{"id":25},[14,23059,23060],{},"Ensure your system meets the following Windows Subsystem for Linux v2 requirements:",[28,23062,23063,23066,23069],{},[31,23064,23065],{},"Windows Server 2022",[31,23067,23068],{},"Windows 10 version 2004 and higher (Build 19041 and higher)",[31,23070,23071],{},"Windows 11",[104,23073,23075],{"id":23074},"step-1-install-windows-subsystem-for-linux","Step 1: Install Windows Subsystem for Linux",[14,23077,23078],{},"Open PowerShell as an administrator and run the following commands:",[398,23080,23081,23093],{},[31,23082,23083,23084],{},"Install WSL and the default Linux distribution (Ubuntu) using the following command:",[50,23085,23087],{"className":13142,"code":23086,"language":12293,"meta":55,"style":55},"wsl --install\n",[18,23088,23089],{"__ignoreMap":55},[59,23090,23091],{"class":61,"line":62},[59,23092,23086],{},[31,23094,23095,23096],{},"Reboot your system to apply changes",[50,23097,23099],{"className":13142,"code":23098,"language":12293,"meta":55,"style":55},"shutdown -r -t 5\n",[18,23100,23101],{"__ignoreMap":55},[59,23102,23103],{"class":61,"line":62},[59,23104,23098],{},[14,23106,23107,23108],{},"After the reboot, WSL will automatically start installing the Ubuntu Linux distribution.\nYou will be prompted to create a new UNIX account. Follow the instructions to create a new user account and set a password.\n",[638,23109],{"alt":23110,"src":23111},"wsl-unix-user-creation","\u002Fdocs\u002Finstall\u002Fimages\u002Fwsl-unix-user.png",[14,23113,23114,23115],{},"Once completed, you will be dropped into a new Ubuntu shell.\n",[638,23116],{"alt":23117,"src":23118},"wsl-install-complete","\u002Fdocs\u002Finstall\u002Fimages\u002Fwsl-install-complete.png",[398,23120,23121],{"start":88},[31,23122,23123,23124],{},"Confirm using proper WSL version, by running (in Powershell window):\n",[50,23125,23127],{"className":13142,"code":23126,"language":12293,"meta":55,"style":55},"wsl --status\n",[18,23128,23129],{"__ignoreMap":55},[59,23130,23131],{"class":61,"line":62},[59,23132,23126],{},[104,23134,23136],{"id":23135},"step-2-install-docker-on-ubuntu","Step 2: Install Docker on Ubuntu",[14,23138,23139],{},"Once the Ubuntu system is ready, follow these steps to install Docker:",[398,23141,23142,23207,23369,23408,23442,23464],{},[31,23143,23144,23147,23149,23150],{},[364,23145,23146],{},"Remove Conflicting Packages",[662,23148],{},"First, remove any existing Docker packages that might conflict with the installation:",[50,23151,23153],{"className":52,"code":23152,"language":54,"meta":55,"style":55},"for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done\n",[18,23154,23155],{"__ignoreMap":55},[59,23156,23157,23160,23163,23166,23169,23172,23175,23178,23181,23184,23187,23190,23193,23196,23198,23201,23204],{"class":61,"line":62},[59,23158,23159],{"class":1372},"for",[59,23161,23162],{"class":178}," pkg ",[59,23164,23165],{"class":1372},"in",[59,23167,23168],{"class":69}," docker.io",[59,23170,23171],{"class":69}," docker-doc",[59,23173,23174],{"class":69}," docker-compose",[59,23176,23177],{"class":69}," docker-compose-v2",[59,23179,23180],{"class":69}," podman-docker",[59,23182,23183],{"class":69}," containerd",[59,23185,23186],{"class":69}," runc",[59,23188,23189],{"class":178},"; ",[59,23191,23192],{"class":1372},"do",[59,23194,23195],{"class":65}," sudo",[59,23197,20405],{"class":69},[59,23199,23200],{"class":69}," remove",[59,23202,23203],{"class":178}," $pkg; ",[59,23205,23206],{"class":1372},"done\n",[31,23208,23209,23212,23214,23215,23289,23291,23292],{},[364,23210,23211],{},"Add Docker’s Official GPG Key and APT Repository",[662,23213],{},"Next, update your package list and add Docker's official GPG key and APT repository:",[50,23216,23218],{"className":52,"code":23217,"language":54,"meta":55,"style":55},"sudo apt-get update\nsudo apt-get install ca-certificates curl\nsudo install -m 0755 -d \u002Fetc\u002Fapt\u002Fkeyrings\nsudo curl -fsSL https:\u002F\u002Fdownload.docker.com\u002Flinux\u002Fubuntu\u002Fgpg -o \u002Fetc\u002Fapt\u002Fkeyrings\u002Fdocker.asc\nsudo chmod a+r \u002Fetc\u002Fapt\u002Fkeyrings\u002Fdocker.asc\n",[18,23219,23220,23229,23243,23260,23277],{"__ignoreMap":55},[59,23221,23222,23224,23226],{"class":61,"line":62},[59,23223,9188],{"class":65},[59,23225,20405],{"class":69},[59,23227,23228],{"class":69}," update\n",[59,23230,23231,23233,23235,23237,23240],{"class":61,"line":77},[59,23232,9188],{"class":65},[59,23234,20405],{"class":69},[59,23236,7956],{"class":69},[59,23238,23239],{"class":69}," ca-certificates",[59,23241,23242],{"class":69}," curl\n",[59,23244,23245,23247,23249,23252,23255,23257],{"class":61,"line":88},[59,23246,9188],{"class":65},[59,23248,7956],{"class":69},[59,23250,23251],{"class":73}," -m",[59,23253,23254],{"class":73}," 0755",[59,23256,9743],{"class":73},[59,23258,23259],{"class":69}," \u002Fetc\u002Fapt\u002Fkeyrings\n",[59,23261,23262,23264,23267,23269,23272,23274],{"class":61,"line":99},[59,23263,9188],{"class":65},[59,23265,23266],{"class":69}," curl",[59,23268,12277],{"class":73},[59,23270,23271],{"class":69}," https:\u002F\u002Fdownload.docker.com\u002Flinux\u002Fubuntu\u002Fgpg",[59,23273,15021],{"class":73},[59,23275,23276],{"class":69}," \u002Fetc\u002Fapt\u002Fkeyrings\u002Fdocker.asc\n",[59,23278,23279,23281,23284,23287],{"class":61,"line":156},[59,23280,9188],{"class":65},[59,23282,23283],{"class":69}," chmod",[59,23285,23286],{"class":69}," a+r",[59,23288,23276],{"class":69},[662,23290],{},"Add the Docker repository to your APT sources:",[50,23293,23295],{"className":52,"code":23294,"language":54,"meta":55,"style":55},"echo \\\n  \"deb [arch=$(dpkg --print-architecture) signed-by=\u002Fetc\u002Fapt\u002Fkeyrings\u002Fdocker.asc] https:\u002F\u002Fdownload.docker.com\u002Flinux\u002Fubuntu \\\n  $(. \u002Fetc\u002Fos-release && echo \"$VERSION_CODENAME\") stable\" | \\\n  sudo tee \u002Fetc\u002Fapt\u002Fsources.list.d\u002Fdocker.list > \u002Fdev\u002Fnull\nsudo apt-get update\n",[18,23296,23297,23303,23320,23346,23361],{"__ignoreMap":55},[59,23298,23299,23301],{"class":61,"line":62},[59,23300,20730],{"class":73},[59,23302,74],{"class":73},[59,23304,23305,23308,23311,23314,23317],{"class":61,"line":77},[59,23306,23307],{"class":69},"  \"deb [arch=$(",[59,23309,23310],{"class":65},"dpkg",[59,23312,23313],{"class":73}," --print-architecture",[59,23315,23316],{"class":69},") signed-by=\u002Fetc\u002Fapt\u002Fkeyrings\u002Fdocker.asc] https:\u002F\u002Fdownload.docker.com\u002Flinux\u002Fubuntu ",[59,23318,23319],{"class":73},"\\\n",[59,23321,23322,23325,23327,23330,23332,23335,23338,23341,23344],{"class":61,"line":88},[59,23323,23324],{"class":69},"  $(",[59,23326,273],{"class":73},[59,23328,23329],{"class":69}," \u002Fetc\u002Fos-release && ",[59,23331,20730],{"class":73},[59,23333,23334],{"class":69}," \"",[59,23336,23337],{"class":178},"$VERSION_CODENAME",[59,23339,23340],{"class":69},"\") stable\"",[59,23342,23343],{"class":1372}," |",[59,23345,74],{"class":73},[59,23347,23348,23351,23353,23356,23358],{"class":61,"line":99},[59,23349,23350],{"class":65},"  sudo",[59,23352,20839],{"class":69},[59,23354,23355],{"class":69}," \u002Fetc\u002Fapt\u002Fsources.list.d\u002Fdocker.list",[59,23357,20464],{"class":1372},[59,23359,23360],{"class":69}," \u002Fdev\u002Fnull\n",[59,23362,23363,23365,23367],{"class":61,"line":156},[59,23364,9188],{"class":65},[59,23366,20405],{"class":69},[59,23368,23228],{"class":69},[31,23370,23371,23374,23376,23377],{},[364,23372,23373],{},"Install Docker Packages",[662,23375],{},"Now, install Docker and its associated packages:",[50,23378,23380],{"className":52,"code":23379,"language":54,"meta":55,"style":55},"sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\n",[18,23381,23382],{"__ignoreMap":55},[59,23383,23384,23386,23388,23390,23393,23396,23399,23402,23405],{"class":61,"line":62},[59,23385,9188],{"class":65},[59,23387,20405],{"class":69},[59,23389,7956],{"class":69},[59,23391,23392],{"class":73}," -y",[59,23394,23395],{"class":69}," docker-ce",[59,23397,23398],{"class":69}," docker-ce-cli",[59,23400,23401],{"class":69}," containerd.io",[59,23403,23404],{"class":69}," docker-buildx-plugin",[59,23406,23407],{"class":69}," docker-compose-plugin\n",[31,23409,23410,23413,23415,23416,23418,23419,3939,23421],{},[364,23411,23412],{},"Add user to Docker group",[662,23414],{},"Add your user to the ",[18,23417,66],{}," group to run Docker commands without ",[18,23420,9188],{},[50,23422,23424],{"className":52,"code":23423,"language":54,"meta":55,"style":55},"sudo usermod -aG docker ubuntu\n",[18,23425,23426],{"__ignoreMap":55},[59,23427,23428,23430,23433,23436,23439],{"class":61,"line":62},[59,23429,9188],{"class":65},[59,23431,23432],{"class":69}," usermod",[59,23434,23435],{"class":73}," -aG",[59,23437,23438],{"class":69}," docker",[59,23440,23441],{"class":69}," ubuntu\n",[31,23443,23444,23447,23449,23450],{},[364,23445,23446],{},"Start the Docker Service",[662,23448],{},"Start Docker using the following command:",[50,23451,23453],{"className":52,"code":23452,"language":54,"meta":55,"style":55},"sudo \u002Fetc\u002Finit.d\u002Fdocker start\n",[18,23454,23455],{"__ignoreMap":55},[59,23456,23457,23459,23462],{"class":61,"line":62},[59,23458,9188],{"class":65},[59,23460,23461],{"class":69}," \u002Fetc\u002Finit.d\u002Fdocker",[59,23463,9510],{"class":69},[31,23465,23466,23469,23471,23472,23488,23490,23491,23493],{},[364,23467,23468],{},"Verify Docker Installation",[662,23470],{},"Run a test Docker container to verify that Docker is installed and running correctly:",[50,23473,23475],{"className":52,"code":23474,"language":54,"meta":55,"style":55},"sudo docker run hello-world\n",[18,23476,23477],{"__ignoreMap":55},[59,23478,23479,23481,23483,23485],{"class":61,"line":62},[59,23480,9188],{"class":65},[59,23482,23438],{"class":69},[59,23484,70],{"class":69},[59,23486,23487],{"class":69}," hello-world\n",[662,23489],{},"If Docker is installed correctly, you should see a similar output:",[662,23492],{},[638,23494],{"alt":23495,"src":23496},"wsl-docker-installation-complete","\u002Fdocs\u002Finstall\u002Fimages\u002Fwsl-docker-complete.png",[14,23498,23499,23500,273],{},"Once Docker is installed, you can ",[41,23501,23502],{"href":22750},"install the FlowFuse platform using docker compose",[316,23504,23505],{},"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 .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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}",{"title":55,"searchDepth":77,"depth":77,"links":23507},[23508,23509,23510,23511],{"id":22992,"depth":88,"text":22993},{"id":23011,"depth":88,"text":23012},{"id":23029,"depth":88,"text":23030},{"id":23051,"depth":77,"text":23052,"children":23512},[23513,23514,23515],{"id":25,"depth":88,"text":26},{"id":23074,"depth":88,"text":23075},{"id":23135,"depth":88,"text":23136},{},"Docker Engine on Windows","install\u002Fdocker\u002Fwindows-docker-ce.md","\u002Fdocs\u002Finstall\u002Fdocker\u002Fwindows-docker-ce",{"title":22981,"description":22989},"docs\u002Finstall\u002Fdocker\u002Fwindows-docker-ce","_P6YhLs8E_F8IPzVj4Y_v5ngKZPx6x0Fydaxnait_nA",{"id":23524,"title":23525,"body":23526,"description":55,"extension":329,"layout":330,"meta":23763,"navGroup":330,"navOrder":330,"navTitle":19235,"navigation":187,"originalPath":23764,"path":19364,"redirect":330,"seo":23765,"stem":23766,"updated":337,"version":338,"__hash__":23767},"docs\u002Fdocs\u002Finstall\u002Femail_providers.md","EmailProviders",{"type":7,"value":23527,"toc":23757},[23528,23532,23536,23636,23643,23647,23754],[23,23529,23531],{"id":23530},"example-configuration-for-common-email-platforms","Example configuration for common email platforms",[104,23533,23535],{"id":23534},"gmail","GMail",[50,23537,23539],{"className":165,"code":23538,"language":167,"meta":55,"style":55},"email:\n  enabled: true\n  debug: false\n  smtp:\n    host: smtp.gmail.com\n    port: 465\n    secure: true\n    auth:\n        user: [USER]@gmail.com\n        pass: [PASSWORD]\n",[18,23540,23541,23547,23555,23564,23571,23581,23590,23599,23606,23623],{"__ignoreMap":55},[59,23542,23543,23545],{"class":61,"line":62},[59,23544,2769],{"class":174},[59,23546,196],{"class":178},[59,23548,23549,23551,23553],{"class":61,"line":77},[59,23550,2957],{"class":174},[59,23552,179],{"class":178},[59,23554,3230],{"class":73},[59,23556,23557,23560,23562],{"class":61,"line":88},[59,23558,23559],{"class":174},"  debug",[59,23561,179],{"class":178},[59,23563,2962],{"class":73},[59,23565,23566,23569],{"class":61,"line":99},[59,23567,23568],{"class":174},"  smtp",[59,23570,196],{"class":178},[59,23572,23573,23576,23578],{"class":61,"line":156},[59,23574,23575],{"class":174},"    host",[59,23577,179],{"class":178},[59,23579,23580],{"class":69},"smtp.gmail.com\n",[59,23582,23583,23585,23587],{"class":61,"line":216},[59,23584,14064],{"class":174},[59,23586,179],{"class":178},[59,23588,23589],{"class":73},"465\n",[59,23591,23592,23595,23597],{"class":61,"line":224},[59,23593,23594],{"class":174},"    secure",[59,23596,179],{"class":178},[59,23598,3230],{"class":73},[59,23600,23601,23604],{"class":61,"line":233},[59,23602,23603],{"class":174},"    auth",[59,23605,196],{"class":178},[59,23607,23608,23611,23614,23617,23620],{"class":61,"line":241},[59,23609,23610],{"class":174},"        user",[59,23612,23613],{"class":178},": [",[59,23615,23616],{"class":69},"USER",[59,23618,23619],{"class":178},"]@",[59,23621,23622],{"class":69},"gmail.com\n",[59,23624,23625,23628,23630,23633],{"class":61,"line":249},[59,23626,23627],{"class":174},"        pass",[59,23629,23613],{"class":178},[59,23631,23632],{"class":69},"PASSWORD",[59,23634,23635],{"class":178},"]\n",[14,23637,23638,23639],{},"Note: Gmail may require an app specific password to be created if you are using 2FA on the account you can set that up ",[41,23640,785],{"href":23641,"rel":23642},"https:\u002F\u002Fsecurity.google.com\u002Fsettings\u002Fsecurity\u002Fapppasswords",[831],[104,23644,23646],{"id":23645},"office365","Office365",[50,23648,23650],{"className":165,"code":23649,"language":167,"meta":55,"style":55},"email:\n  enabled: true\n  debug: false\n  smtp:\n    host: smtp.office365.com,\n    secure: false\n    tls:\n      ciphers: \"SSLv3\",\n      rejectUnauthorized: false\n    auth:\n      user: [USERNAME]\n      pass: [PASSWORD]\n",[18,23651,23652,23658,23666,23674,23680,23689,23697,23704,23716,23725,23731,23743],{"__ignoreMap":55},[59,23653,23654,23656],{"class":61,"line":62},[59,23655,2769],{"class":174},[59,23657,196],{"class":178},[59,23659,23660,23662,23664],{"class":61,"line":77},[59,23661,2957],{"class":174},[59,23663,179],{"class":178},[59,23665,3230],{"class":73},[59,23667,23668,23670,23672],{"class":61,"line":88},[59,23669,23559],{"class":174},[59,23671,179],{"class":178},[59,23673,2962],{"class":73},[59,23675,23676,23678],{"class":61,"line":99},[59,23677,23568],{"class":174},[59,23679,196],{"class":178},[59,23681,23682,23684,23686],{"class":61,"line":156},[59,23683,23575],{"class":174},[59,23685,179],{"class":178},[59,23687,23688],{"class":69},"smtp.office365.com,\n",[59,23690,23691,23693,23695],{"class":61,"line":216},[59,23692,23594],{"class":174},[59,23694,179],{"class":178},[59,23696,2962],{"class":73},[59,23698,23699,23702],{"class":61,"line":224},[59,23700,23701],{"class":174},"    tls",[59,23703,196],{"class":178},[59,23705,23706,23709,23711,23714],{"class":61,"line":233},[59,23707,23708],{"class":174},"      ciphers",[59,23710,179],{"class":178},[59,23712,23713],{"class":69},"\"SSLv3\"",[59,23715,2665],{"class":178},[59,23717,23718,23721,23723],{"class":61,"line":241},[59,23719,23720],{"class":174},"      rejectUnauthorized",[59,23722,179],{"class":178},[59,23724,2962],{"class":73},[59,23726,23727,23729],{"class":61,"line":249},[59,23728,23603],{"class":174},[59,23730,196],{"class":178},[59,23732,23733,23736,23738,23741],{"class":61,"line":257},[59,23734,23735],{"class":174},"      user",[59,23737,23613],{"class":178},[59,23739,23740],{"class":69},"USERNAME",[59,23742,23635],{"class":178},[59,23744,23745,23748,23750,23752],{"class":61,"line":3137},[59,23746,23747],{"class":174},"      pass",[59,23749,23613],{"class":178},[59,23751,23632],{"class":69},[59,23753,23635],{"class":178},[316,23755,23756],{},"html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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);}",{"title":55,"searchDepth":77,"depth":77,"links":23758},[23759],{"id":23530,"depth":77,"text":23531,"children":23760},[23761,23762],{"id":23534,"depth":88,"text":23535},{"id":23645,"depth":88,"text":23646},{},"install\u002Femail_providers.md",{"description":55},"docs\u002Finstall\u002Femail_providers","Wr7jJrg4ApMcuAmSvdGuWROCqeTVM-IODPOTv-D4IIY",{"id":23769,"title":23770,"body":23771,"description":23778,"extension":329,"layout":330,"meta":24857,"navGroup":330,"navOrder":330,"navTitle":23770,"navigation":187,"originalPath":24858,"path":20008,"redirect":330,"seo":24859,"stem":24860,"updated":337,"version":338,"__hash__":24861},"docs\u002Fdocs\u002Finstall\u002Ffile-storage\u002Findex.md","FlowFuse File Storage",{"type":7,"value":23772,"toc":24844},[23773,23776,23779,23797,23800,23804,23807,23888,23891,23894,23900,23903,23907,23927,23930,23976,23982,23986,23996,24036,24044,24047,24050,24058,24063,24066,24078,24081,24087,24114,24117,24138,24141,24187,24190,24193,24196,24199,24202,24244,24250,24298,24302,24305,24397,24409,24412,24502,24506,24509,24512,24515,24517,24523,24526,24529,24532,24535,24538,24592,24594,24649,24653,24743,24745,24832,24836,24842],[10,23774,23770],{"id":23775},"flowfuse-file-storage",[14,23777,23778],{},"FlowFuse has two storage-related features for Node-RED instances in container-based deployments. They are not mutually exclusive, and some deployments use both.",[28,23780,23781,23791],{},[31,23782,23783,23786,23787,23790],{},[364,23784,23785],{},"Persistent Storage"," mounts a persistent volume into the Node-RED instance at ",[18,23788,23789],{},"\u002Fdata\u002Fstorage",". This was introduced in FlowFuse v2.6.0 for Kubernetes.",[31,23792,23793,23796],{},[364,23794,23795],{},"The File Storage service"," provides the FlowFuse File nodes and Persistent Context.",[14,23798,23799],{},"The rest of this page is structured around those two features so it is clearer which configuration you need.",[23,23801,23803],{"id":23802},"which-configuration-do-i-need","Which configuration do I need?",[14,23805,23806],{},"Use the following guide when choosing what to configure:",[2289,23808,23809,23821],{},[2292,23810,23811],{},[2295,23812,23813,23815,23818],{},[2298,23814,10208],{},[2298,23816,23817],{},"FlowFuse version",[2298,23819,23820],{},"What to configure",[2305,23822,23823,23838,23850,23875],{},[2295,23824,23825,23828,23831],{},[2310,23826,23827],{},"Files written directly by the Node-RED instance must survive restart, suspend\u002Fresume, or stack upgrades",[2310,23829,23830],{},"v2.6.0 or later",[2310,23832,23833,23834],{},"Configure ",[41,23835,23837],{"href":23836},"#persistent-storage-for-node-red-instances","Persistent Storage for Node-RED instances",[2295,23839,23840,23842,23845],{},[2310,23841],{},[2310,23843,23844],{},"Before v2.6.0",[2310,23846,23833,23847],{},[41,23848,23795],{"href":23849},"#the-file-storage-service",[2295,23851,23852,23860,23862],{},[2310,23853,23854,23855],{},"You want to use ",[41,23856,23859],{"href":23857,"rel":23858},"https:\u002F\u002Fflowfuse.com\u002Fdocs\u002Fuser\u002Fpersistent-context\u002F",[831],"FlowFuse Persistent Context",[2310,23861,23830],{},[2310,23863,23833,23864,23866,23867,23871,23872,23874],{},[41,23865,23795],{"href":23849}," and the ",[41,23868,23870],{"href":23869},"#persistent-context-configuration","Persistent Context configuration",". If the instance also needs persistent filesystem access, configure ",[41,23873,23837],{"href":23836}," as well.",[2295,23876,23877,23879,23881],{},[2310,23878],{},[2310,23880,23844],{},[2310,23882,23833,23883,23866,23885,23887],{},[41,23884,23795],{"href":23849},[41,23886,23870],{"href":23869},". The File Storage service also provides persistent storage for the instance.",[23,23889,23837],{"id":23890},"persistent-storage-for-node-red-instances",[14,23892,23893],{},"As part of the FlowFuse v2.6.0 release, a new Persistent Storage approach was implemented for Kubernetes. Docker support will follow.",[14,23895,23896,23897,23899],{},"This mounts a Persistent Volume into the container running the Node-RED instance on ",[18,23898,23789],{},".\nFiles written to this location are preserved for the life of the instance and across suspend\u002Fresume operations and stack upgrades.",[14,23901,23902],{},"If you are using FlowFuse version lower than v2.6.0, you will need to use the File Storage service to provide persistent storage to Node-RED or upgrade to v2.6.0 or later to use this feature.",[104,23904,23906],{"id":23905},"configuring","Configuring",[14,23908,23909,23910,23913,23914,23917,23918,23921,23922,273],{},"Create a Kubernetes ",[18,23911,23912],{},"StorageClass"," that allows dynamic provisioning of ",[18,23915,23916],{},"PersistentVolumes"," from ",[18,23919,23920],{},"PersistentVolumeClaims",", for example the ",[41,23923,23926],{"href":23924,"rel":23925},"https:\u002F\u002Fgithub.com\u002Fkubernetes-sigs\u002Faws-efs-csi-driver",[831],"AWS EFS CSI driver",[14,23928,23929],{},"Then pass the following values to the FlowFuse Helm Chart when upgrading.",[50,23931,23933],{"className":165,"code":23932,"language":167,"meta":55,"style":55},"forge:\n  persistentStorage:\n    enabled: true\n    storageClass: '\u003Cname of StorageClass>'\n    size: '5Gi'\n",[18,23934,23935,23941,23948,23956,23966],{"__ignoreMap":55},[59,23936,23937,23939],{"class":61,"line":62},[59,23938,22305],{"class":174},[59,23940,196],{"class":178},[59,23942,23943,23946],{"class":61,"line":77},[59,23944,23945],{"class":174},"  persistentStorage",[59,23947,196],{"class":178},[59,23949,23950,23952,23954],{"class":61,"line":88},[59,23951,16930],{"class":174},[59,23953,179],{"class":178},[59,23955,3230],{"class":73},[59,23957,23958,23961,23963],{"class":61,"line":99},[59,23959,23960],{"class":174},"    storageClass",[59,23962,179],{"class":178},[59,23964,23965],{"class":69},"'\u003Cname of StorageClass>'\n",[59,23967,23968,23971,23973],{"class":61,"line":156},[59,23969,23970],{"class":174},"    size",[59,23972,179],{"class":178},[59,23974,23975],{"class":69},"'5Gi'\n",[14,23977,7876,23978,23981],{},[18,23979,23980],{},"size"," is the default size for the volume.",[768,23983,23985],{"id":23984},"azure","Azure",[14,23987,23988,23989,23992,23993,3939],{},"If you are using the ",[18,23990,23991],{},"azurefile-csi"," Persistent Storage driver then we recommend adding the following to the ",[18,23994,23995],{},"StorageClass mountOptions",[50,23997,23999],{"className":165,"code":23998,"language":167,"meta":55,"style":55},"mountOptions:\n  - dir_mode=0777\n  - file_mode=0777\n  - mfsymlinks\n  - nobrl\n",[18,24000,24001,24008,24015,24022,24029],{"__ignoreMap":55},[59,24002,24003,24006],{"class":61,"line":62},[59,24004,24005],{"class":174},"mountOptions",[59,24007,196],{"class":178},[59,24009,24010,24012],{"class":61,"line":77},[59,24011,14050],{"class":178},[59,24013,24014],{"class":69},"dir_mode=0777\n",[59,24016,24017,24019],{"class":61,"line":88},[59,24018,14050],{"class":178},[59,24020,24021],{"class":69},"file_mode=0777\n",[59,24023,24024,24026],{"class":61,"line":99},[59,24025,14050],{"class":178},[59,24027,24028],{"class":69},"mfsymlinks\n",[59,24030,24031,24033],{"class":61,"line":156},[59,24032,14050],{"class":178},[59,24034,24035],{"class":69},"nobrl\n",[14,24037,4611,24038,24043],{},[41,24039,24042],{"href":24040,"rel":24041},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Ftroubleshoot\u002Fazure\u002Fazure-kubernetes\u002Fstorage\u002Fmountoptions-settings-azure-files",[831],"the Azure Kubernetes documentation"," for more details.",[23,24045,23795],{"id":24046},"the-file-storage-service",[14,24048,24049],{},"The FlowFuse Platform includes a File Storage service that provides:",[28,24051,24052,24055],{},[31,24053,24054],{},"A set of custom File nodes that behave the same way as the standard Node-RED\nFile nodes",[31,24056,24057],{},"An optional Persistent Context store for storing context data within flows.\nThis feature is only available for platforms running with a premium license.",[14,24059,24060,24061,273],{},"On FlowFuse v2.6.0 or later, the File Storage service is used exclusively for Persistent Context. Instance-level Persistent Storage is handled separately via ",[41,24062,23837],{"href":23836},[14,24064,24065],{},"Before FlowFuse v2.6.0, this service was also the primary way to provide persistent storage to Node-RED in container-based environments, in addition to Persistent Context.",[17958,24067,24069,24072],{"className":24068},[21721,21722],[14,24070,1760],{"className":24071},[21726],[17958,24073,24075],{"className":24074},[21730],[14,24076,24077],{},"The File Storage service is only required in Docker or Kubernetes environments. If you are using the LocalFS platform driver, Node-RED already has direct access to the local filesystem.",[104,24079,23906],{"id":24080},"configuring-1",[14,24082,24083,24084,273],{},"The File Storage server has its own configuration file: ",[18,24085,24086],{},"etc\u002Fflowforge-storage.yml",[28,24088,24089,24094],{},[31,24090,24091,24093],{},[364,24092,162],{}," - edit the file directly before starting the service",[31,24095,24096,24099,24100,24103,24104,24107,24108,470,24111,24113],{},[364,24097,24098],{},"Kubernetes\u002FHelm"," - include the options in your ",[18,24101,24102],{},"customization.yml",", using ",[18,24105,24106],{},"forge.fileStore.*"," as the property name prefix.\nYou must also set ",[18,24109,24110],{},"forge.fileStore.enabled",[18,24112,3558],{}," to tell Helm to deploy the service.",[14,24115,24116],{},"There are three parts to the configuration:",[28,24118,24119,24126,24133],{},[31,24120,24121,24125],{},[41,24122,24124],{"href":24123},"#platform-configuration","Platform Configuration"," - how to access the main FlowFuse platform application",[31,24127,24128,24132],{},[41,24129,24131],{"href":24130},"#file-storage-service-configuration","File Storage service configuration"," - what storage to use for the File nodes",[31,24134,24135,24137],{},[41,24136,23870],{"href":23869}," - what storage to use for Persistent Context",[104,24139,24124],{"id":24140},"platform-configuration",[2289,24142,24143,24151],{},[2292,24144,24145],{},[2295,24146,24147,24149],{},[2298,24148,3541],{},[2298,24150,3348],{},[2305,24152,24153,24164,24176],{},[2295,24154,24155,24159],{},[2310,24156,24157],{},[18,24158,9436],{},[2310,24160,24161,24162,273],{},"Where to listen for incoming connections. Default: ",[18,24163,9440],{},[2295,24165,24166,24170],{},[2310,24167,24168],{},[18,24169,18638],{},[2310,24171,24172,24173],{},"The port to listen on. Default: ",[18,24174,24175],{},"3001",[2295,24177,24178,24182],{},[2310,24179,24180],{},[18,24181,9444],{},[2310,24183,24184,24185],{},"The url to access the FlowFuse platform on. This defaults to ",[18,24186,2518],{},[104,24188,24131],{"id":24189},"file-storage-service-configuration",[14,24191,24192],{},"The File Storage configuration determines where the files used by the Node-RED File nodes are stored.",[14,24194,24195],{},"You can configure it to store files either on the local filesystem of the File Storage server or in an AWS S3-compatible service.",[768,24197,24198],{"id":6273},"LocalFS",[14,24200,24201],{},"Stores the files locally, for example by using a volume mounted into the File Storage server container.",[2289,24203,24204,24212],{},[2292,24205,24206],{},[2295,24207,24208,24210],{},[2298,24209,3541],{},[2298,24211,3348],{},[2305,24213,24214,24224,24234],{},[2295,24215,24216,24220],{},[2310,24217,24218],{},[18,24219,18936],{},[2310,24221,24222],{},[18,24223,6273],{},[2295,24225,24226,24231],{},[2310,24227,24228],{},[18,24229,24230],{},"driver.quota",[2310,24232,24233],{},"A per-instance quota for how much data will be stored - in bytes. If this is not set, no limit will be applied",[2295,24235,24236,24241],{},[2310,24237,24238],{},[18,24239,24240],{},"driver.options.root",[2310,24242,24243],{},"The root path under which Node-RED instance data should be stored.",[14,24245,24246,24247,24249],{},"The following shows an example configuration using the ",[18,24248,6273],{}," file driver.",[50,24251,24253],{"className":165,"code":24252,"language":167,"meta":55,"style":55},"driver:\n  type: localfs\n  quota: 104857600\n  options:\n    root: var\u002Froot\n",[18,24254,24255,24262,24272,24282,24289],{"__ignoreMap":55},[59,24256,24257,24260],{"class":61,"line":62},[59,24258,24259],{"class":174},"driver",[59,24261,196],{"class":178},[59,24263,24264,24267,24269],{"class":61,"line":77},[59,24265,24266],{"class":174},"  type",[59,24268,179],{"class":178},[59,24270,24271],{"class":69},"localfs\n",[59,24273,24274,24277,24279],{"class":61,"line":88},[59,24275,24276],{"class":174},"  quota",[59,24278,179],{"class":178},[59,24280,24281],{"class":73},"104857600\n",[59,24283,24284,24287],{"class":61,"line":99},[59,24285,24286],{"class":174},"  options",[59,24288,196],{"class":178},[59,24290,24291,24293,24295],{"class":61,"line":156},[59,24292,16860],{"class":174},[59,24294,179],{"class":178},[59,24296,24297],{"class":69},"var\u002Froot\n",[768,24299,24301],{"id":24300},"s3-compatible-storage","S3 Compatible Storage",[14,24303,24304],{},"Stores the files in an external service using the AWS S3 API.",[2289,24306,24307,24315],{},[2292,24308,24309],{},[2295,24310,24311,24313],{},[2298,24312,3541],{},[2298,24314,3348],{},[2305,24316,24317,24328,24336,24346,24356,24366,24377,24387],{},[2295,24318,24319,24323],{},[2310,24320,24321],{},[18,24322,18936],{},[2310,24324,24325],{},[18,24326,24327],{},"s3",[2295,24329,24330,24334],{},[2310,24331,24332],{},[18,24333,24230],{},[2310,24335,24233],{},[2295,24337,24338,24343],{},[2310,24339,24340],{},[18,24341,24342],{},"driver.options.bucket",[2310,24344,24345],{},"Name of the S3 bucket to use (required)",[2295,24347,24348,24353],{},[2310,24349,24350],{},[18,24351,24352],{},"driver.options.region",[2310,24354,24355],{},"Name of AWS region of the bucket (required)",[2295,24357,24358,24363],{},[2310,24359,24360],{},[18,24361,24362],{},"driver.options.endpoint",[2310,24364,24365],{},"S3 ObjectStore Endpoint, if not using AWS S3",[2295,24367,24368,24373],{},[2310,24369,24370],{},[18,24371,24372],{},"driver.options.forcePathStyle",[2310,24374,24375],{},[18,24376,3558],{},[2295,24378,24379,24384],{},[2310,24380,24381],{},[18,24382,24383],{},"driver.options.credential.accessKeyId",[2310,24385,24386],{},"Account ID \u002F Username",[2295,24388,24389,24394],{},[2310,24390,24391],{},[18,24392,24393],{},"driver.options.credential.secretAccessKey",[2310,24395,24396],{},"Secret Key \u002F Password",[14,24398,24399,24400,24403,24404,273],{},"The full list of valid options under ",[18,24401,24402],{},"driver.options"," is available in the ",[41,24405,24408],{"href":24406,"rel":24407},"https:\u002F\u002Fdocs.aws.amazon.com\u002FAWSJavaScriptSDK\u002Fv3\u002Flatest\u002Fclients\u002Fclient-s3\u002Finterfaces\u002Fs3clientconfig.html",[831],"AWS S3Client documentation",[14,24410,24411],{},"For example:",[50,24413,24415],{"className":165,"code":24414,"language":167,"meta":55,"style":55},"driver:\n  type: s3\n  quota: 104857600\n  options:\n    bucket: flowforge-files\n    credentials:\n      accessKeyId: XXXXXXXXXXXXXXXXXXX\n      secretAccessKey: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n    forcePathStyle: true\n    region: us-east-1\n",[18,24416,24417,24423,24432,24440,24446,24456,24463,24473,24483,24492],{"__ignoreMap":55},[59,24418,24419,24421],{"class":61,"line":62},[59,24420,24259],{"class":174},[59,24422,196],{"class":178},[59,24424,24425,24427,24429],{"class":61,"line":77},[59,24426,24266],{"class":174},[59,24428,179],{"class":178},[59,24430,24431],{"class":69},"s3\n",[59,24433,24434,24436,24438],{"class":61,"line":88},[59,24435,24276],{"class":174},[59,24437,179],{"class":178},[59,24439,24281],{"class":73},[59,24441,24442,24444],{"class":61,"line":99},[59,24443,24286],{"class":174},[59,24445,196],{"class":178},[59,24447,24448,24451,24453],{"class":61,"line":156},[59,24449,24450],{"class":174},"    bucket",[59,24452,179],{"class":178},[59,24454,24455],{"class":69},"flowforge-files\n",[59,24457,24458,24461],{"class":61,"line":216},[59,24459,24460],{"class":174},"    credentials",[59,24462,196],{"class":178},[59,24464,24465,24468,24470],{"class":61,"line":224},[59,24466,24467],{"class":174},"      accessKeyId",[59,24469,179],{"class":178},[59,24471,24472],{"class":69},"XXXXXXXXXXXXXXXXXXX\n",[59,24474,24475,24478,24480],{"class":61,"line":233},[59,24476,24477],{"class":174},"      secretAccessKey",[59,24479,179],{"class":178},[59,24481,24482],{"class":69},"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n",[59,24484,24485,24488,24490],{"class":61,"line":241},[59,24486,24487],{"class":174},"    forcePathStyle",[59,24489,179],{"class":178},[59,24491,3230],{"class":73},[59,24493,24494,24497,24499],{"class":61,"line":249},[59,24495,24496],{"class":174},"    region",[59,24498,179],{"class":178},[59,24500,24501],{"class":69},"us-east-1\n",[768,24503,24505],{"id":24504},"enabling-the-flowfuse-file-nodes","Enabling the FlowFuse File Nodes",[14,24507,24508],{},"The FlowFuse File nodes have been written to be direct replacements\nfor the Node-RED core file-in and file-out nodes. This means that only\none version of these nodes can be active in Node-RED at a time.",[14,24510,24511],{},"The FlowFuse File nodes will automatically disable themselves if the\ncore nodes are present. This means to enable the nodes you need to\nexclude the code nodes.",[14,24513,24514],{},"This can be done in the FlowFuse Template.",[638,24516],{"src":22682,"width":15952},[14,24518,24519,24520,24522],{},"Adding ",[18,24521,20052],{}," to the list of \"Excluded nodes by filename\" section will ensure that the core file nodes are not loaded by Node-RED.",[104,24524,23870],{"id":24525},"persistent-context-configuration",[14,24527,24528],{},"The Context Storage configuration determines where Node-RED Context data is stored.",[14,24530,24531],{},"This feature is only available when running with a FlowFuse Premium license.",[14,24533,24534],{},"Due to the different access patterns for context data, this requires a separate\nstorage configuration to the File store. It can use either an SQLite or PostgreSQL\ndatabase.",[768,24536,24537],{"id":18777},"SQLite",[2289,24539,24540,24548],{},[2292,24541,24542],{},[2295,24543,24544,24546],{},[2298,24545,3541],{},[2298,24547,3348],{},[2305,24549,24550,24562,24571,24582],{},[2295,24551,24552,24557],{},[2310,24553,24554],{},[18,24555,24556],{},"context.type",[2310,24558,24559],{},[18,24560,24561],{},"sequelize",[2295,24563,24564,24569],{},[2310,24565,24566],{},[18,24567,24568],{},"context.quote",[2310,24570,24233],{},[2295,24572,24573,24578],{},[2310,24574,24575],{},[18,24576,24577],{},"context.options.type",[2310,24579,24580],{},[18,24581,18777],{},[2295,24583,24584,24589],{},[2310,24585,24586],{},[18,24587,24588],{},"context.options.storage",[2310,24590,24591],{},"Path to the sqlite database file to use",[14,24593,24411],{},[50,24595,24597],{"className":165,"code":24596,"language":167,"meta":55,"style":55},"context:\n  type: sequelize\n  quota: 1048576\n  options:\n    type: sqlite\n    storage: ff-context.db\n",[18,24598,24599,24605,24614,24623,24629,24639],{"__ignoreMap":55},[59,24600,24601,24603],{"class":61,"line":62},[59,24602,6941],{"class":174},[59,24604,196],{"class":178},[59,24606,24607,24609,24611],{"class":61,"line":77},[59,24608,24266],{"class":174},[59,24610,179],{"class":178},[59,24612,24613],{"class":69},"sequelize\n",[59,24615,24616,24618,24620],{"class":61,"line":88},[59,24617,24276],{"class":174},[59,24619,179],{"class":178},[59,24621,24622],{"class":73},"1048576\n",[59,24624,24625,24627],{"class":61,"line":99},[59,24626,24286],{"class":174},[59,24628,196],{"class":178},[59,24630,24631,24634,24636],{"class":61,"line":156},[59,24632,24633],{"class":174},"    type",[59,24635,179],{"class":178},[59,24637,24638],{"class":69},"sqlite\n",[59,24640,24641,24644,24646],{"class":61,"line":216},[59,24642,24643],{"class":174},"    storage",[59,24645,179],{"class":178},[59,24647,24648],{"class":69},"ff-context.db\n",[768,24650,24652],{"id":24651},"postgresql","PostgreSQL",[2289,24654,24655,24663],{},[2292,24656,24657],{},[2295,24658,24659,24661],{},[2298,24660,3541],{},[2298,24662,3348],{},[2305,24664,24665,24675,24683,24693,24703,24713,24723,24733],{},[2295,24666,24667,24671],{},[2310,24668,24669],{},[18,24670,24556],{},[2310,24672,24673],{},[18,24674,24561],{},[2295,24676,24677,24681],{},[2310,24678,24679],{},[18,24680,24568],{},[2310,24682,24233],{},[2295,24684,24685,24689],{},[2310,24686,24687],{},[18,24688,24577],{},[2310,24690,24691],{},[18,24692,18780],{},[2295,24694,24695,24700],{},[2310,24696,24697],{},[18,24698,24699],{},"context.options.host",[2310,24701,24702],{},"The hostname of the database server",[2295,24704,24705,24710],{},[2310,24706,24707],{},[18,24708,24709],{},"context.options.port",[2310,24711,24712],{},"The port of the database server",[2295,24714,24715,24720],{},[2310,24716,24717],{},[18,24718,24719],{},"context.options.database",[2310,24721,24722],{},"The name of the database to store context data in",[2295,24724,24725,24730],{},[2310,24726,24727],{},[18,24728,24729],{},"context.options.username",[2310,24731,24732],{},"The username to access to the database with",[2295,24734,24735,24740],{},[2310,24736,24737],{},[18,24738,24739],{},"context.options.password",[2310,24741,24742],{},"The password to access to the database with",[14,24744,24411],{},[50,24746,24748],{"className":165,"code":24747,"language":167,"meta":55,"style":55},"context:\n  type: sequelize\n  quota: 1048576\n  options:\n    type: postgres\n    host: flowforge-postgresql\n    port: 5432\n    database: ff-context\n    username: postgres\n    password: password\n",[18,24749,24750,24756,24764,24772,24778,24786,24795,24803,24813,24822],{"__ignoreMap":55},[59,24751,24752,24754],{"class":61,"line":62},[59,24753,6941],{"class":174},[59,24755,196],{"class":178},[59,24757,24758,24760,24762],{"class":61,"line":77},[59,24759,24266],{"class":174},[59,24761,179],{"class":178},[59,24763,24613],{"class":69},[59,24765,24766,24768,24770],{"class":61,"line":88},[59,24767,24276],{"class":174},[59,24769,179],{"class":178},[59,24771,24622],{"class":73},[59,24773,24774,24776],{"class":61,"line":99},[59,24775,24286],{"class":174},[59,24777,196],{"class":178},[59,24779,24780,24782,24784],{"class":61,"line":156},[59,24781,24633],{"class":174},[59,24783,179],{"class":178},[59,24785,8523],{"class":69},[59,24787,24788,24790,24792],{"class":61,"line":216},[59,24789,23575],{"class":174},[59,24791,179],{"class":178},[59,24793,24794],{"class":69},"flowforge-postgresql\n",[59,24796,24797,24799,24801],{"class":61,"line":224},[59,24798,14064],{"class":174},[59,24800,179],{"class":178},[59,24802,8511],{"class":73},[59,24804,24805,24808,24810],{"class":61,"line":233},[59,24806,24807],{"class":174},"    database",[59,24809,179],{"class":178},[59,24811,24812],{"class":69},"ff-context\n",[59,24814,24815,24818,24820],{"class":61,"line":241},[59,24816,24817],{"class":174},"    username",[59,24819,179],{"class":178},[59,24821,8523],{"class":69},[59,24823,24824,24827,24829],{"class":61,"line":249},[59,24825,24826],{"class":174},"    password",[59,24828,179],{"class":178},[59,24830,24831],{"class":69},"password\n",[23,24833,24835],{"id":24834},"working-with-flowfuse-devices","Working with FlowFuse Devices",[14,24837,24838,24839,24841],{},"The FlowFuse Device Agent does ",[1160,24840,1230],{}," use the File Storage service. Any flows\nusing the file nodes or persistent context that are deployed to a Device will use\nthe local filesystem directly.",[316,24843,23756],{},{"title":55,"searchDepth":77,"depth":77,"links":24845},[24846,24847,24850,24856],{"id":23802,"depth":77,"text":23803},{"id":23890,"depth":77,"text":23837,"children":24848},[24849],{"id":23905,"depth":88,"text":23906},{"id":24046,"depth":77,"text":23795,"children":24851},[24852,24853,24854,24855],{"id":24080,"depth":88,"text":23906},{"id":24140,"depth":88,"text":24124},{"id":24189,"depth":88,"text":24131},{"id":24525,"depth":88,"text":23870},{"id":24834,"depth":77,"text":24835},{},"install\u002Ffile-storage\u002FREADME.md",{"title":23770,"description":23778},"docs\u002Finstall\u002Ffile-storage\u002Findex","BENFDPxZMrFaGjrbWnLmmSp0aOazwvD6qle7IlFM9rg",{"id":24863,"title":24864,"body":24865,"description":24871,"extension":329,"layout":330,"meta":24965,"navGroup":330,"navOrder":330,"navTitle":584,"navigation":187,"originalPath":24966,"path":21164,"redirect":330,"seo":24967,"stem":24968,"updated":337,"version":338,"__hash__":24969},"docs\u002Fdocs\u002Finstall\u002Ffirst-run.md","First-run Setup",{"type":7,"value":24866,"toc":24956},[24867,24869,24872,24876,24879,24884,24888,24897,24903,24906,24910,24913,24926,24929,24933,24936,24939,24942,24946,24949],[10,24868,24864],{"id":9513},[14,24870,24871],{},"Following a successful install, you will be able to access the platform to go\nthrough the initial setup.",[23,24873,24875],{"id":24874},"_1-start-setup","1. Start setup",[104,24877,24878],{"id":6273},"- LocalFS",[14,24880,24881,24882],{},"Open FlowFuse in your browser ",[18,24883,2518],{},[104,24885,24887],{"id":24886},"docker-or-kubernetes","- Docker or Kubernetes",[14,24889,24881,24890,24892,24893,24896],{},[18,24891,21536],{}," (Change ",[18,24894,24895],{},".example.com"," to match the domain you set up in DNS)",[14,24898,15930,24899,24902],{},[364,24900,24901],{},"START SETUP"," button",[638,24904],{"src":24905,"width":15952},"\u002Fdocs\u002Finstall\u002Fimages\u002Fsetup-01.png",[23,24907,24909],{"id":24908},"_2-create-an-administrator","2. Create an Administrator",[14,24911,24912],{},"The first user you create will be an Administrator. They will have full access\nto the platform, be able to set platform-wide configuration and manage users and teams.",[14,24914,24915,24917,24918,24921,24922,24925],{},[364,24916,1760],{},": with the 0.3 release, it is possible to reset your password ",[1160,24919,24920],{},"if"," you have email\nconfigured and enabled the ",[18,24923,24924],{},"user:reset-password"," option in Admin settings. Otherwise,\nmake sure you make a note of the password you set. We will provide tools to manage passwords\noutside of the platform in a future release.",[638,24927],{"src":24928,"width":15952},"\u002Fdocs\u002Finstall\u002Fimages\u002Fsetup-02-user.png",[23,24930,24932],{"id":24931},"_3-upload-a-license","3. Upload a license",[14,24934,24935],{},"FlowFuse Community Edition is Open Source and can be used freely without a license.",[14,24937,24938],{},"If you have a FlowFuse Enterprise Edition license you can upload it here.",[638,24940],{"src":24941,"width":15952},"\u002Fdocs\u002Finstall\u002Fimages\u002Fsetup-03-license.png",[23,24943,24945],{"id":24944},"_4-finish-setup","4. Finish setup",[14,24947,24948],{},"Once you complete the setup, you will be able to log in as the Administrator\nuser you created and start using the platform. You can setup your Team and\ncreate your first Node-RED instance.",[14,24950,24951,24952,273],{},"More information about using the FlowFuse platform is available in the\nmain ",[41,24953,24955],{"href":24954},"\u002Fdocs\u002Fuser\u002Fintroduction.md","user guide",{"title":55,"searchDepth":77,"depth":77,"links":24957},[24958,24962,24963,24964],{"id":24874,"depth":77,"text":24875,"children":24959},[24960,24961],{"id":6273,"depth":88,"text":24878},{"id":24886,"depth":88,"text":24887},{"id":24908,"depth":77,"text":24909},{"id":24931,"depth":77,"text":24932},{"id":24944,"depth":77,"text":24945},{},"install\u002Ffirst-run.md",{"title":24864,"description":24871},"docs\u002Finstall\u002Ffirst-run","WLFsaT71JmJwB7XcR9NQMNxobhm5YZd1ST2uQkulayw",{"id":24971,"title":55,"body":24972,"description":55,"extension":329,"layout":532,"meta":24976,"navGroup":534,"navOrder":77,"navTitle":9164,"navigation":187,"originalPath":24977,"path":566,"redirect":24978,"seo":24980,"stem":24981,"updated":337,"version":338,"__hash__":24982},"docs\u002Fdocs\u002Finstall\u002Findex.md",{"type":7,"value":24973,"toc":24974},[],{"title":55,"searchDepth":77,"depth":77,"links":24975},[],{},"install\u002FREADME.md",{"to":24979},"\u002Fdocs\u002Finstall\u002Fintroduction",{"description":55},"docs\u002Finstall\u002Findex","q4GjTwfC3nqBoEhj9JTamymeS7cYaQX1y2kYgOQWjGI",{"id":24984,"title":9164,"body":24985,"description":24991,"extension":329,"layout":330,"meta":25064,"navGroup":330,"navOrder":62,"navTitle":15465,"navigation":187,"originalPath":25068,"path":24979,"redirect":330,"seo":25069,"stem":25070,"updated":337,"version":338,"__hash__":25071},"docs\u002Fdocs\u002Finstall\u002Fintroduction.md",{"type":7,"value":24986,"toc":25060},[24987,24989,24992,25024,25027,25041,25045,25053,25057],[10,24988,9164],{"id":9163},[14,24990,24991],{},"FlowFuse can be installed to run in Docker or Kubernetes based environments.",[28,24993,24994,25011],{},[31,24995,24996,24999],{},[364,24997,24998],{},"Docker:",[28,25000,25001,25005],{},[31,25002,25003],{},[41,25004,15506],{"href":21365},[31,25006,25007],{},[41,25008,25010],{"href":25009},"\u002Fdocs\u002Finstall\u002Fdocker\u002FREADME.md","Full Install",[31,25012,25013,25016],{},[364,25014,25015],{},"Kubernetes:",[28,25017,25018],{},[31,25019,25020],{},[41,25021,25023],{"href":25022},"\u002Fdocs\u002Finstall\u002Fkubernetes\u002FREADME.md","Install Guide",[14,25025,25026],{},"We also provide one-click installs of the Docker version:",[28,25028,25029,25035],{},[31,25030,25031],{},[41,25032,25034],{"href":25033},"\u002Fdocs\u002Finstall\u002Fdocker\u002Fdigital-ocean.md","Digital Ocean Docker Install Guide",[31,25036,25037],{},[41,25038,25040],{"href":25039},"\u002Fdocs\u002Finstall\u002Fdocker\u002Faws-marketplace.md","AWS Docker Install Guide",[23,25042,25044],{"id":25043},"upgrading-flowfuse","Upgrading FlowFuse",[14,25046,25047,25048,25052],{},"If you are upgrading FlowFuse, please refer to the ",[41,25049,25051],{"href":25050},"\u002Fdocs\u002Fupgrade\u002FREADME.md","Upgrade Guide","\nfor any specific actions required.",[23,25054,25056],{"id":25055},"do-you-need-help-installation-service","Do You Need Help? Installation Service",[14,25058,25059],{},"If you need assistance, request our complimentary Installation Service, and we will help you install FlowFuse.",{"title":55,"searchDepth":77,"depth":77,"links":25061},[25062,25063],{"id":25043,"depth":77,"text":25044},{"id":25055,"depth":77,"text":25056},{"installationServiceHubspot":25065},{"formId":25066,"targetId":25067},"22edc659-d098-4767-aeb1-6480daae41ad","hs-form-installation-service","install\u002Fintroduction.md",{"title":9164,"description":24991},"docs\u002Finstall\u002Fintroduction","xkUVFGK4J7THOS7awlBEOAGUd4S1QqRm7HvCLTWaHIA",{"id":25073,"title":25074,"body":25075,"description":25082,"extension":329,"layout":330,"meta":26938,"navGroup":330,"navOrder":330,"navTitle":26939,"navigation":187,"originalPath":26940,"path":26941,"redirect":330,"seo":26942,"stem":26943,"updated":337,"version":338,"__hash__":26944},"docs\u002Fdocs\u002Finstall\u002Fkubernetes\u002Faws.md","AWS EKS Specific details",{"type":7,"value":25076,"toc":26918},[25077,25080,25083,25086,25097,25099,25103,25106,25112,25115,25118,25121,25124,25130,25134,25137,25141,25148,25151,25155,25166,25185,25194,25495,25499,25503,25511,25518,25531,25541,26277,26280,26364,26368,26374,26376,26379,26383,26389,26392,26398,26506,26829,26835,26838,26845,26852,26856,26859,26862,26869,26872,26881,26884,26906,26909,26915],[10,25078,25074],{"id":25079},"aws-eks-specific-details",[14,25081,25082],{},"This document includes details of installing FlowFuse on AWS EKS.",[14,25084,25085],{},"The following assumptions have been made in the examples:",[398,25087,25088,25091],{},[31,25089,25090],{},"The user has the correct AWS IAM policy access to complete all tasks",[31,25092,25093,25094],{},"All AWS services are running in ",[18,25095,25096],{},"eu-west-1",[23,25098,26],{"id":25},[104,25100,25102],{"id":25101},"aws-cli","AWS Cli",[14,25104,25105],{},"This is used to interact with the whole AWS environment",[14,25107,25108],{},[41,25109,25110],{"href":25110,"rel":25111},"https:\u002F\u002Fdocs.aws.amazon.com\u002Fcli\u002Flatest\u002Fuserguide\u002Fgetting-started-install.html",[831],[14,25113,25114],{},"From here onwards this document assumes that you have configured the\nAWS CLI tools with a user that has permission to carry out the steps.",[14,25116,25117],{},"This document does not include details of how to configure such a user\nin AWS IAM. Please show this document to you AWS Account Admin if you\nneed help.",[104,25119,25120],{"id":25120},"eksctl",[14,25122,25123],{},"This tool is used to create\u002Fmodify AWS EKS Clusters, it uses the credentials from the AWS Cli.",[14,25125,25126],{},[41,25127,25128],{"href":25128,"rel":25129},"https:\u002F\u002Fdocs.aws.amazon.com\u002Feks\u002Flatest\u002Fuserguide\u002Feksctl.html",[831],[23,25131,25133],{"id":25132},"setup-a-new-domain-on-route53","Setup a new domain on Route53",[14,25135,25136],{},"Do this in the AWS Console\u002FYour DNS provider",[23,25138,25140],{"id":25139},"create-an-aws-certificate","Create an AWS Certificate",[14,25142,25143,25144,25147],{},"Request a certificate for ",[18,25145,25146],{},"*.[DOMAIN]"," from Amazon Certificate Manager",[14,25149,25150],{},"Do this in AWS Console, with Route53 validation",[23,25152,25154],{"id":25153},"create-eks-cluster","Create EKS Cluster",[14,25156,25157,25158,25161,25162,25165],{},"Edit the ",[18,25159,25160],{},"cluster.yml"," file in ",[18,25163,25164],{},"aws_eks"," to set your preferred instance type and count along with AWS Region",[50,25167,25169],{"className":52,"code":25168,"language":54,"meta":55,"style":55},"eksctl create cluster -f cluster.yml\n",[18,25170,25171],{"__ignoreMap":55},[59,25172,25173,25175,25177,25180,25182],{"class":61,"line":62},[59,25174,25120],{"class":65},[59,25176,13630],{"class":69},[59,25178,25179],{"class":69}," cluster",[59,25181,13111],{"class":73},[59,25183,25184],{"class":69}," cluster.yml\n",[14,25186,25187,25188,25193],{},"Example cluster.yml (Please visit ",[41,25189,25192],{"href":25190,"rel":25191},"https:\u002F\u002Feksctl.io\u002Fusage\u002Fcreating-and-managing-clusters\u002F#using-config-files",[831],"eksctl.io"," to be sure you understand what this does.)",[50,25195,25197],{"className":165,"code":25196,"language":167,"meta":55,"style":55},"apiVersion: eksctl.io\u002Fv1alpha5\nkind: ClusterConfig\n\nmetadata:\n  name: FlowFuse\n  region: eu-west-1\n\niam:\n  withOIDC: true\n\naddons:\n  - name: aws-ebs-csi-driver\n    resolveConflicts: overwrite\n\nnodeGroups:\n  - name: management\n    labels:\n      role: \"management\"\n    instanceType: t2.small\n    desiredCapacity: 1\n    volumeSize: 20\n    ssh:\n      allow: false\n    iam:\n      withAddonPolicies:\n        ebs: true\n  - name: instance\n    labels: \n      role: \"projects\"\n    tags:\n      k8s.io\u002Fcluster-autoscaler\u002Fenabled: \"true\"\n      k8s.io\u002Fcluster-autoscaler\u002Fflowforge: \"owned\"\n    instanceType: t2.small\n    desiredCapacity: 2\n    volumeSize:\n    ssh:\n      allow: false\n",[18,25198,25199,25208,25217,25221,25227,25236,25246,25250,25257,25266,25270,25277,25288,25298,25302,25309,25320,25327,25337,25347,25357,25367,25374,25383,25390,25397,25406,25417,25423,25432,25439,25449,25459,25467,25475,25481,25487],{"__ignoreMap":55},[59,25200,25201,25203,25205],{"class":61,"line":62},[59,25202,13655],{"class":174},[59,25204,179],{"class":178},[59,25206,25207],{"class":69},"eksctl.io\u002Fv1alpha5\n",[59,25209,25210,25212,25214],{"class":61,"line":77},[59,25211,13665],{"class":174},[59,25213,179],{"class":178},[59,25215,25216],{"class":69},"ClusterConfig\n",[59,25218,25219],{"class":61,"line":88},[59,25220,188],{"emptyLinePlaceholder":187},[59,25222,25223,25225],{"class":61,"line":99},[59,25224,13675],{"class":174},[59,25226,196],{"class":178},[59,25228,25229,25231,25233],{"class":61,"line":156},[59,25230,13682],{"class":174},[59,25232,179],{"class":178},[59,25234,25235],{"class":69},"FlowFuse\n",[59,25237,25238,25241,25243],{"class":61,"line":216},[59,25239,25240],{"class":174},"  region",[59,25242,179],{"class":178},[59,25244,25245],{"class":69},"eu-west-1\n",[59,25247,25248],{"class":61,"line":224},[59,25249,188],{"emptyLinePlaceholder":187},[59,25251,25252,25255],{"class":61,"line":233},[59,25253,25254],{"class":174},"iam",[59,25256,196],{"class":178},[59,25258,25259,25262,25264],{"class":61,"line":241},[59,25260,25261],{"class":174},"  withOIDC",[59,25263,179],{"class":178},[59,25265,3230],{"class":73},[59,25267,25268],{"class":61,"line":249},[59,25269,188],{"emptyLinePlaceholder":187},[59,25271,25272,25275],{"class":61,"line":257},[59,25273,25274],{"class":174},"addons",[59,25276,196],{"class":178},[59,25278,25279,25281,25283,25285],{"class":61,"line":3137},[59,25280,14050],{"class":178},[59,25282,5782],{"class":174},[59,25284,179],{"class":178},[59,25286,25287],{"class":69},"aws-ebs-csi-driver\n",[59,25289,25290,25293,25295],{"class":61,"line":3150},[59,25291,25292],{"class":174},"    resolveConflicts",[59,25294,179],{"class":178},[59,25296,25297],{"class":69},"overwrite\n",[59,25299,25300],{"class":61,"line":3163},[59,25301,188],{"emptyLinePlaceholder":187},[59,25303,25304,25307],{"class":61,"line":3176},[59,25305,25306],{"class":174},"nodeGroups",[59,25308,196],{"class":178},[59,25310,25311,25313,25315,25317],{"class":61,"line":3187},[59,25312,14050],{"class":178},[59,25314,5782],{"class":174},[59,25316,179],{"class":178},[59,25318,25319],{"class":69},"management\n",[59,25321,25322,25325],{"class":61,"line":3193},[59,25323,25324],{"class":174},"    labels",[59,25326,196],{"class":178},[59,25328,25329,25332,25334],{"class":61,"line":3201},[59,25330,25331],{"class":174},"      role",[59,25333,179],{"class":178},[59,25335,25336],{"class":69},"\"management\"\n",[59,25338,25339,25342,25344],{"class":61,"line":3214},[59,25340,25341],{"class":174},"    instanceType",[59,25343,179],{"class":178},[59,25345,25346],{"class":69},"t2.small\n",[59,25348,25349,25352,25354],{"class":61,"line":3222},[59,25350,25351],{"class":174},"    desiredCapacity",[59,25353,179],{"class":178},[59,25355,25356],{"class":73},"1\n",[59,25358,25359,25362,25364],{"class":61,"line":3233},[59,25360,25361],{"class":174},"    volumeSize",[59,25363,179],{"class":178},[59,25365,25366],{"class":73},"20\n",[59,25368,25369,25372],{"class":61,"line":3239},[59,25370,25371],{"class":174},"    ssh",[59,25373,196],{"class":178},[59,25375,25376,25379,25381],{"class":61,"line":3247},[59,25377,25378],{"class":174},"      allow",[59,25380,179],{"class":178},[59,25382,2962],{"class":73},[59,25384,25385,25388],{"class":61,"line":3256},[59,25386,25387],{"class":174},"    iam",[59,25389,196],{"class":178},[59,25391,25392,25395],{"class":61,"line":3261},[59,25393,25394],{"class":174},"      withAddonPolicies",[59,25396,196],{"class":178},[59,25398,25399,25402,25404],{"class":61,"line":3269},[59,25400,25401],{"class":174},"        ebs",[59,25403,179],{"class":178},[59,25405,3230],{"class":73},[59,25407,25408,25410,25412,25414],{"class":61,"line":3278},[59,25409,14050],{"class":178},[59,25411,5782],{"class":174},[59,25413,179],{"class":178},[59,25415,25416],{"class":69},"instance\n",[59,25418,25419,25421],{"class":61,"line":3284},[59,25420,25324],{"class":174},[59,25422,14289],{"class":178},[59,25424,25425,25427,25429],{"class":61,"line":3289},[59,25426,25331],{"class":174},[59,25428,179],{"class":178},[59,25430,25431],{"class":69},"\"projects\"\n",[59,25433,25434,25437],{"class":61,"line":3297},[59,25435,25436],{"class":174},"    tags",[59,25438,196],{"class":178},[59,25440,25441,25444,25446],{"class":61,"line":3310},[59,25442,25443],{"class":174},"      k8s.io\u002Fcluster-autoscaler\u002Fenabled",[59,25445,179],{"class":178},[59,25447,25448],{"class":69},"\"true\"\n",[59,25450,25451,25454,25456],{"class":61,"line":3321},[59,25452,25453],{"class":174},"      k8s.io\u002Fcluster-autoscaler\u002Fflowforge",[59,25455,179],{"class":178},[59,25457,25458],{"class":69},"\"owned\"\n",[59,25460,25461,25463,25465],{"class":61,"line":3327},[59,25462,25341],{"class":174},[59,25464,179],{"class":178},[59,25466,25346],{"class":69},[59,25468,25469,25471,25473],{"class":61,"line":3333},[59,25470,25351],{"class":174},[59,25472,179],{"class":178},[59,25474,3184],{"class":73},[59,25476,25477,25479],{"class":61,"line":6115},[59,25478,25361],{"class":174},[59,25480,196],{"class":178},[59,25482,25483,25485],{"class":61,"line":6152},[59,25484,25371],{"class":174},[59,25486,196],{"class":178},[59,25488,25489,25491,25493],{"class":61,"line":6158},[59,25490,25378],{"class":174},[59,25492,179],{"class":178},[59,25494,2962],{"class":73},[23,25496,25498],{"id":25497},"ingress-controller","Ingress Controller",[104,25500,25502],{"id":25501},"traefik","Traefik",[14,25504,25505,25506,25510],{},"It is recommended to run the ",[41,25507,25502],{"href":25508,"target":25509},"https:\u002F\u002Fdoc.traefik.io\u002Ftraefik\u002F","_blank"," even on AWS EKS (The AWS ALB load balancer currently appears to only support up to 100 Ingress Targets which limits the number of Hosted Instances that can be run).",[14,25512,25513,25514,25517],{},"Create a ",[18,25515,25516],{},"traefik-values.yaml"," file to pass the values to the Traefik helm file.",[50,25519,25521],{"className":52,"code":25520,"language":54,"meta":55,"style":55},"touch traefik-values.yaml\n",[18,25522,25523],{"__ignoreMap":55},[59,25524,25525,25528],{"class":61,"line":62},[59,25526,25527],{"class":65},"touch",[59,25529,25530],{"class":69}," traefik-values.yaml\n",[14,25532,25533,25534,25536,25537,25540],{},"Fill the ",[18,25535,25516],{}," file with the following content. Replace ",[18,25538,25539],{},"\u003Cyour-certificate-arn>"," with the certificate ARN created earlier",[50,25542,25544],{"className":165,"code":25543,"language":167,"meta":55,"style":55},"service:\n  enabled: true\n  type: LoadBalancer\n  annotations:\n    service.beta.kubernetes.io\u002Faws-load-balancer-type: \"nlb\"\n    service.beta.kubernetes.io\u002Faws-load-balancer-proxy-protocol: \"*\"\n    service.beta.kubernetes.io\u002Faws-load-balancer-ssl-cert: \"\u003Cyour-certificate-arn>\"\n    service.beta.kubernetes.io\u002Faws-load-balancer-ssl-ports: \"443\"\n    service.beta.kubernetes.io\u002Faws-load-balancer-backend-protocol: \"tcp\"\n    service.beta.kubernetes.io\u002Faws-load-balancer-connection-idle-timeout: \"120\"\n    service.beta.kubernetes.io\u002Faws-load-balancer-target-group-attributes: \"proxy_protocol_v2.enabled=true\"\n  spec:\n    externalTrafficPolicy: Cluster\n\ndeployment:\n  replicas: 2\n\nports:\n  web:\n    port: 8000\n    expose:\n      default: true\n    exposedPort: 80\n    protocol: TCP\n    forwardedHeaders:\n      trustedIPs:\n        - \"10.0.0.0\u002F8\"\n    proxyProtocol:\n      trustedIPs:\n        - \"10.0.0.0\u002F8\"\n  websecure:\n    port: 8443\n    expose:\n      default: true\n    exposedPort: 443\n    protocol: TCP\n    http:\n      middlewares:\n        - traefik-force-https@kubernetescrd\n        - traefik-large-body@kubernetescrd\n      # Disable TLS since NLB handles termination\n      tls:\n        enabled: false\n    forwardedHeaders:\n      trustedIPs:\n        - \"10.0.0.0\u002F8\"\n    proxyProtocol:\n      trustedIPs:\n        - \"10.0.0.0\u002F8\"\n\ningressClass:\n  enabled: true\n  isDefaultClass: false\n  name: traefik\n\nadditionalArguments:\n  - \"--entryPoints.web.proxyProtocol.insecure=true\"\n  - \"--entryPoints.websecure.proxyProtocol.insecure=true\"\n  - \"--entryPoints.web.forwardedHeaders.insecure=true\"\n  - \"--entryPoints.websecure.forwardedHeaders.insecure=true\"\n\nproviders:\n  kubernetesIngress:\n    enabled: true\n  kubernetesCRD:\n    enabled: true\n\napi:\n  dashboard: false\n  insecure: false\n\nlogs:\n  access:\n    enabled: true\n    fields:\n      headers:\n        defaultMode: keep\n\nextraObjects:\n  - apiVersion: traefik.io\u002Fv1alpha1\n    kind: Middleware\n    metadata:\n      name: force-https\n      namespace: traefik\n    spec:\n      headers:\n        customRequestHeaders:\n          X-Forwarded-Proto: \"https\"\n  - apiVersion: traefik.io\u002Fv1alpha1\n    kind: Middleware\n    metadata:\n      name: large-body\n      namespace: traefik\n    spec:\n      buffering:\n        maxRequestBodyBytes: 10485760\n",[18,25545,25546,25552,25560,25569,25576,25586,25596,25606,25616,25626,25636,25646,25653,25663,25667,25674,25682,25686,25693,25700,25709,25716,25725,25734,25743,25750,25757,25764,25771,25777,25783,25790,25799,25805,25813,25821,25829,25836,25843,25850,25857,25862,25869,25878,25884,25890,25896,25902,25908,25914,25918,25925,25933,25942,25951,25955,25962,25969,25976,25983,25990,25994,26000,26007,26015,26022,26030,26034,26041,26050,26059,26063,26070,26077,26085,26092,26099,26109,26114,26122,26133,26144,26151,26162,26172,26179,26186,26194,26205,26216,26225,26232,26242,26251,26258,26266],{"__ignoreMap":55},[59,25547,25548,25550],{"class":61,"line":62},[59,25549,9504],{"class":174},[59,25551,196],{"class":178},[59,25553,25554,25556,25558],{"class":61,"line":77},[59,25555,2957],{"class":174},[59,25557,179],{"class":178},[59,25559,3230],{"class":73},[59,25561,25562,25564,25566],{"class":61,"line":88},[59,25563,24266],{"class":174},[59,25565,179],{"class":178},[59,25567,25568],{"class":69},"LoadBalancer\n",[59,25570,25571,25574],{"class":61,"line":99},[59,25572,25573],{"class":174},"  annotations",[59,25575,196],{"class":178},[59,25577,25578,25581,25583],{"class":61,"line":156},[59,25579,25580],{"class":174},"    service.beta.kubernetes.io\u002Faws-load-balancer-type",[59,25582,179],{"class":178},[59,25584,25585],{"class":69},"\"nlb\"\n",[59,25587,25588,25591,25593],{"class":61,"line":216},[59,25589,25590],{"class":174},"    service.beta.kubernetes.io\u002Faws-load-balancer-proxy-protocol",[59,25592,179],{"class":178},[59,25594,25595],{"class":69},"\"*\"\n",[59,25597,25598,25601,25603],{"class":61,"line":224},[59,25599,25600],{"class":174},"    service.beta.kubernetes.io\u002Faws-load-balancer-ssl-cert",[59,25602,179],{"class":178},[59,25604,25605],{"class":69},"\"\u003Cyour-certificate-arn>\"\n",[59,25607,25608,25611,25613],{"class":61,"line":233},[59,25609,25610],{"class":174},"    service.beta.kubernetes.io\u002Faws-load-balancer-ssl-ports",[59,25612,179],{"class":178},[59,25614,25615],{"class":69},"\"443\"\n",[59,25617,25618,25621,25623],{"class":61,"line":241},[59,25619,25620],{"class":174},"    service.beta.kubernetes.io\u002Faws-load-balancer-backend-protocol",[59,25622,179],{"class":178},[59,25624,25625],{"class":69},"\"tcp\"\n",[59,25627,25628,25631,25633],{"class":61,"line":249},[59,25629,25630],{"class":174},"    service.beta.kubernetes.io\u002Faws-load-balancer-connection-idle-timeout",[59,25632,179],{"class":178},[59,25634,25635],{"class":69},"\"120\"\n",[59,25637,25638,25641,25643],{"class":61,"line":257},[59,25639,25640],{"class":174},"    service.beta.kubernetes.io\u002Faws-load-balancer-target-group-attributes",[59,25642,179],{"class":178},[59,25644,25645],{"class":69},"\"proxy_protocol_v2.enabled=true\"\n",[59,25647,25648,25651],{"class":61,"line":3137},[59,25649,25650],{"class":174},"  spec",[59,25652,196],{"class":178},[59,25654,25655,25658,25660],{"class":61,"line":3150},[59,25656,25657],{"class":174},"    externalTrafficPolicy",[59,25659,179],{"class":178},[59,25661,25662],{"class":69},"Cluster\n",[59,25664,25665],{"class":61,"line":3163},[59,25666,188],{"emptyLinePlaceholder":187},[59,25668,25669,25672],{"class":61,"line":3176},[59,25670,25671],{"class":174},"deployment",[59,25673,196],{"class":178},[59,25675,25676,25678,25680],{"class":61,"line":3187},[59,25677,13715],{"class":174},[59,25679,179],{"class":178},[59,25681,3184],{"class":73},[59,25683,25684],{"class":61,"line":3193},[59,25685,188],{"emptyLinePlaceholder":187},[59,25687,25688,25691],{"class":61,"line":3201},[59,25689,25690],{"class":174},"ports",[59,25692,196],{"class":178},[59,25694,25695,25698],{"class":61,"line":3214},[59,25696,25697],{"class":174},"  web",[59,25699,196],{"class":178},[59,25701,25702,25704,25706],{"class":61,"line":3222},[59,25703,14064],{"class":174},[59,25705,179],{"class":178},[59,25707,25708],{"class":73},"8000\n",[59,25710,25711,25714],{"class":61,"line":3233},[59,25712,25713],{"class":174},"    expose",[59,25715,196],{"class":178},[59,25717,25718,25721,25723],{"class":61,"line":3239},[59,25719,25720],{"class":174},"      default",[59,25722,179],{"class":178},[59,25724,3230],{"class":73},[59,25726,25727,25730,25732],{"class":61,"line":3247},[59,25728,25729],{"class":174},"    exposedPort",[59,25731,179],{"class":178},[59,25733,22591],{"class":73},[59,25735,25736,25739,25741],{"class":61,"line":3256},[59,25737,25738],{"class":174},"    protocol",[59,25740,179],{"class":178},[59,25742,14058],{"class":69},[59,25744,25745,25748],{"class":61,"line":3261},[59,25746,25747],{"class":174},"    forwardedHeaders",[59,25749,196],{"class":178},[59,25751,25752,25755],{"class":61,"line":3269},[59,25753,25754],{"class":174},"      trustedIPs",[59,25756,196],{"class":178},[59,25758,25759,25761],{"class":61,"line":3278},[59,25760,13829],{"class":178},[59,25762,25763],{"class":69},"\"10.0.0.0\u002F8\"\n",[59,25765,25766,25769],{"class":61,"line":3284},[59,25767,25768],{"class":174},"    proxyProtocol",[59,25770,196],{"class":178},[59,25772,25773,25775],{"class":61,"line":3289},[59,25774,25754],{"class":174},[59,25776,196],{"class":178},[59,25778,25779,25781],{"class":61,"line":3297},[59,25780,13829],{"class":178},[59,25782,25763],{"class":69},[59,25784,25785,25788],{"class":61,"line":3310},[59,25786,25787],{"class":174},"  websecure",[59,25789,196],{"class":178},[59,25791,25792,25794,25796],{"class":61,"line":3321},[59,25793,14064],{"class":174},[59,25795,179],{"class":178},[59,25797,25798],{"class":73},"8443\n",[59,25800,25801,25803],{"class":61,"line":3327},[59,25802,25713],{"class":174},[59,25804,196],{"class":178},[59,25806,25807,25809,25811],{"class":61,"line":3333},[59,25808,25720],{"class":174},[59,25810,179],{"class":178},[59,25812,3230],{"class":73},[59,25814,25815,25817,25819],{"class":61,"line":6115},[59,25816,25729],{"class":174},[59,25818,179],{"class":178},[59,25820,22617],{"class":73},[59,25822,25823,25825,25827],{"class":61,"line":6152},[59,25824,25738],{"class":174},[59,25826,179],{"class":178},[59,25828,14058],{"class":69},[59,25830,25831,25834],{"class":61,"line":6158},[59,25832,25833],{"class":174},"    http",[59,25835,196],{"class":178},[59,25837,25838,25841],{"class":61,"line":6164},[59,25839,25840],{"class":174},"      middlewares",[59,25842,196],{"class":178},[59,25844,25845,25847],{"class":61,"line":6170},[59,25846,13829],{"class":178},[59,25848,25849],{"class":69},"traefik-force-https@kubernetescrd\n",[59,25851,25852,25854],{"class":61,"line":6175},[59,25853,13829],{"class":178},[59,25855,25856],{"class":69},"traefik-large-body@kubernetescrd\n",[59,25858,25859],{"class":61,"line":6619},[59,25860,25861],{"class":3773},"      # Disable TLS since NLB handles termination\n",[59,25863,25864,25867],{"class":61,"line":6625},[59,25865,25866],{"class":174},"      tls",[59,25868,196],{"class":178},[59,25870,25871,25874,25876],{"class":61,"line":8974},[59,25872,25873],{"class":174},"        enabled",[59,25875,179],{"class":178},[59,25877,2962],{"class":73},[59,25879,25880,25882],{"class":61,"line":8979},[59,25881,25747],{"class":174},[59,25883,196],{"class":178},[59,25885,25886,25888],{"class":61,"line":8985},[59,25887,25754],{"class":174},[59,25889,196],{"class":178},[59,25891,25892,25894],{"class":61,"line":14030},[59,25893,13829],{"class":178},[59,25895,25763],{"class":69},[59,25897,25898,25900],{"class":61,"line":14039},[59,25899,25768],{"class":174},[59,25901,196],{"class":178},[59,25903,25904,25906],{"class":61,"line":14047},[59,25905,25754],{"class":174},[59,25907,196],{"class":178},[59,25909,25910,25912],{"class":61,"line":14061},[59,25911,13829],{"class":178},[59,25913,25763],{"class":69},[59,25915,25916],{"class":61,"line":14071},[59,25917,188],{"emptyLinePlaceholder":187},[59,25919,25920,25923],{"class":61,"line":14519},[59,25921,25922],{"class":174},"ingressClass",[59,25924,196],{"class":178},[59,25926,25927,25929,25931],{"class":61,"line":14530},[59,25928,2957],{"class":174},[59,25930,179],{"class":178},[59,25932,3230],{"class":73},[59,25934,25935,25938,25940],{"class":61,"line":14538},[59,25936,25937],{"class":174},"  isDefaultClass",[59,25939,179],{"class":178},[59,25941,2962],{"class":73},[59,25943,25944,25946,25948],{"class":61,"line":14549},[59,25945,13682],{"class":174},[59,25947,179],{"class":178},[59,25949,25950],{"class":69},"traefik\n",[59,25952,25953],{"class":61,"line":14555},[59,25954,188],{"emptyLinePlaceholder":187},[59,25956,25957,25960],{"class":61,"line":14560},[59,25958,25959],{"class":174},"additionalArguments",[59,25961,196],{"class":178},[59,25963,25964,25966],{"class":61,"line":14569},[59,25965,14050],{"class":178},[59,25967,25968],{"class":69},"\"--entryPoints.web.proxyProtocol.insecure=true\"\n",[59,25970,25971,25973],{"class":61,"line":14578},[59,25972,14050],{"class":178},[59,25974,25975],{"class":69},"\"--entryPoints.websecure.proxyProtocol.insecure=true\"\n",[59,25977,25978,25980],{"class":61,"line":14585},[59,25979,14050],{"class":178},[59,25981,25982],{"class":69},"\"--entryPoints.web.forwardedHeaders.insecure=true\"\n",[59,25984,25985,25987],{"class":61,"line":14594},[59,25986,14050],{"class":178},[59,25988,25989],{"class":69},"\"--entryPoints.websecure.forwardedHeaders.insecure=true\"\n",[59,25991,25992],{"class":61,"line":14601},[59,25993,188],{"emptyLinePlaceholder":187},[59,25995,25996,25998],{"class":61,"line":14608},[59,25997,1806],{"class":174},[59,25999,196],{"class":178},[59,26001,26002,26005],{"class":61,"line":14617},[59,26003,26004],{"class":174},"  kubernetesIngress",[59,26006,196],{"class":178},[59,26008,26009,26011,26013],{"class":61,"line":14624},[59,26010,16930],{"class":174},[59,26012,179],{"class":178},[59,26014,3230],{"class":73},[59,26016,26017,26020],{"class":61,"line":14635},[59,26018,26019],{"class":174},"  kubernetesCRD",[59,26021,196],{"class":178},[59,26023,26024,26026,26028],{"class":61,"line":14644},[59,26025,16930],{"class":174},[59,26027,179],{"class":178},[59,26029,3230],{"class":73},[59,26031,26032],{"class":61,"line":14653},[59,26033,188],{"emptyLinePlaceholder":187},[59,26035,26036,26039],{"class":61,"line":14658},[59,26037,26038],{"class":174},"api",[59,26040,196],{"class":178},[59,26042,26043,26046,26048],{"class":61,"line":14667},[59,26044,26045],{"class":174},"  dashboard",[59,26047,179],{"class":178},[59,26049,2962],{"class":73},[59,26051,26052,26055,26057],{"class":61,"line":14677},[59,26053,26054],{"class":174},"  insecure",[59,26056,179],{"class":178},[59,26058,2962],{"class":73},[59,26060,26061],{"class":61,"line":14684},[59,26062,188],{"emptyLinePlaceholder":187},[59,26064,26065,26068],{"class":61,"line":14693},[59,26066,26067],{"class":174},"logs",[59,26069,196],{"class":178},[59,26071,26072,26075],{"class":61,"line":14700},[59,26073,26074],{"class":174},"  access",[59,26076,196],{"class":178},[59,26078,26079,26081,26083],{"class":61,"line":14708},[59,26080,16930],{"class":174},[59,26082,179],{"class":178},[59,26084,3230],{"class":73},[59,26086,26087,26090],{"class":61,"line":14716},[59,26088,26089],{"class":174},"    fields",[59,26091,196],{"class":178},[59,26093,26094,26097],{"class":61,"line":14724},[59,26095,26096],{"class":174},"      headers",[59,26098,196],{"class":178},[59,26100,26101,26104,26106],{"class":61,"line":14732},[59,26102,26103],{"class":174},"        defaultMode",[59,26105,179],{"class":178},[59,26107,26108],{"class":69},"keep\n",[59,26110,26112],{"class":61,"line":26111},78,[59,26113,188],{"emptyLinePlaceholder":187},[59,26115,26117,26120],{"class":61,"line":26116},79,[59,26118,26119],{"class":174},"extraObjects",[59,26121,196],{"class":178},[59,26123,26124,26126,26128,26130],{"class":61,"line":17786},[59,26125,14050],{"class":178},[59,26127,13655],{"class":174},[59,26129,179],{"class":178},[59,26131,26132],{"class":69},"traefik.io\u002Fv1alpha1\n",[59,26134,26136,26139,26141],{"class":61,"line":26135},81,[59,26137,26138],{"class":174},"    kind",[59,26140,179],{"class":178},[59,26142,26143],{"class":69},"Middleware\n",[59,26145,26147,26149],{"class":61,"line":26146},82,[59,26148,13766],{"class":174},[59,26150,196],{"class":178},[59,26152,26154,26157,26159],{"class":61,"line":26153},83,[59,26155,26156],{"class":174},"      name",[59,26158,179],{"class":178},[59,26160,26161],{"class":69},"force-https\n",[59,26163,26165,26168,26170],{"class":61,"line":26164},84,[59,26166,26167],{"class":174},"      namespace",[59,26169,179],{"class":178},[59,26171,25950],{"class":69},[59,26173,26175,26177],{"class":61,"line":26174},85,[59,26176,13789],{"class":174},[59,26178,196],{"class":178},[59,26180,26182,26184],{"class":61,"line":26181},86,[59,26183,26096],{"class":174},[59,26185,196],{"class":178},[59,26187,26189,26192],{"class":61,"line":26188},87,[59,26190,26191],{"class":174},"        customRequestHeaders",[59,26193,196],{"class":178},[59,26195,26197,26200,26202],{"class":61,"line":26196},88,[59,26198,26199],{"class":174},"          X-Forwarded-Proto",[59,26201,179],{"class":178},[59,26203,26204],{"class":69},"\"https\"\n",[59,26206,26208,26210,26212,26214],{"class":61,"line":26207},89,[59,26209,14050],{"class":178},[59,26211,13655],{"class":174},[59,26213,179],{"class":178},[59,26215,26132],{"class":69},[59,26217,26219,26221,26223],{"class":61,"line":26218},90,[59,26220,26138],{"class":174},[59,26222,179],{"class":178},[59,26224,26143],{"class":69},[59,26226,26228,26230],{"class":61,"line":26227},91,[59,26229,13766],{"class":174},[59,26231,196],{"class":178},[59,26233,26235,26237,26239],{"class":61,"line":26234},92,[59,26236,26156],{"class":174},[59,26238,179],{"class":178},[59,26240,26241],{"class":69},"large-body\n",[59,26243,26245,26247,26249],{"class":61,"line":26244},93,[59,26246,26167],{"class":174},[59,26248,179],{"class":178},[59,26250,25950],{"class":69},[59,26252,26254,26256],{"class":61,"line":26253},94,[59,26255,13789],{"class":174},[59,26257,196],{"class":178},[59,26259,26261,26264],{"class":61,"line":26260},95,[59,26262,26263],{"class":174},"      buffering",[59,26265,196],{"class":178},[59,26267,26269,26272,26274],{"class":61,"line":26268},96,[59,26270,26271],{"class":174},"        maxRequestBodyBytes",[59,26273,179],{"class":178},[59,26275,26276],{"class":73},"10485760\n",[14,26278,26279],{},"Install the Traefik with the following command:",[50,26281,26283],{"className":52,"code":26282,"language":54,"meta":55,"style":55},"helm repo add traefik https:\u002F\u002Ftraefik.github.io\u002Fcharts\nhelm repo update\nhelm upgrade --install traefik traefik\u002Ftraefik \\\n  --create-namespace \\\n  -n traefik \\\n  -f traefik-values.yaml \\\n  --wait \\\n  --atomic\n",[18,26284,26285,26301,26309,26326,26333,26342,26352,26359],{"__ignoreMap":55},[59,26286,26287,26290,26293,26295,26298],{"class":61,"line":62},[59,26288,26289],{"class":65},"helm",[59,26291,26292],{"class":69}," repo",[59,26294,22570],{"class":69},[59,26296,26297],{"class":69}," traefik",[59,26299,26300],{"class":69}," https:\u002F\u002Ftraefik.github.io\u002Fcharts\n",[59,26302,26303,26305,26307],{"class":61,"line":77},[59,26304,26289],{"class":65},[59,26306,26292],{"class":69},[59,26308,23228],{"class":69},[59,26310,26311,26313,26316,26319,26321,26324],{"class":61,"line":88},[59,26312,26289],{"class":65},[59,26314,26315],{"class":69}," upgrade",[59,26317,26318],{"class":73}," --install",[59,26320,26297],{"class":69},[59,26322,26323],{"class":69}," traefik\u002Ftraefik",[59,26325,74],{"class":73},[59,26327,26328,26331],{"class":61,"line":99},[59,26329,26330],{"class":73},"  --create-namespace",[59,26332,74],{"class":73},[59,26334,26335,26338,26340],{"class":61,"line":156},[59,26336,26337],{"class":73},"  -n",[59,26339,26297],{"class":69},[59,26341,74],{"class":73},[59,26343,26344,26347,26350],{"class":61,"line":216},[59,26345,26346],{"class":73},"  -f",[59,26348,26349],{"class":69}," traefik-values.yaml",[59,26351,74],{"class":73},[59,26353,26354,26357],{"class":61,"line":224},[59,26355,26356],{"class":73},"  --wait",[59,26358,74],{"class":73},[59,26360,26361],{"class":61,"line":233},[59,26362,26363],{"class":73},"  --atomic\n",[104,26365,26367],{"id":26366},"references","References",[14,26369,26370],{},[41,26371,26372],{"href":26372,"rel":26373},"https:\u002F\u002Fdoc.traefik.io\u002Ftraefik\u002Fgetting-started\u002Fkubernetes\u002F",[831],[23,26375,20348],{"id":20347},[14,26377,26378],{},"AWS ALB has a hard limit of 100 Ingress endpoints which limits the number of Projects\u002FInstances that can be deployed.",[23,26380,26382],{"id":26381},"setup-aws-ses-for-email","Setup AWS SES for email",[14,26384,26385],{},[41,26386,26387],{"href":26387,"rel":26388},"https:\u002F\u002Feu-west-1.console.aws.amazon.com\u002Fses\u002Fhome?region=eu-west-1#\u002Fhomepage",[831],[14,26390,26391],{},"Setup identity to match sending domain (requires DNS entries)\nSetup email identity to send test emails to\nRequest move to production from sandbox (need to include examples of emails being sent and why\u002Fwhen those emails will be sent should only need this for prod)",[14,26393,26394,26397],{},[18,26395,26396],{},"ses_policy.json"," (with suitable aws id, aws region and domain modifications):",[50,26399,26401],{"className":2972,"code":26400,"language":2974,"meta":55,"style":55},"{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"VisualEditor0\",\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"ses:SendEmail\",\n                \"ses:SendTemplatedEmail\",\n                \"ses:SendRawEmail\"\n            ],\n            \"Resource\": \"arn:aws:ses:[aws region]:[aws id]:identity\u002F[domain name]\"\n        }\n    ]\n}\n",[18,26402,26403,26407,26419,26426,26430,26442,26454,26461,26468,26475,26480,26484,26494,26498,26502],{"__ignoreMap":55},[59,26404,26405],{"class":61,"line":62},[59,26406,2981],{"class":178},[59,26408,26409,26412,26414,26417],{"class":61,"line":77},[59,26410,26411],{"class":73},"    \"Version\"",[59,26413,179],{"class":178},[59,26415,26416],{"class":69},"\"2012-10-17\"",[59,26418,2665],{"class":178},[59,26420,26421,26424],{"class":61,"line":88},[59,26422,26423],{"class":73},"    \"Statement\"",[59,26425,8686],{"class":178},[59,26427,26428],{"class":61,"line":99},[59,26429,6003],{"class":178},[59,26431,26432,26435,26437,26440],{"class":61,"line":156},[59,26433,26434],{"class":73},"            \"Sid\"",[59,26436,179],{"class":178},[59,26438,26439],{"class":69},"\"VisualEditor0\"",[59,26441,2665],{"class":178},[59,26443,26444,26447,26449,26452],{"class":61,"line":216},[59,26445,26446],{"class":73},"            \"Effect\"",[59,26448,179],{"class":178},[59,26450,26451],{"class":69},"\"Allow\"",[59,26453,2665],{"class":178},[59,26455,26456,26459],{"class":61,"line":224},[59,26457,26458],{"class":73},"            \"Action\"",[59,26460,8686],{"class":178},[59,26462,26463,26466],{"class":61,"line":233},[59,26464,26465],{"class":69},"                \"ses:SendEmail\"",[59,26467,2665],{"class":178},[59,26469,26470,26473],{"class":61,"line":241},[59,26471,26472],{"class":69},"                \"ses:SendTemplatedEmail\"",[59,26474,2665],{"class":178},[59,26476,26477],{"class":61,"line":249},[59,26478,26479],{"class":69},"                \"ses:SendRawEmail\"\n",[59,26481,26482],{"class":61,"line":257},[59,26483,8819],{"class":178},[59,26485,26486,26489,26491],{"class":61,"line":3137},[59,26487,26488],{"class":73},"            \"Resource\"",[59,26490,179],{"class":178},[59,26492,26493],{"class":69},"\"arn:aws:ses:[aws region]:[aws id]:identity\u002F[domain name]\"\n",[59,26495,26496],{"class":61,"line":3150},[59,26497,6016],{"class":178},[59,26499,26500],{"class":61,"line":3163},[59,26501,8982],{"class":178},[59,26503,26504],{"class":61,"line":3176},[59,26505,3336],{"class":178},[50,26507,26509],{"className":52,"code":26508,"language":54,"meta":55,"style":55},"IAM_POLICY_ARN=$(aws iam create-policy --policy-name FlowForgeSendEmail --policy-document file:\u002F\u002Fses_policy.json --output json | jq -r .Policy.Arn)\nACCOUNT_ID=$(aws sts get-caller-identity --query \"Account\" --output text)\nOIDC_PROVIDER=$(aws eks describe-cluster --name flowforge --query \"cluster.identity.oidc.issuer\" --output text | sed -e \"s\u002F^https:\\\u002F\\\u002F\u002F\u002F\")\n\n\nread -r -d '' TRUST_RELATIONSHIP \u003C\u003CEOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"Federated\": \"arn:aws:iam::${ACCOUNT_ID}:oidc-provider\u002F${OIDC_PROVIDER}\"\n      },\n      \"Action\": \"sts:AssumeRoleWithWebIdentity\",\n      \"Condition\": {\n        \"StringEquals\": {\n          \"${OIDC_PROVIDER}:sub\": \"system:serviceaccount:default:flowforge\"\n        }\n      }\n    }\n  ]\n}\nEOF\necho \"${TRUST_RELATIONSHIP}\" > trust.json\n\naws iam create-role --role-name flowforge_service_account_role --assume-role-policy-document file:\u002F\u002Ftrust.json --description \"Role to bind to flowforge service account\"\n\naws iam attach-role-policy --role-name flowforge_service_account_role --policy-arn=$IAM_POLICY_ARN\n",[18,26510,26511,26560,26590,26632,26636,26640,26659,26663,26668,26673,26678,26683,26688,26703,26707,26712,26717,26722,26732,26736,26740,26744,26749,26753,26757,26775,26779,26806,26810],{"__ignoreMap":55},[59,26512,26513,26516,26518,26521,26523,26526,26529,26532,26535,26538,26541,26544,26547,26549,26552,26555,26558],{"class":61,"line":62},[59,26514,26515],{"class":178},"IAM_POLICY_ARN",[59,26517,1373],{"class":1372},[59,26519,26520],{"class":178},"$(",[59,26522,19096],{"class":65},[59,26524,26525],{"class":69}," iam",[59,26527,26528],{"class":69}," create-policy",[59,26530,26531],{"class":73}," --policy-name",[59,26533,26534],{"class":69}," FlowForgeSendEmail",[59,26536,26537],{"class":73}," --policy-document",[59,26539,26540],{"class":69}," file:\u002F\u002Fses_policy.json",[59,26542,26543],{"class":73}," --output",[59,26545,26546],{"class":69}," json",[59,26548,23343],{"class":1372},[59,26550,26551],{"class":65}," jq",[59,26553,26554],{"class":73}," -r",[59,26556,26557],{"class":69}," .Policy.Arn",[59,26559,5041],{"class":178},[59,26561,26562,26565,26567,26569,26571,26574,26577,26580,26583,26585,26588],{"class":61,"line":77},[59,26563,26564],{"class":178},"ACCOUNT_ID",[59,26566,1373],{"class":1372},[59,26568,26520],{"class":178},[59,26570,19096],{"class":65},[59,26572,26573],{"class":69}," sts",[59,26575,26576],{"class":69}," get-caller-identity",[59,26578,26579],{"class":73}," --query",[59,26581,26582],{"class":69}," \"Account\"",[59,26584,26543],{"class":73},[59,26586,26587],{"class":69}," text",[59,26589,5041],{"class":178},[59,26591,26592,26595,26597,26599,26601,26604,26607,26609,26611,26613,26616,26618,26620,26622,26625,26627,26630],{"class":61,"line":88},[59,26593,26594],{"class":178},"OIDC_PROVIDER",[59,26596,1373],{"class":1372},[59,26598,26520],{"class":178},[59,26600,19096],{"class":65},[59,26602,26603],{"class":69}," eks",[59,26605,26606],{"class":69}," describe-cluster",[59,26608,8447],{"class":73},[59,26610,9507],{"class":69},[59,26612,26579],{"class":73},[59,26614,26615],{"class":69}," \"cluster.identity.oidc.issuer\"",[59,26617,26543],{"class":73},[59,26619,26587],{"class":69},[59,26621,23343],{"class":1372},[59,26623,26624],{"class":65}," sed",[59,26626,8453],{"class":73},[59,26628,26629],{"class":69}," \"s\u002F^https:\\\u002F\\\u002F\u002F\u002F\"",[59,26631,5041],{"class":178},[59,26633,26634],{"class":61,"line":99},[59,26635,188],{"emptyLinePlaceholder":187},[59,26637,26638],{"class":61,"line":156},[59,26639,188],{"emptyLinePlaceholder":187},[59,26641,26642,26645,26647,26649,26652,26655,26657],{"class":61,"line":216},[59,26643,26644],{"class":73},"read",[59,26646,26554],{"class":73},[59,26648,9743],{"class":73},[59,26650,26651],{"class":69}," ''",[59,26653,26654],{"class":69}," TRUST_RELATIONSHIP",[59,26656,20850],{"class":1372},[59,26658,20853],{"class":69},[59,26660,26661],{"class":61,"line":224},[59,26662,2981],{"class":69},[59,26664,26665],{"class":61,"line":233},[59,26666,26667],{"class":69},"  \"Version\": \"2012-10-17\",\n",[59,26669,26670],{"class":61,"line":241},[59,26671,26672],{"class":69},"  \"Statement\": [\n",[59,26674,26675],{"class":61,"line":249},[59,26676,26677],{"class":69},"    {\n",[59,26679,26680],{"class":61,"line":257},[59,26681,26682],{"class":69},"      \"Effect\": \"Allow\",\n",[59,26684,26685],{"class":61,"line":3137},[59,26686,26687],{"class":69},"      \"Principal\": {\n",[59,26689,26690,26693,26695,26698,26700],{"class":61,"line":3150},[59,26691,26692],{"class":69},"        \"Federated\": \"arn:aws:iam::${",[59,26694,26564],{"class":178},[59,26696,26697],{"class":69},"}:oidc-provider\u002F${",[59,26699,26594],{"class":178},[59,26701,26702],{"class":69},"}\"\n",[59,26704,26705],{"class":61,"line":3163},[59,26706,3236],{"class":69},[59,26708,26709],{"class":61,"line":3176},[59,26710,26711],{"class":69},"      \"Action\": \"sts:AssumeRoleWithWebIdentity\",\n",[59,26713,26714],{"class":61,"line":3187},[59,26715,26716],{"class":69},"      \"Condition\": {\n",[59,26718,26719],{"class":61,"line":3193},[59,26720,26721],{"class":69},"        \"StringEquals\": {\n",[59,26723,26724,26727,26729],{"class":61,"line":3201},[59,26725,26726],{"class":69},"          \"${",[59,26728,26594],{"class":178},[59,26730,26731],{"class":69},"}:sub\": \"system:serviceaccount:default:flowforge\"\n",[59,26733,26734],{"class":61,"line":3214},[59,26735,6016],{"class":69},[59,26737,26738],{"class":61,"line":3222},[59,26739,3281],{"class":69},[59,26741,26742],{"class":61,"line":3233},[59,26743,3324],{"class":69},[59,26745,26746],{"class":61,"line":3239},[59,26747,26748],{"class":69},"  ]\n",[59,26750,26751],{"class":61,"line":3247},[59,26752,3336],{"class":69},[59,26754,26755],{"class":61,"line":3256},[59,26756,20853],{"class":69},[59,26758,26759,26761,26764,26767,26770,26772],{"class":61,"line":3261},[59,26760,20730],{"class":73},[59,26762,26763],{"class":69}," \"${",[59,26765,26766],{"class":178},"TRUST_RELATIONSHIP",[59,26768,26769],{"class":69},"}\"",[59,26771,20464],{"class":1372},[59,26773,26774],{"class":69}," trust.json\n",[59,26776,26777],{"class":61,"line":3269},[59,26778,188],{"emptyLinePlaceholder":187},[59,26780,26781,26783,26785,26788,26791,26794,26797,26800,26803],{"class":61,"line":3278},[59,26782,19096],{"class":65},[59,26784,26525],{"class":69},[59,26786,26787],{"class":69}," create-role",[59,26789,26790],{"class":73}," --role-name",[59,26792,26793],{"class":69}," flowforge_service_account_role",[59,26795,26796],{"class":73}," --assume-role-policy-document",[59,26798,26799],{"class":69}," file:\u002F\u002Ftrust.json",[59,26801,26802],{"class":73}," --description",[59,26804,26805],{"class":69}," \"Role to bind to flowforge service account\"\n",[59,26807,26808],{"class":61,"line":3284},[59,26809,188],{"emptyLinePlaceholder":187},[59,26811,26812,26814,26816,26819,26821,26823,26826],{"class":61,"line":3289},[59,26813,19096],{"class":65},[59,26815,26525],{"class":69},[59,26817,26818],{"class":69}," attach-role-policy",[59,26820,26790],{"class":73},[59,26822,26793],{"class":69},[59,26824,26825],{"class":73}," --policy-arn=",[59,26827,26828],{"class":178},"$IAM_POLICY_ARN\n",[14,26830,26831,26832],{},"Make a note of the  ARN for the IAM Role (flowforge_service_account_role) is needed in the helm chart values yaml file.\n",[18,26833,26834],{},"aws iam get-role --role-name flowforge_service_account_role",[104,26836,26367],{"id":26837},"references-1",[14,26839,26840,26841],{},"Create a IAM Role to bind IAM Policies to the service account ",[41,26842,26843],{"href":26843,"rel":26844},"https:\u002F\u002Fdocs.aws.amazon.com\u002Feks\u002Flatest\u002Fuserguide\u002Fiam-roles-for-service-accounts.html",[831],[14,26846,26847,26848,660],{},"Create IAM Policy to allow sending emails (example: ",[41,26849,26850],{"href":26850,"rel":26851},"https:\u002F\u002Fdocs.aws.amazon.com\u002Fses\u002Flatest\u002Fdg\u002Fsending-authorization-policy-examples.html",[831],[23,26853,26855],{"id":26854},"use-aws-rds-postgresql-instance","Use AWS RDS PostgreSQL instance",[14,26857,26858],{},"The following script creates a AWS RDS PostgreSQL instance, it also\nsets up some network access rules so only the FlowFuse app can access\nit from inside the cluster (and not the Node-RED instances).",[14,26860,26861],{},"Please read it carefully before running it to ensure you understand it.",[14,26863,26864,26865],{},"A copy of this file can be found ",[41,26866,785],{"href":26867,"rel":26868},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowforge\u002Fblob\u002Ff8c06e3cea0ffb539350797af429f1a0366243f1\u002Fdocs\u002Finstall\u002Fkubernetes\u002Fsetup-rds.sh",[831],[14,26870,26871],{},"Run the following command",[50,26873,26875],{"className":52,"code":26874,"language":54,"meta":55,"style":55},".\u002Fsetup-rds.sh\n",[18,26876,26877],{"__ignoreMap":55},[59,26878,26879],{"class":61,"line":62},[59,26880,26874],{"class":65},[14,26882,26883],{},"Make a note of the postgres hostname",[50,26885,26887],{"className":52,"code":26886,"language":54,"meta":55,"style":55},"aws rds describe-db-instances | jq .DBInstances[].Endpoint.Address\n",[18,26888,26889],{"__ignoreMap":55},[59,26890,26891,26893,26896,26899,26901,26903],{"class":61,"line":62},[59,26892,19096],{"class":65},[59,26894,26895],{"class":69}," rds",[59,26897,26898],{"class":69}," describe-db-instances",[59,26900,23343],{"class":1372},[59,26902,26551],{"class":65},[59,26904,26905],{"class":69}," .DBInstances[].Endpoint.Address\n",[104,26907,26367],{"id":26908},"references-2",[14,26910,26911],{},[41,26912,26913],{"href":26913,"rel":26914},"https:\u002F\u002Fdev.to\u002Fbensooraj\u002Faccessing-amazon-rds-from-aws-eks-2pc3",[831],[316,26916,26917],{},"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 .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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}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}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":55,"searchDepth":77,"depth":77,"links":26919},[26920,26924,26925,26926,26927,26931,26932,26935],{"id":25,"depth":77,"text":26,"children":26921},[26922,26923],{"id":25101,"depth":88,"text":25102},{"id":25120,"depth":88,"text":25120},{"id":25132,"depth":77,"text":25133},{"id":25139,"depth":77,"text":25140},{"id":25153,"depth":77,"text":25154},{"id":25497,"depth":77,"text":25498,"children":26928},[26929,26930],{"id":25501,"depth":88,"text":25502},{"id":26366,"depth":88,"text":26367},{"id":20347,"depth":77,"text":20348},{"id":26381,"depth":77,"text":26382,"children":26933},[26934],{"id":26837,"depth":88,"text":26367},{"id":26854,"depth":77,"text":26855,"children":26936},[26937],{"id":26908,"depth":88,"text":26367},{},"AWS EKS Installation","install\u002Fkubernetes\u002Faws.md","\u002Fdocs\u002Finstall\u002Fkubernetes\u002Faws",{"title":25074,"description":25082},"docs\u002Finstall\u002Fkubernetes\u002Faws","iBj7K9SwumnH8YXFJ36xALEcEutVtSNrrYW2qWQO_y8",{"id":26946,"title":26947,"body":26948,"description":26955,"extension":329,"layout":330,"meta":28409,"navGroup":330,"navOrder":330,"navTitle":28410,"navigation":187,"originalPath":28411,"path":28412,"redirect":330,"seo":28413,"stem":28414,"updated":337,"version":338,"__hash__":28415},"docs\u002Fdocs\u002Finstall\u002Fkubernetes\u002Faws_terraform.md","FlowFuse-oriented infrastructure in AWS using Terraform and Helm",{"type":7,"value":26949,"toc":28392},[26950,26953,26956,27004,27030,27033,27035,27063,27067,27070,27091,27095,27098,27146,27159,27166,27182,27185,27192,27197,27209,27227,27281,27285,27290,27294,27297,27312,27315,27331,27335,27338,27352,27355,27370,27374,27377,27391,27394,27409,27413,27416,27430,27433,27448,27452,27455,27469,27472,27487,27490,27507,27511,27514,27528,27531,27546,27554,27570,27574,27578,27583,27593,27602,27618,27625,28269,28272,28305,28308,28378,28382,28389],[10,26951,26947],{"id":26952},"flowfuse-oriented-infrastructure-in-aws-using-terraform-and-helm",[14,26954,26955],{},"This step-by-step guide will help you use the Terraform modules available in the FlowFuse repository to setup resources required to run FlowFuse platform on AWS.\nAfter following the commands in this documentation, the following resources will be created:",[28,26957,26958,26964,26971,26978,26984,26991,26997],{},[31,26959,26960],{},[41,26961,26963],{"href":26962,"target":25509},"https:\u002F\u002Fdocs.aws.amazon.com\u002Fvpc\u002Flatest\u002Fuserguide\u002Fwhat-is-amazon-vpc.html","VPC",[31,26965,26966,26970],{},[41,26967,26969],{"href":26968,"target":25509},"https:\u002F\u002Fdocs.aws.amazon.com\u002Feks\u002Flatest\u002Fuserguide\u002Fwhat-is-eks.html","EKS"," (Elastic Kubernetes Service)",[31,26972,26973,26977],{},[41,26974,26976],{"href":26975,"target":25509},"https:\u002F\u002Fdocs.aws.amazon.com\u002FAmazonRDS\u002Flatest\u002FUserGuide\u002FWelcome.html","RDS"," (Relational Database Service)",[31,26979,26980],{},[41,26981,26983],{"href":26982,"target":25509},"https:\u002F\u002Fdocs.aws.amazon.com\u002Fvpc\u002Flatest\u002Fuserguide\u002Fvpc-peering.html","VPC Peering",[31,26985,26986,26990],{},[41,26987,26989],{"href":26988,"target":25509},"https:\u002F\u002Fdocs.aws.amazon.com\u002FRoute53\u002Flatest\u002FDeveloperGuide\u002FWelcome.html","Route53"," domain zone",[31,26992,26993],{},[41,26994,26996],{"href":26995,"target":25509},"https:\u002F\u002Fdocs.aws.amazon.com\u002Facm\u002Flatest\u002Fuserguide\u002Facm-overview.html","AWS Certificate",[31,26998,26999,27003],{},[41,27000,27002],{"href":27001,"target":25509},"https:\u002F\u002Fdocs.aws.amazon.com\u002Fses\u002Flatest\u002FDeveloperGuide\u002FWelcome.html","SES"," (Simple Email Service)",[14,27005,27006,27007,3012,27010,3012,27013,3012,27016,3012,27019,302,27022,27025,27026,27029],{},"The full list of the resources created by each module can be found in the documentation of the respective modules.\nTo ensure proper infrastructure deployment, these modules must be executed in a specific order: ",[18,27008,27009],{},"vpc",[18,27011,27012],{},"eks",[18,27014,27015],{},"rds",[18,27017,27018],{},"vpc-peering",[18,27020,27021],{},"route53",[18,27023,27024],{},"ses",".\nAdditionally, a shared variables file (",[18,27027,27028],{},"terraform.tfvars",") will be created to manage configuration used by all modules.",[14,27031,27032],{},"While Terraform supports nested modules for complex infrastructure, this guide will treat each module as a root module for simplicity.\nAll modules are configurable, allowing you to customize the infrastructure to meet your specific needs. Detailed information on configuring each module can be found in its documentation.\nThis guide will uses minimal configuration to demonstrate the basic setup.",[23,27034,26],{"id":25},[28,27036,27037,27055],{},[31,27038,27039,3012,27043,3012,27046,27050,27051,27054],{},[41,27040,27042],{"href":27041,"target":25509},"https:\u002F\u002Fdeveloper.hashicorp.com\u002Fterraform\u002Ftutorials\u002Faws-get-started\u002Finstall-cli","Terraform",[41,27044,13627],{"href":27045,"target":25509},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Ftasks\u002Ftools\u002F#kubectl",[41,27047,27049],{"href":27048,"target":25509},"https:\u002F\u002Fhelm.sh\u002Fdocs\u002Fhelm\u002Fhelm_install\u002F","Helm"," and\n",[41,27052,7523],{"href":27053,"target":25509},"https:\u002F\u002Fgit-scm.com\u002Fbook\u002Fen\u002Fv2\u002FGetting-Started-Installing-Git"," installed on your machine.",[31,27056,27057,27058,27062],{},"Access to an AWS account through ",[41,27059,27061],{"href":27060,"target":25509},"https:\u002F\u002Frepost.aws\u002Fknowledge-center\u002Fcreate-access-key","AWS access key",", that allows the creation of new resources. The specific resources created by each module are detailed in the documentation of the respective modules.",[23,27064,27066],{"id":27065},"step-1-clone-the-repository","Step 1: Clone the Repository",[14,27068,27069],{},"First, clone the FlowFuse Terraform repository to your local machine:",[50,27071,27073],{"className":52,"code":27072,"language":54,"meta":55,"style":55},"git clone https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fterraform-aws-flowfuse.git\ncd terraform-aws-flowfuse\n",[18,27074,27075,27084],{"__ignoreMap":55},[59,27076,27077,27079,27081],{"class":61,"line":62},[59,27078,7523],{"class":65},[59,27080,7526],{"class":69},[59,27082,27083],{"class":69}," https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fterraform-aws-flowfuse.git\n",[59,27085,27086,27088],{"class":61,"line":77},[59,27087,7534],{"class":73},[59,27089,27090],{"class":69}," terraform-aws-flowfuse\n",[23,27092,27094],{"id":27093},"step-2-set-aws-environment-variables","Step 2: Set AWS Environment Variables",[14,27096,27097],{},"Set the following environment variables to authenticate Terraform with your AWS account:",[50,27099,27101],{"className":52,"code":27100,"language":54,"meta":55,"style":55},"export AWS_ACCESS_KEY_ID=\u003Cyour-access-key-id>\nexport AWS_SECRET_ACCESS_KEY=\u003Cyour-secret-access-key>\nexport AWS_REGION=\u003Cyour-desired-region>\n",[18,27102,27103,27118,27132],{"__ignoreMap":55},[59,27104,27105,27107,27110,27113,27116],{"class":61,"line":62},[59,27106,13205],{"class":1372},[59,27108,27109],{"class":178}," AWS_ACCESS_KEY_ID",[59,27111,27112],{"class":1372},"=\u003C",[59,27114,27115],{"class":178},"your-access-key-id",[59,27117,12331],{"class":1372},[59,27119,27120,27122,27125,27127,27130],{"class":61,"line":77},[59,27121,13205],{"class":1372},[59,27123,27124],{"class":178}," AWS_SECRET_ACCESS_KEY",[59,27126,27112],{"class":1372},[59,27128,27129],{"class":178},"your-secret-access-key",[59,27131,12331],{"class":1372},[59,27133,27134,27136,27139,27141,27144],{"class":61,"line":88},[59,27135,13205],{"class":1372},[59,27137,27138],{"class":178}," AWS_REGION",[59,27140,27112],{"class":1372},[59,27142,27143],{"class":178},"your-desired-region",[59,27145,12331],{"class":1372},[14,27147,27148,27149,3012,27152,15036,27155,27158],{},"Replace ",[18,27150,27151],{},"\u003Cyour-access-key-id>",[18,27153,27154],{},"\u003Cyour-secret-access-key>",[18,27156,27157],{},"\u003Cyour-desired-region>"," with your actual AWS credentials and desired AWS region. All resources must be created in the same region.",[14,27160,27161,27162,27165],{},"For example, if you want to create resources in ",[18,27163,27164],{},"us-west-2",", set the region as follows:",[50,27167,27169],{"className":52,"code":27168,"language":54,"meta":55,"style":55},"export AWS_REGION=us-west-2\n",[18,27170,27171],{"__ignoreMap":55},[59,27172,27173,27175,27177,27179],{"class":61,"line":62},[59,27174,13205],{"class":1372},[59,27176,27138],{"class":178},[59,27178,1373],{"class":1372},[59,27180,27181],{"class":178},"us-west-2\n",[14,27183,27184],{},"Ensure that these environment variables are set for the duration of the session or included in your shell profile to persist across sessions.",[23,27186,27188,27189,27191],{"id":27187},"step-3-create-the-terraformtfvars-file","Step 3: Create the ",[18,27190,27028],{}," File",[14,27193,25513,27194,27196],{},[18,27195,27028],{}," file in the root directory of the repository. This file will contain the shared variables for all the modules.",[50,27198,27200],{"className":52,"code":27199,"language":54,"meta":55,"style":55},"touch terraform.tfvars\n",[18,27201,27202],{"__ignoreMap":55},[59,27203,27204,27206],{"class":61,"line":62},[59,27205,25527],{"class":65},[59,27207,27208],{"class":69}," terraform.tfvars\n",[14,27210,25157,27211,27213,27214,27217,27218,27222,27223,27226],{},[18,27212,27028],{}," file and add the following content.\nReplace ",[18,27215,27216],{},"\u003Caws-user-arn>"," with the ",[41,27219,27221],{"href":27220,"target":25509},"https:\u002F\u002Fdocs.aws.amazon.com\u002FIAM\u002Flatest\u002FUserGuide\u002Freference-arns.html","ARN"," of the AWS user that will have access to the EKS cluster. This user will be granted the ",[18,27224,27225],{},"ClusterAdmin"," role in the EKS cluster.",[50,27228,27232],{"className":27229,"code":27230,"language":27231,"meta":55,"style":55},"language-hcl shiki shiki-themes github-light github-dark","namespace = \"my-company\"\nstage     = \"production\"\nroute53_zone_name = \"my-domain.com\"\neks_access_entry_map = {\n    \"\u003Caws-user-arn>\" = {\n      access_policy_associations = {\n        ClusterAdmin = {}\n      }\n    }\n  }\n","hcl",[18,27233,27234,27239,27244,27249,27254,27259,27264,27269,27273,27277],{"__ignoreMap":55},[59,27235,27236],{"class":61,"line":62},[59,27237,27238],{},"namespace = \"my-company\"\n",[59,27240,27241],{"class":61,"line":77},[59,27242,27243],{},"stage     = \"production\"\n",[59,27245,27246],{"class":61,"line":88},[59,27247,27248],{},"route53_zone_name = \"my-domain.com\"\n",[59,27250,27251],{"class":61,"line":99},[59,27252,27253],{},"eks_access_entry_map = {\n",[59,27255,27256],{"class":61,"line":156},[59,27257,27258],{},"    \"\u003Caws-user-arn>\" = {\n",[59,27260,27261],{"class":61,"line":216},[59,27262,27263],{},"      access_policy_associations = {\n",[59,27265,27266],{"class":61,"line":224},[59,27267,27268],{},"        ClusterAdmin = {}\n",[59,27270,27271],{"class":61,"line":233},[59,27272,3281],{},[59,27274,27275],{"class":61,"line":241},[59,27276,3324],{},[59,27278,27279],{"class":61,"line":249},[59,27280,3330],{},[23,27282,27284],{"id":27283},"step-4-create-aws-resources","Step 4: Create AWS Resources",[14,27286,27287],{},[364,27288,27289],{},"The order of creating resources is important. The documentation specifies the correct order. Attempts to create resources in a different order may fail.",[104,27291,27293],{"id":27292},"_1-vpc-module","1. VPC Module",[14,27295,27296],{},"To create basic networking, initialize the VPC module:",[50,27298,27300],{"className":52,"code":27299,"language":54,"meta":55,"style":55},"terraform -chdir=vpc init\n",[18,27301,27302],{"__ignoreMap":55},[59,27303,27304,27307,27310],{"class":61,"line":62},[59,27305,27306],{"class":65},"terraform",[59,27308,27309],{"class":73}," -chdir=vpc",[59,27311,7554],{"class":69},[14,27313,27314],{},"Apply the VPC module using the shared variables file:",[50,27316,27318],{"className":52,"code":27317,"language":54,"meta":55,"style":55},"terraform -chdir=vpc apply -var-file=..\u002Fterraform.tfvars\n",[18,27319,27320],{"__ignoreMap":55},[59,27321,27322,27324,27326,27328],{"class":61,"line":62},[59,27323,27306],{"class":65},[59,27325,27309],{"class":73},[59,27327,22488],{"class":69},[59,27329,27330],{"class":73}," -var-file=..\u002Fterraform.tfvars\n",[104,27332,27334],{"id":27333},"_2-eks-module","2. EKS Module",[14,27336,27337],{},"To create EKS cluster, initialize the EKS module:",[50,27339,27341],{"className":52,"code":27340,"language":54,"meta":55,"style":55},"terraform -chdir=eks init\n",[18,27342,27343],{"__ignoreMap":55},[59,27344,27345,27347,27350],{"class":61,"line":62},[59,27346,27306],{"class":65},[59,27348,27349],{"class":73}," -chdir=eks",[59,27351,7554],{"class":69},[14,27353,27354],{},"Apply the EKS module using the shared variables file:",[50,27356,27358],{"className":52,"code":27357,"language":54,"meta":55,"style":55},"terraform -chdir=eks apply -var-file=..\u002Fterraform.tfvars\n",[18,27359,27360],{"__ignoreMap":55},[59,27361,27362,27364,27366,27368],{"class":61,"line":62},[59,27363,27306],{"class":65},[59,27365,27349],{"class":73},[59,27367,22488],{"class":69},[59,27369,27330],{"class":73},[104,27371,27373],{"id":27372},"_3-rds-module","3. RDS Module",[14,27375,27376],{},"To create RDS database, initialize the RDS module:",[50,27378,27380],{"className":52,"code":27379,"language":54,"meta":55,"style":55},"terraform -chdir=rds init\n",[18,27381,27382],{"__ignoreMap":55},[59,27383,27384,27386,27389],{"class":61,"line":62},[59,27385,27306],{"class":65},[59,27387,27388],{"class":73}," -chdir=rds",[59,27390,7554],{"class":69},[14,27392,27393],{},"Apply the RDS module using the shared variables file:",[50,27395,27397],{"className":52,"code":27396,"language":54,"meta":55,"style":55},"terraform -chdir=rds apply -var-file=..\u002Fterraform.tfvars\n",[18,27398,27399],{"__ignoreMap":55},[59,27400,27401,27403,27405,27407],{"class":61,"line":62},[59,27402,27306],{"class":65},[59,27404,27388],{"class":73},[59,27406,22488],{"class":69},[59,27408,27330],{"class":73},[104,27410,27412],{"id":27411},"_4-vpc-peering-module","4. VPC Peering Module",[14,27414,27415],{},"To create a peering between EKS and RDS networks, initialize the VPC Peering module:",[50,27417,27419],{"className":52,"code":27418,"language":54,"meta":55,"style":55},"terraform -chdir=vpc-peering init\n",[18,27420,27421],{"__ignoreMap":55},[59,27422,27423,27425,27428],{"class":61,"line":62},[59,27424,27306],{"class":65},[59,27426,27427],{"class":73}," -chdir=vpc-peering",[59,27429,7554],{"class":69},[14,27431,27432],{},"Apply the VPC Peering module using the shared variables file:",[50,27434,27436],{"className":52,"code":27435,"language":54,"meta":55,"style":55},"terraform -chdir=vpc-peering apply -var-file=..\u002Fterraform.tfvars\n",[18,27437,27438],{"__ignoreMap":55},[59,27439,27440,27442,27444,27446],{"class":61,"line":62},[59,27441,27306],{"class":65},[59,27443,27427],{"class":73},[59,27445,22488],{"class":69},[59,27447,27330],{"class":73},[104,27449,27451],{"id":27450},"_5-route53-module","5. Route53 Module",[14,27453,27454],{},"To create a domain and certificate, initialize the Route53 module:",[50,27456,27458],{"className":52,"code":27457,"language":54,"meta":55,"style":55},"terraform -chdir=route53 init\n",[18,27459,27460],{"__ignoreMap":55},[59,27461,27462,27464,27467],{"class":61,"line":62},[59,27463,27306],{"class":65},[59,27465,27466],{"class":73}," -chdir=route53",[59,27468,7554],{"class":69},[14,27470,27471],{},"Apply the Route53 module using the shared variables file:",[50,27473,27475],{"className":52,"code":27474,"language":54,"meta":55,"style":55},"terraform -chdir=route53 apply -var-file=..\u002Fterraform.tfvars\n",[18,27476,27477],{"__ignoreMap":55},[59,27478,27479,27481,27483,27485],{"class":61,"line":62},[59,27480,27306],{"class":65},[59,27482,27466],{"class":73},[59,27484,22488],{"class":69},[59,27486,27330],{"class":73},[14,27488,27489],{},"Remember to change NS records in your domain registrar to the ones provided by the Route53 module.\nNS records are printed after the Route53 module is applied.\nThey can be also printed using the following command:",[50,27491,27493],{"className":52,"code":27492,"language":54,"meta":55,"style":55},"terraform -chdir=route53 output domain_dns_records\n",[18,27494,27495],{"__ignoreMap":55},[59,27496,27497,27499,27501,27504],{"class":61,"line":62},[59,27498,27306],{"class":65},[59,27500,27466],{"class":73},[59,27502,27503],{"class":69}," output",[59,27505,27506],{"class":69}," domain_dns_records\n",[104,27508,27510],{"id":27509},"_6-ses-module","6. SES Module",[14,27512,27513],{},"To create an email service, initialize the SES module:",[50,27515,27517],{"className":52,"code":27516,"language":54,"meta":55,"style":55},"terraform -chdir=ses init\n",[18,27518,27519],{"__ignoreMap":55},[59,27520,27521,27523,27526],{"class":61,"line":62},[59,27522,27306],{"class":65},[59,27524,27525],{"class":73}," -chdir=ses",[59,27527,7554],{"class":69},[14,27529,27530],{},"Apply the SES module using the shared variables file:",[50,27532,27534],{"className":52,"code":27533,"language":54,"meta":55,"style":55},"terraform -chdir=ses apply -var-file=..\u002Fterraform.tfvars\n",[18,27535,27536],{"__ignoreMap":55},[59,27537,27538,27540,27542,27544],{"class":61,"line":62},[59,27539,27306],{"class":65},[59,27541,27525],{"class":73},[59,27543,22488],{"class":69},[59,27545,27330],{"class":73},[14,27547,27548,27549,27553],{},"To get the IAM role ARN created by the SES module and required during ",[41,27550,27552],{"href":27551},"\u002Fdocs\u002Finstall\u002Fconfiguration.md#aws-ses-email","FlowFuse platform configuration",", run the following command:",[50,27555,27557],{"className":52,"code":27556,"language":54,"meta":55,"style":55},"terraform -chdir=eks output flowfuse_role_arn\n",[18,27558,27559],{"__ignoreMap":55},[59,27560,27561,27563,27565,27567],{"class":61,"line":62},[59,27562,27306],{"class":65},[59,27564,27349],{"class":73},[59,27566,27503],{"class":69},[59,27568,27569],{"class":69}," flowfuse_role_arn\n",[23,27571,27573],{"id":27572},"step-5-deploy-traefik","Step 5: Deploy Traefik",[14,27575,25505,27576,25510],{},[41,27577,25502],{"href":25508,"target":25509},[14,27579,25513,27580,27582],{},[18,27581,25516],{}," file for Traefik configuration:",[50,27584,27585],{"className":52,"code":25520,"language":54,"meta":55,"style":55},[18,27586,27587],{"__ignoreMap":55},[59,27588,27589,27591],{"class":61,"line":62},[59,27590,25527],{"class":65},[59,27592,25530],{"class":69},[14,27594,27595,27596,27598,27599,27601],{},"Get the certificate ARN (",[18,27597,25539],{},") from the ",[18,27600,27021],{}," module outputs:",[50,27603,27605],{"className":52,"code":27604,"language":54,"meta":55,"style":55},"terraform -chdir=route53 output acm_certificate_arn\n",[18,27606,27607],{"__ignoreMap":55},[59,27608,27609,27611,27613,27615],{"class":61,"line":62},[59,27610,27306],{"class":65},[59,27612,27466],{"class":73},[59,27614,27503],{"class":69},[59,27616,27617],{"class":69}," acm_certificate_arn\n",[14,27619,25533,27620,25536,27622,27624],{},[18,27621,25516],{},[18,27623,25539],{}," with the certificate ARN.",[50,27626,27627],{"className":165,"code":25543,"language":167,"meta":55,"style":55},[18,27628,27629,27635,27643,27651,27657,27665,27673,27681,27689,27697,27705,27713,27719,27727,27731,27737,27745,27749,27755,27761,27769,27775,27783,27791,27799,27805,27811,27817,27823,27829,27835,27841,27849,27855,27863,27871,27879,27885,27891,27897,27903,27907,27913,27921,27927,27933,27939,27945,27951,27957,27961,27967,27975,27983,27991,27995,28001,28007,28013,28019,28025,28029,28035,28041,28049,28055,28063,28067,28073,28081,28089,28093,28099,28105,28113,28119,28125,28133,28137,28143,28153,28161,28167,28175,28183,28189,28195,28201,28209,28219,28227,28233,28241,28249,28255,28261],{"__ignoreMap":55},[59,27630,27631,27633],{"class":61,"line":62},[59,27632,9504],{"class":174},[59,27634,196],{"class":178},[59,27636,27637,27639,27641],{"class":61,"line":77},[59,27638,2957],{"class":174},[59,27640,179],{"class":178},[59,27642,3230],{"class":73},[59,27644,27645,27647,27649],{"class":61,"line":88},[59,27646,24266],{"class":174},[59,27648,179],{"class":178},[59,27650,25568],{"class":69},[59,27652,27653,27655],{"class":61,"line":99},[59,27654,25573],{"class":174},[59,27656,196],{"class":178},[59,27658,27659,27661,27663],{"class":61,"line":156},[59,27660,25580],{"class":174},[59,27662,179],{"class":178},[59,27664,25585],{"class":69},[59,27666,27667,27669,27671],{"class":61,"line":216},[59,27668,25590],{"class":174},[59,27670,179],{"class":178},[59,27672,25595],{"class":69},[59,27674,27675,27677,27679],{"class":61,"line":224},[59,27676,25600],{"class":174},[59,27678,179],{"class":178},[59,27680,25605],{"class":69},[59,27682,27683,27685,27687],{"class":61,"line":233},[59,27684,25610],{"class":174},[59,27686,179],{"class":178},[59,27688,25615],{"class":69},[59,27690,27691,27693,27695],{"class":61,"line":241},[59,27692,25620],{"class":174},[59,27694,179],{"class":178},[59,27696,25625],{"class":69},[59,27698,27699,27701,27703],{"class":61,"line":249},[59,27700,25630],{"class":174},[59,27702,179],{"class":178},[59,27704,25635],{"class":69},[59,27706,27707,27709,27711],{"class":61,"line":257},[59,27708,25640],{"class":174},[59,27710,179],{"class":178},[59,27712,25645],{"class":69},[59,27714,27715,27717],{"class":61,"line":3137},[59,27716,25650],{"class":174},[59,27718,196],{"class":178},[59,27720,27721,27723,27725],{"class":61,"line":3150},[59,27722,25657],{"class":174},[59,27724,179],{"class":178},[59,27726,25662],{"class":69},[59,27728,27729],{"class":61,"line":3163},[59,27730,188],{"emptyLinePlaceholder":187},[59,27732,27733,27735],{"class":61,"line":3176},[59,27734,25671],{"class":174},[59,27736,196],{"class":178},[59,27738,27739,27741,27743],{"class":61,"line":3187},[59,27740,13715],{"class":174},[59,27742,179],{"class":178},[59,27744,3184],{"class":73},[59,27746,27747],{"class":61,"line":3193},[59,27748,188],{"emptyLinePlaceholder":187},[59,27750,27751,27753],{"class":61,"line":3201},[59,27752,25690],{"class":174},[59,27754,196],{"class":178},[59,27756,27757,27759],{"class":61,"line":3214},[59,27758,25697],{"class":174},[59,27760,196],{"class":178},[59,27762,27763,27765,27767],{"class":61,"line":3222},[59,27764,14064],{"class":174},[59,27766,179],{"class":178},[59,27768,25708],{"class":73},[59,27770,27771,27773],{"class":61,"line":3233},[59,27772,25713],{"class":174},[59,27774,196],{"class":178},[59,27776,27777,27779,27781],{"class":61,"line":3239},[59,27778,25720],{"class":174},[59,27780,179],{"class":178},[59,27782,3230],{"class":73},[59,27784,27785,27787,27789],{"class":61,"line":3247},[59,27786,25729],{"class":174},[59,27788,179],{"class":178},[59,27790,22591],{"class":73},[59,27792,27793,27795,27797],{"class":61,"line":3256},[59,27794,25738],{"class":174},[59,27796,179],{"class":178},[59,27798,14058],{"class":69},[59,27800,27801,27803],{"class":61,"line":3261},[59,27802,25747],{"class":174},[59,27804,196],{"class":178},[59,27806,27807,27809],{"class":61,"line":3269},[59,27808,25754],{"class":174},[59,27810,196],{"class":178},[59,27812,27813,27815],{"class":61,"line":3278},[59,27814,13829],{"class":178},[59,27816,25763],{"class":69},[59,27818,27819,27821],{"class":61,"line":3284},[59,27820,25768],{"class":174},[59,27822,196],{"class":178},[59,27824,27825,27827],{"class":61,"line":3289},[59,27826,25754],{"class":174},[59,27828,196],{"class":178},[59,27830,27831,27833],{"class":61,"line":3297},[59,27832,13829],{"class":178},[59,27834,25763],{"class":69},[59,27836,27837,27839],{"class":61,"line":3310},[59,27838,25787],{"class":174},[59,27840,196],{"class":178},[59,27842,27843,27845,27847],{"class":61,"line":3321},[59,27844,14064],{"class":174},[59,27846,179],{"class":178},[59,27848,25798],{"class":73},[59,27850,27851,27853],{"class":61,"line":3327},[59,27852,25713],{"class":174},[59,27854,196],{"class":178},[59,27856,27857,27859,27861],{"class":61,"line":3333},[59,27858,25720],{"class":174},[59,27860,179],{"class":178},[59,27862,3230],{"class":73},[59,27864,27865,27867,27869],{"class":61,"line":6115},[59,27866,25729],{"class":174},[59,27868,179],{"class":178},[59,27870,22617],{"class":73},[59,27872,27873,27875,27877],{"class":61,"line":6152},[59,27874,25738],{"class":174},[59,27876,179],{"class":178},[59,27878,14058],{"class":69},[59,27880,27881,27883],{"class":61,"line":6158},[59,27882,25833],{"class":174},[59,27884,196],{"class":178},[59,27886,27887,27889],{"class":61,"line":6164},[59,27888,25840],{"class":174},[59,27890,196],{"class":178},[59,27892,27893,27895],{"class":61,"line":6170},[59,27894,13829],{"class":178},[59,27896,25849],{"class":69},[59,27898,27899,27901],{"class":61,"line":6175},[59,27900,13829],{"class":178},[59,27902,25856],{"class":69},[59,27904,27905],{"class":61,"line":6619},[59,27906,25861],{"class":3773},[59,27908,27909,27911],{"class":61,"line":6625},[59,27910,25866],{"class":174},[59,27912,196],{"class":178},[59,27914,27915,27917,27919],{"class":61,"line":8974},[59,27916,25873],{"class":174},[59,27918,179],{"class":178},[59,27920,2962],{"class":73},[59,27922,27923,27925],{"class":61,"line":8979},[59,27924,25747],{"class":174},[59,27926,196],{"class":178},[59,27928,27929,27931],{"class":61,"line":8985},[59,27930,25754],{"class":174},[59,27932,196],{"class":178},[59,27934,27935,27937],{"class":61,"line":14030},[59,27936,13829],{"class":178},[59,27938,25763],{"class":69},[59,27940,27941,27943],{"class":61,"line":14039},[59,27942,25768],{"class":174},[59,27944,196],{"class":178},[59,27946,27947,27949],{"class":61,"line":14047},[59,27948,25754],{"class":174},[59,27950,196],{"class":178},[59,27952,27953,27955],{"class":61,"line":14061},[59,27954,13829],{"class":178},[59,27956,25763],{"class":69},[59,27958,27959],{"class":61,"line":14071},[59,27960,188],{"emptyLinePlaceholder":187},[59,27962,27963,27965],{"class":61,"line":14519},[59,27964,25922],{"class":174},[59,27966,196],{"class":178},[59,27968,27969,27971,27973],{"class":61,"line":14530},[59,27970,2957],{"class":174},[59,27972,179],{"class":178},[59,27974,3230],{"class":73},[59,27976,27977,27979,27981],{"class":61,"line":14538},[59,27978,25937],{"class":174},[59,27980,179],{"class":178},[59,27982,2962],{"class":73},[59,27984,27985,27987,27989],{"class":61,"line":14549},[59,27986,13682],{"class":174},[59,27988,179],{"class":178},[59,27990,25950],{"class":69},[59,27992,27993],{"class":61,"line":14555},[59,27994,188],{"emptyLinePlaceholder":187},[59,27996,27997,27999],{"class":61,"line":14560},[59,27998,25959],{"class":174},[59,28000,196],{"class":178},[59,28002,28003,28005],{"class":61,"line":14569},[59,28004,14050],{"class":178},[59,28006,25968],{"class":69},[59,28008,28009,28011],{"class":61,"line":14578},[59,28010,14050],{"class":178},[59,28012,25975],{"class":69},[59,28014,28015,28017],{"class":61,"line":14585},[59,28016,14050],{"class":178},[59,28018,25982],{"class":69},[59,28020,28021,28023],{"class":61,"line":14594},[59,28022,14050],{"class":178},[59,28024,25989],{"class":69},[59,28026,28027],{"class":61,"line":14601},[59,28028,188],{"emptyLinePlaceholder":187},[59,28030,28031,28033],{"class":61,"line":14608},[59,28032,1806],{"class":174},[59,28034,196],{"class":178},[59,28036,28037,28039],{"class":61,"line":14617},[59,28038,26004],{"class":174},[59,28040,196],{"class":178},[59,28042,28043,28045,28047],{"class":61,"line":14624},[59,28044,16930],{"class":174},[59,28046,179],{"class":178},[59,28048,3230],{"class":73},[59,28050,28051,28053],{"class":61,"line":14635},[59,28052,26019],{"class":174},[59,28054,196],{"class":178},[59,28056,28057,28059,28061],{"class":61,"line":14644},[59,28058,16930],{"class":174},[59,28060,179],{"class":178},[59,28062,3230],{"class":73},[59,28064,28065],{"class":61,"line":14653},[59,28066,188],{"emptyLinePlaceholder":187},[59,28068,28069,28071],{"class":61,"line":14658},[59,28070,26038],{"class":174},[59,28072,196],{"class":178},[59,28074,28075,28077,28079],{"class":61,"line":14667},[59,28076,26045],{"class":174},[59,28078,179],{"class":178},[59,28080,2962],{"class":73},[59,28082,28083,28085,28087],{"class":61,"line":14677},[59,28084,26054],{"class":174},[59,28086,179],{"class":178},[59,28088,2962],{"class":73},[59,28090,28091],{"class":61,"line":14684},[59,28092,188],{"emptyLinePlaceholder":187},[59,28094,28095,28097],{"class":61,"line":14693},[59,28096,26067],{"class":174},[59,28098,196],{"class":178},[59,28100,28101,28103],{"class":61,"line":14700},[59,28102,26074],{"class":174},[59,28104,196],{"class":178},[59,28106,28107,28109,28111],{"class":61,"line":14708},[59,28108,16930],{"class":174},[59,28110,179],{"class":178},[59,28112,3230],{"class":73},[59,28114,28115,28117],{"class":61,"line":14716},[59,28116,26089],{"class":174},[59,28118,196],{"class":178},[59,28120,28121,28123],{"class":61,"line":14724},[59,28122,26096],{"class":174},[59,28124,196],{"class":178},[59,28126,28127,28129,28131],{"class":61,"line":14732},[59,28128,26103],{"class":174},[59,28130,179],{"class":178},[59,28132,26108],{"class":69},[59,28134,28135],{"class":61,"line":26111},[59,28136,188],{"emptyLinePlaceholder":187},[59,28138,28139,28141],{"class":61,"line":26116},[59,28140,26119],{"class":174},[59,28142,196],{"class":178},[59,28144,28145,28147,28149,28151],{"class":61,"line":17786},[59,28146,14050],{"class":178},[59,28148,13655],{"class":174},[59,28150,179],{"class":178},[59,28152,26132],{"class":69},[59,28154,28155,28157,28159],{"class":61,"line":26135},[59,28156,26138],{"class":174},[59,28158,179],{"class":178},[59,28160,26143],{"class":69},[59,28162,28163,28165],{"class":61,"line":26146},[59,28164,13766],{"class":174},[59,28166,196],{"class":178},[59,28168,28169,28171,28173],{"class":61,"line":26153},[59,28170,26156],{"class":174},[59,28172,179],{"class":178},[59,28174,26161],{"class":69},[59,28176,28177,28179,28181],{"class":61,"line":26164},[59,28178,26167],{"class":174},[59,28180,179],{"class":178},[59,28182,25950],{"class":69},[59,28184,28185,28187],{"class":61,"line":26174},[59,28186,13789],{"class":174},[59,28188,196],{"class":178},[59,28190,28191,28193],{"class":61,"line":26181},[59,28192,26096],{"class":174},[59,28194,196],{"class":178},[59,28196,28197,28199],{"class":61,"line":26188},[59,28198,26191],{"class":174},[59,28200,196],{"class":178},[59,28202,28203,28205,28207],{"class":61,"line":26196},[59,28204,26199],{"class":174},[59,28206,179],{"class":178},[59,28208,26204],{"class":69},[59,28210,28211,28213,28215,28217],{"class":61,"line":26207},[59,28212,14050],{"class":178},[59,28214,13655],{"class":174},[59,28216,179],{"class":178},[59,28218,26132],{"class":69},[59,28220,28221,28223,28225],{"class":61,"line":26218},[59,28222,26138],{"class":174},[59,28224,179],{"class":178},[59,28226,26143],{"class":69},[59,28228,28229,28231],{"class":61,"line":26227},[59,28230,13766],{"class":174},[59,28232,196],{"class":178},[59,28234,28235,28237,28239],{"class":61,"line":26234},[59,28236,26156],{"class":174},[59,28238,179],{"class":178},[59,28240,26241],{"class":69},[59,28242,28243,28245,28247],{"class":61,"line":26244},[59,28244,26167],{"class":174},[59,28246,179],{"class":178},[59,28248,25950],{"class":69},[59,28250,28251,28253],{"class":61,"line":26253},[59,28252,13789],{"class":174},[59,28254,196],{"class":178},[59,28256,28257,28259],{"class":61,"line":26260},[59,28258,26263],{"class":174},[59,28260,196],{"class":178},[59,28262,28263,28265,28267],{"class":61,"line":26268},[59,28264,26271],{"class":174},[59,28266,179],{"class":178},[59,28268,26276],{"class":73},[14,28270,28271],{},"Update your kubeconfig file to point to the EKS cluster and set the correct context",[50,28273,28275],{"className":52,"code":28274,"language":54,"meta":55,"style":55},"aws eks update-kubeconfig --name $(terraform -chdir=eks output -raw cluster_name)\n",[18,28276,28277],{"__ignoreMap":55},[59,28278,28279,28281,28283,28286,28288,28291,28293,28295,28297,28300,28303],{"class":61,"line":62},[59,28280,19096],{"class":65},[59,28282,26603],{"class":69},[59,28284,28285],{"class":69}," update-kubeconfig",[59,28287,8447],{"class":73},[59,28289,28290],{"class":178}," $(",[59,28292,27306],{"class":65},[59,28294,27349],{"class":73},[59,28296,27503],{"class":69},[59,28298,28299],{"class":73}," -raw",[59,28301,28302],{"class":69}," cluster_name",[59,28304,5041],{"class":178},[14,28306,28307],{},"Install the Traefik Ingress controller with the following command:",[50,28309,28310],{"className":52,"code":26282,"language":54,"meta":55,"style":55},[18,28311,28312,28324,28332,28346,28352,28360,28368,28374],{"__ignoreMap":55},[59,28313,28314,28316,28318,28320,28322],{"class":61,"line":62},[59,28315,26289],{"class":65},[59,28317,26292],{"class":69},[59,28319,22570],{"class":69},[59,28321,26297],{"class":69},[59,28323,26300],{"class":69},[59,28325,28326,28328,28330],{"class":61,"line":77},[59,28327,26289],{"class":65},[59,28329,26292],{"class":69},[59,28331,23228],{"class":69},[59,28333,28334,28336,28338,28340,28342,28344],{"class":61,"line":88},[59,28335,26289],{"class":65},[59,28337,26315],{"class":69},[59,28339,26318],{"class":73},[59,28341,26297],{"class":69},[59,28343,26323],{"class":69},[59,28345,74],{"class":73},[59,28347,28348,28350],{"class":61,"line":99},[59,28349,26330],{"class":73},[59,28351,74],{"class":73},[59,28353,28354,28356,28358],{"class":61,"line":156},[59,28355,26337],{"class":73},[59,28357,26297],{"class":69},[59,28359,74],{"class":73},[59,28361,28362,28364,28366],{"class":61,"line":216},[59,28363,26346],{"class":73},[59,28365,26349],{"class":69},[59,28367,74],{"class":73},[59,28369,28370,28372],{"class":61,"line":224},[59,28371,26356],{"class":73},[59,28373,74],{"class":73},[59,28375,28376],{"class":61,"line":233},[59,28377,26363],{"class":73},[23,28379,28381],{"id":28380},"conclusion","Conclusion",[14,28383,28384,28385,273],{},"You have successfully set up the necessary AWS infrastructure to run the FlowFuse platform and performed minimal EKS cluster configuration required.\nWith this infrastructure in place, your environment is now ready for the installation of the FlowFuse platform. For detailed installation instructions, please visit: ",[41,28386,28388],{"href":28387},"\u002Fdocs\u002Finstall\u002Fkubernetes","FlowFuse Kubernetes Installation Guide",[316,28390,28391],{},"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 .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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":55,"searchDepth":77,"depth":77,"links":28393},[28394,28395,28396,28397,28399,28407,28408],{"id":25,"depth":77,"text":26},{"id":27065,"depth":77,"text":27066},{"id":27093,"depth":77,"text":27094},{"id":27187,"depth":77,"text":28398},"Step 3: Create the terraform.tfvars File",{"id":27283,"depth":77,"text":27284,"children":28400},[28401,28402,28403,28404,28405,28406],{"id":27292,"depth":88,"text":27293},{"id":27333,"depth":88,"text":27334},{"id":27372,"depth":88,"text":27373},{"id":27411,"depth":88,"text":27412},{"id":27450,"depth":88,"text":27451},{"id":27509,"depth":88,"text":27510},{"id":27572,"depth":77,"text":27573},{"id":28380,"depth":77,"text":28381},{},"AWS EKS Installation with Terraform and Helm","install\u002Fkubernetes\u002Faws_terraform.md","\u002Fdocs\u002Finstall\u002Fkubernetes\u002Faws_terraform",{"title":26947,"description":26955},"docs\u002Finstall\u002Fkubernetes\u002Faws_terraform","uMDIAmVAPiFyYFCEZb5HzQ3NPtDF_l9We2TeFYb5kmM",{"id":28417,"title":28418,"body":28419,"description":55,"extension":329,"layout":330,"meta":29732,"navGroup":330,"navOrder":330,"navTitle":29733,"navigation":187,"originalPath":29734,"path":29735,"redirect":330,"seo":29736,"stem":29737,"updated":337,"version":338,"__hash__":29738},"docs\u002Fdocs\u002Finstall\u002Fkubernetes\u002Fdigital-ocean.md","Installing FlowFuse on a Digital Ocean Kubernetes cluster",{"type":7,"value":28420,"toc":29719},[28421,28424,28426,28430,28437,28441,28457,28459,28462,28468,28472,28498,28505,28509,28515,29113,29115,29189,29193,29196,29223,29232,29236,29239,29327,29334,29345,29484,29490,29508,29512,29515,29541,29548,29551,29657,29667,29670,29711,29716],[10,28422,28418],{"id":28423},"installing-flowfuse-on-a-digital-ocean-kubernetes-cluster",[23,28425,26],{"id":25},[104,28427,28429],{"id":28428},"digital-ocean-account","Digital Ocean Account",[14,28431,28432,28433],{},"You will need an active Digital Oceans Account, you can sign up\nfor an account ",[41,28434,785],{"href":28435,"rel":28436},"https:\u002F\u002Fcloud.digitalocean.com\u002Fregistrations\u002Fnew",[831],[104,28438,28440],{"id":28439},"utilities","Utilities",[28,28442,28443,28450],{},[31,28444,28445,28446],{},"kubectl - ",[41,28447,28448],{"href":28448,"rel":28449},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Ftasks\u002Ftools\u002F",[831],[31,28451,28452,28453],{},"helm - ",[41,28454,28455],{"href":28455,"rel":28456},"https:\u002F\u002Fhelm.sh\u002Fdocs\u002Fintro\u002Finstall\u002F",[831],[104,28458,21035],{"id":21034},[14,28460,28461],{},"You will need a domain that FlowFuse will run on and access to configure\na wildcard entry for that domain in it's root DNS server.",[14,28463,28464,28465,28467],{},"In this guide I will use ",[18,28466,1312],{}," as the domain, remember to substitute your domain.",[23,28469,28471],{"id":28470},"create-cluster","Create Cluster",[28,28473,28474,28477,28480,28483,28486,28489,28495],{},[31,28475,28476],{},"Click on the big green \"Create\" button at the top of the screen",[31,28478,28479],{},"Select \"Kubernetes\" from the list",[31,28481,28482],{},"Pick a suitable region (normally the one physically closest to you)",[31,28484,28485],{},"Reduce the number of nodes from 3 to 2",[31,28487,28488],{},"Reduce the \"Node Plan\" to 1GB Ram\u002F2vCPU",[31,28490,28491,28492],{},"Change the cluster name to ",[18,28493,28494],{},"k8s-flowforge",[31,28496,28497],{},"Hit \"Create Cluster\" button",[14,28499,28500,28501,28504],{},"When the cluster has finished provisioning you should be able to download\nthe ",[18,28502,28503],{},"k8s-flowforge-kubeconfig.yaml"," file which will allow you to connect to the cluster.",[23,28506,28508],{"id":28507},"install-traefik","Install Traefik",[14,28510,28511,28512,28514],{},"Prepare values for the Traefik Helm chart by creating a file called ",[18,28513,25516],{}," with the following content:",[50,28516,28518],{"className":165,"code":28517,"language":167,"meta":55,"style":55},"service:\n  enabled: true\n  type: LoadBalancer\n  annotations:\n    service.beta.kubernetes.io\u002Fdo-loadbalancer-enable-proxy-protocol: \"true\"\n  spec:\n    externalTrafficPolicy: Cluster\n\ndeployment:\n  replicas: 2\n\nports:\n  web:\n    port: 8000\n    expose:\n      default: true\n    exposedPort: 80\n    protocol: TCP\n    forwardedHeaders:\n      trustedIPs:\n        - \"10.0.0.0\u002F8\"\n    proxyProtocol:\n      trustedIPs:\n        - \"10.0.0.0\u002F8\"\n  websecure:\n    port: 8443\n    expose:\n      default: true\n    exposedPort: 443\n    protocol: TCP\n    http:\n      middlewares:\n        - traefik-force-https@kubernetescrd\n        - traefik-large-body@kubernetescrd\n      # Disable TLS since NLB handles termination\n      tls:\n        enabled: false\n    forwardedHeaders:\n      trustedIPs:\n        - \"10.0.0.0\u002F8\"\n    proxyProtocol:\n      trustedIPs:\n        - \"10.0.0.0\u002F8\"\n\ningressClass:\n  enabled: true\n  isDefaultClass: false\n  name: traefik\n\nadditionalArguments:\n  - \"--entryPoints.web.proxyProtocol.insecure=true\"\n  - \"--entryPoints.websecure.proxyProtocol.insecure=true\"\n  - \"--entryPoints.web.forwardedHeaders.insecure=true\"\n  - \"--entryPoints.websecure.forwardedHeaders.insecure=true\"\n\nproviders:\n  kubernetesIngress:\n    enabled: true\n  kubernetesCRD:\n    enabled: true\n\napi:\n  dashboard: false\n  insecure: false\n\nlogs:\n  access:\n    enabled: true\n    fields:\n      headers:\n        defaultMode: keep\n\nextraObjects:\n  - apiVersion: traefik.io\u002Fv1alpha1\n    kind: Middleware\n    metadata:\n      name: force-https\n      namespace: traefik\n    spec:\n      headers:\n        customRequestHeaders:\n          X-Forwarded-Proto: \"https\"\n  - apiVersion: traefik.io\u002Fv1alpha1\n    kind: Middleware\n    metadata:\n      name: large-body\n      namespace: traefik\n    spec:\n      buffering:\n        maxRequestBodyBytes: 10485760\n",[18,28519,28520,28526,28534,28542,28548,28557,28563,28571,28575,28581,28589,28593,28599,28605,28613,28619,28627,28635,28643,28649,28655,28661,28667,28673,28679,28685,28693,28699,28707,28715,28723,28729,28735,28741,28747,28751,28757,28765,28771,28777,28783,28789,28795,28801,28805,28811,28819,28827,28835,28839,28845,28851,28857,28863,28869,28873,28879,28885,28893,28899,28907,28911,28917,28925,28933,28937,28943,28949,28957,28963,28969,28977,28981,28987,28997,29005,29011,29019,29027,29033,29039,29045,29053,29063,29071,29077,29085,29093,29099,29105],{"__ignoreMap":55},[59,28521,28522,28524],{"class":61,"line":62},[59,28523,9504],{"class":174},[59,28525,196],{"class":178},[59,28527,28528,28530,28532],{"class":61,"line":77},[59,28529,2957],{"class":174},[59,28531,179],{"class":178},[59,28533,3230],{"class":73},[59,28535,28536,28538,28540],{"class":61,"line":88},[59,28537,24266],{"class":174},[59,28539,179],{"class":178},[59,28541,25568],{"class":69},[59,28543,28544,28546],{"class":61,"line":99},[59,28545,25573],{"class":174},[59,28547,196],{"class":178},[59,28549,28550,28553,28555],{"class":61,"line":156},[59,28551,28552],{"class":174},"    service.beta.kubernetes.io\u002Fdo-loadbalancer-enable-proxy-protocol",[59,28554,179],{"class":178},[59,28556,25448],{"class":69},[59,28558,28559,28561],{"class":61,"line":216},[59,28560,25650],{"class":174},[59,28562,196],{"class":178},[59,28564,28565,28567,28569],{"class":61,"line":224},[59,28566,25657],{"class":174},[59,28568,179],{"class":178},[59,28570,25662],{"class":69},[59,28572,28573],{"class":61,"line":233},[59,28574,188],{"emptyLinePlaceholder":187},[59,28576,28577,28579],{"class":61,"line":241},[59,28578,25671],{"class":174},[59,28580,196],{"class":178},[59,28582,28583,28585,28587],{"class":61,"line":249},[59,28584,13715],{"class":174},[59,28586,179],{"class":178},[59,28588,3184],{"class":73},[59,28590,28591],{"class":61,"line":257},[59,28592,188],{"emptyLinePlaceholder":187},[59,28594,28595,28597],{"class":61,"line":3137},[59,28596,25690],{"class":174},[59,28598,196],{"class":178},[59,28600,28601,28603],{"class":61,"line":3150},[59,28602,25697],{"class":174},[59,28604,196],{"class":178},[59,28606,28607,28609,28611],{"class":61,"line":3163},[59,28608,14064],{"class":174},[59,28610,179],{"class":178},[59,28612,25708],{"class":73},[59,28614,28615,28617],{"class":61,"line":3176},[59,28616,25713],{"class":174},[59,28618,196],{"class":178},[59,28620,28621,28623,28625],{"class":61,"line":3187},[59,28622,25720],{"class":174},[59,28624,179],{"class":178},[59,28626,3230],{"class":73},[59,28628,28629,28631,28633],{"class":61,"line":3193},[59,28630,25729],{"class":174},[59,28632,179],{"class":178},[59,28634,22591],{"class":73},[59,28636,28637,28639,28641],{"class":61,"line":3201},[59,28638,25738],{"class":174},[59,28640,179],{"class":178},[59,28642,14058],{"class":69},[59,28644,28645,28647],{"class":61,"line":3214},[59,28646,25747],{"class":174},[59,28648,196],{"class":178},[59,28650,28651,28653],{"class":61,"line":3222},[59,28652,25754],{"class":174},[59,28654,196],{"class":178},[59,28656,28657,28659],{"class":61,"line":3233},[59,28658,13829],{"class":178},[59,28660,25763],{"class":69},[59,28662,28663,28665],{"class":61,"line":3239},[59,28664,25768],{"class":174},[59,28666,196],{"class":178},[59,28668,28669,28671],{"class":61,"line":3247},[59,28670,25754],{"class":174},[59,28672,196],{"class":178},[59,28674,28675,28677],{"class":61,"line":3256},[59,28676,13829],{"class":178},[59,28678,25763],{"class":69},[59,28680,28681,28683],{"class":61,"line":3261},[59,28682,25787],{"class":174},[59,28684,196],{"class":178},[59,28686,28687,28689,28691],{"class":61,"line":3269},[59,28688,14064],{"class":174},[59,28690,179],{"class":178},[59,28692,25798],{"class":73},[59,28694,28695,28697],{"class":61,"line":3278},[59,28696,25713],{"class":174},[59,28698,196],{"class":178},[59,28700,28701,28703,28705],{"class":61,"line":3284},[59,28702,25720],{"class":174},[59,28704,179],{"class":178},[59,28706,3230],{"class":73},[59,28708,28709,28711,28713],{"class":61,"line":3289},[59,28710,25729],{"class":174},[59,28712,179],{"class":178},[59,28714,22617],{"class":73},[59,28716,28717,28719,28721],{"class":61,"line":3297},[59,28718,25738],{"class":174},[59,28720,179],{"class":178},[59,28722,14058],{"class":69},[59,28724,28725,28727],{"class":61,"line":3310},[59,28726,25833],{"class":174},[59,28728,196],{"class":178},[59,28730,28731,28733],{"class":61,"line":3321},[59,28732,25840],{"class":174},[59,28734,196],{"class":178},[59,28736,28737,28739],{"class":61,"line":3327},[59,28738,13829],{"class":178},[59,28740,25849],{"class":69},[59,28742,28743,28745],{"class":61,"line":3333},[59,28744,13829],{"class":178},[59,28746,25856],{"class":69},[59,28748,28749],{"class":61,"line":6115},[59,28750,25861],{"class":3773},[59,28752,28753,28755],{"class":61,"line":6152},[59,28754,25866],{"class":174},[59,28756,196],{"class":178},[59,28758,28759,28761,28763],{"class":61,"line":6158},[59,28760,25873],{"class":174},[59,28762,179],{"class":178},[59,28764,2962],{"class":73},[59,28766,28767,28769],{"class":61,"line":6164},[59,28768,25747],{"class":174},[59,28770,196],{"class":178},[59,28772,28773,28775],{"class":61,"line":6170},[59,28774,25754],{"class":174},[59,28776,196],{"class":178},[59,28778,28779,28781],{"class":61,"line":6175},[59,28780,13829],{"class":178},[59,28782,25763],{"class":69},[59,28784,28785,28787],{"class":61,"line":6619},[59,28786,25768],{"class":174},[59,28788,196],{"class":178},[59,28790,28791,28793],{"class":61,"line":6625},[59,28792,25754],{"class":174},[59,28794,196],{"class":178},[59,28796,28797,28799],{"class":61,"line":8974},[59,28798,13829],{"class":178},[59,28800,25763],{"class":69},[59,28802,28803],{"class":61,"line":8979},[59,28804,188],{"emptyLinePlaceholder":187},[59,28806,28807,28809],{"class":61,"line":8985},[59,28808,25922],{"class":174},[59,28810,196],{"class":178},[59,28812,28813,28815,28817],{"class":61,"line":14030},[59,28814,2957],{"class":174},[59,28816,179],{"class":178},[59,28818,3230],{"class":73},[59,28820,28821,28823,28825],{"class":61,"line":14039},[59,28822,25937],{"class":174},[59,28824,179],{"class":178},[59,28826,2962],{"class":73},[59,28828,28829,28831,28833],{"class":61,"line":14047},[59,28830,13682],{"class":174},[59,28832,179],{"class":178},[59,28834,25950],{"class":69},[59,28836,28837],{"class":61,"line":14061},[59,28838,188],{"emptyLinePlaceholder":187},[59,28840,28841,28843],{"class":61,"line":14071},[59,28842,25959],{"class":174},[59,28844,196],{"class":178},[59,28846,28847,28849],{"class":61,"line":14519},[59,28848,14050],{"class":178},[59,28850,25968],{"class":69},[59,28852,28853,28855],{"class":61,"line":14530},[59,28854,14050],{"class":178},[59,28856,25975],{"class":69},[59,28858,28859,28861],{"class":61,"line":14538},[59,28860,14050],{"class":178},[59,28862,25982],{"class":69},[59,28864,28865,28867],{"class":61,"line":14549},[59,28866,14050],{"class":178},[59,28868,25989],{"class":69},[59,28870,28871],{"class":61,"line":14555},[59,28872,188],{"emptyLinePlaceholder":187},[59,28874,28875,28877],{"class":61,"line":14560},[59,28876,1806],{"class":174},[59,28878,196],{"class":178},[59,28880,28881,28883],{"class":61,"line":14569},[59,28882,26004],{"class":174},[59,28884,196],{"class":178},[59,28886,28887,28889,28891],{"class":61,"line":14578},[59,28888,16930],{"class":174},[59,28890,179],{"class":178},[59,28892,3230],{"class":73},[59,28894,28895,28897],{"class":61,"line":14585},[59,28896,26019],{"class":174},[59,28898,196],{"class":178},[59,28900,28901,28903,28905],{"class":61,"line":14594},[59,28902,16930],{"class":174},[59,28904,179],{"class":178},[59,28906,3230],{"class":73},[59,28908,28909],{"class":61,"line":14601},[59,28910,188],{"emptyLinePlaceholder":187},[59,28912,28913,28915],{"class":61,"line":14608},[59,28914,26038],{"class":174},[59,28916,196],{"class":178},[59,28918,28919,28921,28923],{"class":61,"line":14617},[59,28920,26045],{"class":174},[59,28922,179],{"class":178},[59,28924,2962],{"class":73},[59,28926,28927,28929,28931],{"class":61,"line":14624},[59,28928,26054],{"class":174},[59,28930,179],{"class":178},[59,28932,2962],{"class":73},[59,28934,28935],{"class":61,"line":14635},[59,28936,188],{"emptyLinePlaceholder":187},[59,28938,28939,28941],{"class":61,"line":14644},[59,28940,26067],{"class":174},[59,28942,196],{"class":178},[59,28944,28945,28947],{"class":61,"line":14653},[59,28946,26074],{"class":174},[59,28948,196],{"class":178},[59,28950,28951,28953,28955],{"class":61,"line":14658},[59,28952,16930],{"class":174},[59,28954,179],{"class":178},[59,28956,3230],{"class":73},[59,28958,28959,28961],{"class":61,"line":14667},[59,28960,26089],{"class":174},[59,28962,196],{"class":178},[59,28964,28965,28967],{"class":61,"line":14677},[59,28966,26096],{"class":174},[59,28968,196],{"class":178},[59,28970,28971,28973,28975],{"class":61,"line":14684},[59,28972,26103],{"class":174},[59,28974,179],{"class":178},[59,28976,26108],{"class":69},[59,28978,28979],{"class":61,"line":14693},[59,28980,188],{"emptyLinePlaceholder":187},[59,28982,28983,28985],{"class":61,"line":14700},[59,28984,26119],{"class":174},[59,28986,196],{"class":178},[59,28988,28989,28991,28993,28995],{"class":61,"line":14708},[59,28990,14050],{"class":178},[59,28992,13655],{"class":174},[59,28994,179],{"class":178},[59,28996,26132],{"class":69},[59,28998,28999,29001,29003],{"class":61,"line":14716},[59,29000,26138],{"class":174},[59,29002,179],{"class":178},[59,29004,26143],{"class":69},[59,29006,29007,29009],{"class":61,"line":14724},[59,29008,13766],{"class":174},[59,29010,196],{"class":178},[59,29012,29013,29015,29017],{"class":61,"line":14732},[59,29014,26156],{"class":174},[59,29016,179],{"class":178},[59,29018,26161],{"class":69},[59,29020,29021,29023,29025],{"class":61,"line":26111},[59,29022,26167],{"class":174},[59,29024,179],{"class":178},[59,29026,25950],{"class":69},[59,29028,29029,29031],{"class":61,"line":26116},[59,29030,13789],{"class":174},[59,29032,196],{"class":178},[59,29034,29035,29037],{"class":61,"line":17786},[59,29036,26096],{"class":174},[59,29038,196],{"class":178},[59,29040,29041,29043],{"class":61,"line":26135},[59,29042,26191],{"class":174},[59,29044,196],{"class":178},[59,29046,29047,29049,29051],{"class":61,"line":26146},[59,29048,26199],{"class":174},[59,29050,179],{"class":178},[59,29052,26204],{"class":69},[59,29054,29055,29057,29059,29061],{"class":61,"line":26153},[59,29056,14050],{"class":178},[59,29058,13655],{"class":174},[59,29060,179],{"class":178},[59,29062,26132],{"class":69},[59,29064,29065,29067,29069],{"class":61,"line":26164},[59,29066,26138],{"class":174},[59,29068,179],{"class":178},[59,29070,26143],{"class":69},[59,29072,29073,29075],{"class":61,"line":26174},[59,29074,13766],{"class":174},[59,29076,196],{"class":178},[59,29078,29079,29081,29083],{"class":61,"line":26181},[59,29080,26156],{"class":174},[59,29082,179],{"class":178},[59,29084,26241],{"class":69},[59,29086,29087,29089,29091],{"class":61,"line":26188},[59,29088,26167],{"class":174},[59,29090,179],{"class":178},[59,29092,25950],{"class":69},[59,29094,29095,29097],{"class":61,"line":26196},[59,29096,13789],{"class":174},[59,29098,196],{"class":178},[59,29100,29101,29103],{"class":61,"line":26207},[59,29102,26263],{"class":174},[59,29104,196],{"class":178},[59,29106,29107,29109,29111],{"class":61,"line":26218},[59,29108,26271],{"class":174},[59,29110,179],{"class":178},[59,29112,26276],{"class":73},[14,29114,26279],{},[50,29116,29118],{"className":52,"code":29117,"language":54,"meta":55,"style":55},"helm repo add traefik https:\u002F\u002Ftraefik.github.io\u002Fcharts\nhelm repo update\nhelm --kubeconfig=.\u002Fk8s-flowforge-kubeconfig.yaml upgrade --install traefik traefik\u002Ftraefik \\\n  --create-namespace \\\n  -n traefik \\\n  -f traefik-values.yaml \\\n  --wait \\\n  --atomic\n",[18,29119,29120,29132,29140,29157,29163,29171,29179,29185],{"__ignoreMap":55},[59,29121,29122,29124,29126,29128,29130],{"class":61,"line":62},[59,29123,26289],{"class":65},[59,29125,26292],{"class":69},[59,29127,22570],{"class":69},[59,29129,26297],{"class":69},[59,29131,26300],{"class":69},[59,29133,29134,29136,29138],{"class":61,"line":77},[59,29135,26289],{"class":65},[59,29137,26292],{"class":69},[59,29139,23228],{"class":69},[59,29141,29142,29144,29147,29149,29151,29153,29155],{"class":61,"line":88},[59,29143,26289],{"class":65},[59,29145,29146],{"class":73}," --kubeconfig=.\u002Fk8s-flowforge-kubeconfig.yaml",[59,29148,26315],{"class":69},[59,29150,26318],{"class":73},[59,29152,26297],{"class":69},[59,29154,26323],{"class":69},[59,29156,74],{"class":73},[59,29158,29159,29161],{"class":61,"line":99},[59,29160,26330],{"class":73},[59,29162,74],{"class":73},[59,29164,29165,29167,29169],{"class":61,"line":156},[59,29166,26337],{"class":73},[59,29168,26297],{"class":69},[59,29170,74],{"class":73},[59,29172,29173,29175,29177],{"class":61,"line":216},[59,29174,26346],{"class":73},[59,29176,26349],{"class":69},[59,29178,74],{"class":73},[59,29180,29181,29183],{"class":61,"line":224},[59,29182,26356],{"class":73},[59,29184,74],{"class":73},[59,29186,29187],{"class":61,"line":233},[59,29188,26363],{"class":73},[104,29190,29192],{"id":29191},"setup-dns","Setup DNS",[14,29194,29195],{},"Run the following to get the external IP address of the Traefik controller",[50,29197,29199],{"className":52,"code":29198,"language":54,"meta":55,"style":55},"kubectl --kubeconfig=.\u002Fk8s-flowforge-kubeconfig.yaml \\\n  -n traefik get service traefik\n",[18,29200,29201,29209],{"__ignoreMap":55},[59,29202,29203,29205,29207],{"class":61,"line":62},[59,29204,13627],{"class":65},[59,29206,29146],{"class":73},[59,29208,74],{"class":73},[59,29210,29211,29213,29215,29218,29220],{"class":61,"line":77},[59,29212,26337],{"class":73},[59,29214,26297],{"class":69},[59,29216,29217],{"class":69}," get",[59,29219,9801],{"class":69},[59,29221,29222],{"class":69}," traefik\n",[14,29224,29225,29226,29228,29229],{},"You will need to update the entry in your DNS server to point\n",[18,29227,21530],{}," to the IP address listed under ",[18,29230,29231],{},"EXTERNAL-IP",[23,29233,29235],{"id":29234},"install-cert-manager","Install Cert-manager",[14,29237,29238],{},"This will use LetsEncrypt to issue certificates for both the FlowFuse application\nbut also for the Node-RED instances.",[50,29240,29242],{"className":52,"code":29241,"language":54,"meta":55,"style":55},"helm repo add jetstack https:\u002F\u002Fcharts.jetstack.io\nhelm repo update\nhelm install \\\n  --kubeconfig=.\u002Fk8s-flowforge-kubeconfig.yaml \\\n  cert-manager jetstack\u002Fcert-manager \\\n  --namespace cert-manager \\\n  --create-namespace \\\n  --version v1.13.3 \\\n  --set installCRDs=true\n",[18,29243,29244,29258,29266,29274,29281,29291,29301,29307,29317],{"__ignoreMap":55},[59,29245,29246,29248,29250,29252,29255],{"class":61,"line":62},[59,29247,26289],{"class":65},[59,29249,26292],{"class":69},[59,29251,22570],{"class":69},[59,29253,29254],{"class":69}," jetstack",[59,29256,29257],{"class":69}," https:\u002F\u002Fcharts.jetstack.io\n",[59,29259,29260,29262,29264],{"class":61,"line":77},[59,29261,26289],{"class":65},[59,29263,26292],{"class":69},[59,29265,23228],{"class":69},[59,29267,29268,29270,29272],{"class":61,"line":88},[59,29269,26289],{"class":65},[59,29271,7956],{"class":69},[59,29273,74],{"class":73},[59,29275,29276,29279],{"class":61,"line":99},[59,29277,29278],{"class":73},"  --kubeconfig=.\u002Fk8s-flowforge-kubeconfig.yaml",[59,29280,74],{"class":73},[59,29282,29283,29286,29289],{"class":61,"line":156},[59,29284,29285],{"class":69},"  cert-manager",[59,29287,29288],{"class":69}," jetstack\u002Fcert-manager",[59,29290,74],{"class":73},[59,29292,29293,29296,29299],{"class":61,"line":216},[59,29294,29295],{"class":73},"  --namespace",[59,29297,29298],{"class":69}," cert-manager",[59,29300,74],{"class":73},[59,29302,29303,29305],{"class":61,"line":224},[59,29304,26330],{"class":73},[59,29306,74],{"class":73},[59,29308,29309,29312,29315],{"class":61,"line":233},[59,29310,29311],{"class":73},"  --version",[59,29313,29314],{"class":69}," v1.13.3",[59,29316,74],{"class":73},[59,29318,29319,29322,29325],{"class":61,"line":241},[59,29320,29321],{"class":73},"  --set",[59,29323,29324],{"class":69}," installCRDs=",[59,29326,3230],{"class":73},[14,29328,29329,29330,29333],{},"After installing you will need to create a ",[18,29331,29332],{},"ClusterIssuer"," to access LetsEncrypt.",[14,29335,29336,29337,29340,29341,29344],{},"Create the following YAML file called ",[18,29338,29339],{},"letsencrypt.yml",", please replace\n",[18,29342,29343],{},"user@example.com"," with your email address.",[50,29346,29348],{"className":165,"code":29347,"language":167,"meta":55,"style":55},"apiVersion: cert-manager.io\u002Fv1\nkind: ClusterIssuer\nmetadata:\n  name: letsencrypt\nspec:\n  acme:\n    # The ACME server URL\n    server: https:\u002F\u002Facme-v02.api.letsencrypt.org\u002Fdirectory\n    # Email address used for ACME registration\n    email: user@example.com\n    # Name of a secret used to store the ACME account private key\n    privateKeySecretRef:\n      name: letsencrypt-prod\n    # Enable the HTTP-01 challenge provider\n    solvers:\n      - http01:\n          ingress:\n            ingressClassName: traefik\n",[18,29349,29350,29359,29368,29374,29383,29389,29396,29401,29411,29416,29426,29431,29438,29447,29452,29459,29468,29475],{"__ignoreMap":55},[59,29351,29352,29354,29356],{"class":61,"line":62},[59,29353,13655],{"class":174},[59,29355,179],{"class":178},[59,29357,29358],{"class":69},"cert-manager.io\u002Fv1\n",[59,29360,29361,29363,29365],{"class":61,"line":77},[59,29362,13665],{"class":174},[59,29364,179],{"class":178},[59,29366,29367],{"class":69},"ClusterIssuer\n",[59,29369,29370,29372],{"class":61,"line":88},[59,29371,13675],{"class":174},[59,29373,196],{"class":178},[59,29375,29376,29378,29380],{"class":61,"line":99},[59,29377,13682],{"class":174},[59,29379,179],{"class":178},[59,29381,29382],{"class":69},"letsencrypt\n",[59,29384,29385,29387],{"class":61,"line":156},[59,29386,13708],{"class":174},[59,29388,196],{"class":178},[59,29390,29391,29394],{"class":61,"line":216},[59,29392,29393],{"class":174},"  acme",[59,29395,196],{"class":178},[59,29397,29398],{"class":61,"line":224},[59,29399,29400],{"class":3773},"    # The ACME server URL\n",[59,29402,29403,29406,29408],{"class":61,"line":233},[59,29404,29405],{"class":174},"    server",[59,29407,179],{"class":178},[59,29409,29410],{"class":69},"https:\u002F\u002Facme-v02.api.letsencrypt.org\u002Fdirectory\n",[59,29412,29413],{"class":61,"line":241},[59,29414,29415],{"class":3773},"    # Email address used for ACME registration\n",[59,29417,29418,29421,29423],{"class":61,"line":249},[59,29419,29420],{"class":174},"    email",[59,29422,179],{"class":178},[59,29424,29425],{"class":69},"user@example.com\n",[59,29427,29428],{"class":61,"line":257},[59,29429,29430],{"class":3773},"    # Name of a secret used to store the ACME account private key\n",[59,29432,29433,29436],{"class":61,"line":3137},[59,29434,29435],{"class":174},"    privateKeySecretRef",[59,29437,196],{"class":178},[59,29439,29440,29442,29444],{"class":61,"line":3150},[59,29441,26156],{"class":174},[59,29443,179],{"class":178},[59,29445,29446],{"class":69},"letsencrypt-prod\n",[59,29448,29449],{"class":61,"line":3163},[59,29450,29451],{"class":3773},"    # Enable the HTTP-01 challenge provider\n",[59,29453,29454,29457],{"class":61,"line":3176},[59,29455,29456],{"class":174},"    solvers",[59,29458,196],{"class":178},[59,29460,29461,29463,29466],{"class":61,"line":3187},[59,29462,227],{"class":178},[59,29464,29465],{"class":174},"http01",[59,29467,196],{"class":178},[59,29469,29470,29473],{"class":61,"line":3193},[59,29471,29472],{"class":174},"          ingress",[59,29474,196],{"class":178},[59,29476,29477,29480,29482],{"class":61,"line":3201},[59,29478,29479],{"class":174},"            ingressClassName",[59,29481,179],{"class":178},[59,29483,25950],{"class":69},[14,29485,29486,29487,29489],{},"Then use ",[18,29488,13627],{}," to install this",[50,29491,29493],{"className":52,"code":29492,"language":54,"meta":55,"style":55},"kubectl --kubeconfig=.\u002Fk8s-flowforge-kubeconfig.yaml apply -f letsencrypt.yml\n",[18,29494,29495],{"__ignoreMap":55},[59,29496,29497,29499,29501,29503,29505],{"class":61,"line":62},[59,29498,13627],{"class":65},[59,29500,29146],{"class":73},[59,29502,22488],{"class":69},[59,29504,13111],{"class":73},[59,29506,29507],{"class":69}," letsencrypt.yml\n",[23,29509,29511],{"id":29510},"install-flowfuse","Install FlowFuse",[14,29513,29514],{},"Then setup the FlowFuse Helm repository",[50,29516,29518],{"className":52,"code":29517,"language":54,"meta":55,"style":55},"helm repo add flowforge https:\u002F\u002Fflowfuse.github.io\u002Fhelm\nhelm repo update\n",[18,29519,29520,29533],{"__ignoreMap":55},[59,29521,29522,29524,29526,29528,29530],{"class":61,"line":62},[59,29523,26289],{"class":65},[59,29525,26292],{"class":69},[59,29527,22570],{"class":69},[59,29529,9507],{"class":69},[59,29531,29532],{"class":69}," https:\u002F\u002Fflowfuse.github.io\u002Fhelm\n",[59,29534,29535,29537,29539],{"class":61,"line":77},[59,29536,26289],{"class":65},[59,29538,26292],{"class":69},[59,29540,23228],{"class":69},[14,29542,29543,29544,29547],{},"Now create a ",[18,29545,29546],{},"customizations.yml"," file. This is how we configure the\nFlowFuse instance.",[14,29549,29550],{},"The following is the bare minimum to get started:",[50,29552,29554],{"className":165,"code":29553,"language":167,"meta":55,"style":55},"forge:\n  domain: example.com\n  https: true\n  localPostgresql: true\n  projectSelector: \n  managementSelector: \n  broker:\n    enabled: true\npostgresql:\n  global:\n    storageClass: do-block-storage\ningress:\n  certManagerIssuer: letsencrypt\n",[18,29555,29556,29562,29572,29581,29590,29597,29604,29611,29619,29625,29632,29641,29648],{"__ignoreMap":55},[59,29557,29558,29560],{"class":61,"line":62},[59,29559,22305],{"class":174},[59,29561,196],{"class":178},[59,29563,29564,29567,29569],{"class":61,"line":77},[59,29565,29566],{"class":174},"  domain",[59,29568,179],{"class":178},[59,29570,29571],{"class":69},"example.com\n",[59,29573,29574,29577,29579],{"class":61,"line":88},[59,29575,29576],{"class":174},"  https",[59,29578,179],{"class":178},[59,29580,3230],{"class":73},[59,29582,29583,29586,29588],{"class":61,"line":99},[59,29584,29585],{"class":174},"  localPostgresql",[59,29587,179],{"class":178},[59,29589,3230],{"class":73},[59,29591,29592,29595],{"class":61,"line":156},[59,29593,29594],{"class":174},"  projectSelector",[59,29596,14289],{"class":178},[59,29598,29599,29602],{"class":61,"line":216},[59,29600,29601],{"class":174},"  managementSelector",[59,29603,14289],{"class":178},[59,29605,29606,29609],{"class":61,"line":224},[59,29607,29608],{"class":174},"  broker",[59,29610,196],{"class":178},[59,29612,29613,29615,29617],{"class":61,"line":233},[59,29614,16930],{"class":174},[59,29616,179],{"class":178},[59,29618,3230],{"class":73},[59,29620,29621,29623],{"class":61,"line":241},[59,29622,24651],{"class":174},[59,29624,196],{"class":178},[59,29626,29627,29630],{"class":61,"line":249},[59,29628,29629],{"class":174},"  global",[59,29631,196],{"class":178},[59,29633,29634,29636,29638],{"class":61,"line":257},[59,29635,23960],{"class":174},[59,29637,179],{"class":178},[59,29639,29640],{"class":69},"do-block-storage\n",[59,29642,29643,29646],{"class":61,"line":3137},[59,29644,29645],{"class":174},"ingress",[59,29647,196],{"class":178},[59,29649,29650,29653,29655],{"class":61,"line":3150},[59,29651,29652],{"class":174},"  certManagerIssuer",[59,29654,179],{"class":178},[59,29656,29382],{"class":69},[14,29658,29659,29660,29662,29663,29666],{},"Again, please replace ",[18,29661,1312],{}," with the domain you configured\nearlier in the ",[41,29664,29192],{"href":29665},"#setup-dns"," section",[14,29668,29669],{},"Then we use this to install FlowFuse",[50,29671,29673],{"className":52,"code":29672,"language":54,"meta":55,"style":55},"helm upgrade --install --kubeconfig .\u002Fk8s-flowforge-kubeconfig.yaml \\\n  flowforge flowforge\u002Fflowforge -f customizations.yml \\\n  --wait\n",[18,29674,29675,29691,29706],{"__ignoreMap":55},[59,29676,29677,29679,29681,29683,29686,29689],{"class":61,"line":62},[59,29678,26289],{"class":65},[59,29680,26315],{"class":69},[59,29682,26318],{"class":73},[59,29684,29685],{"class":73}," --kubeconfig",[59,29687,29688],{"class":69}," .\u002Fk8s-flowforge-kubeconfig.yaml",[59,29690,74],{"class":73},[59,29692,29693,29696,29699,29701,29704],{"class":61,"line":77},[59,29694,29695],{"class":69},"  flowforge",[59,29697,29698],{"class":69}," flowforge\u002Fflowforge",[59,29700,13111],{"class":73},[59,29702,29703],{"class":69}," customizations.yml",[59,29705,74],{"class":73},[59,29707,29708],{"class":61,"line":88},[59,29709,29710],{"class":73},"  --wait\n",[14,29712,29713,29714],{},"Once complete you should be able to sign into the FlowFuse setup wizard\nat ",[18,29715,21536],{},[316,29717,29718],{},"html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}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}",{"title":55,"searchDepth":77,"depth":77,"links":29720},[29721,29726,29727,29730,29731],{"id":25,"depth":77,"text":26,"children":29722},[29723,29724,29725],{"id":28428,"depth":88,"text":28429},{"id":28439,"depth":88,"text":28440},{"id":21034,"depth":88,"text":21035},{"id":28470,"depth":77,"text":28471},{"id":28507,"depth":77,"text":28508,"children":29728},[29729],{"id":29191,"depth":88,"text":29192},{"id":29234,"depth":77,"text":29235},{"id":29510,"depth":77,"text":29511},{},"Digital Ocean Kubernetes Installation","install\u002Fkubernetes\u002Fdigital-ocean.md","\u002Fdocs\u002Finstall\u002Fkubernetes\u002Fdigital-ocean",{"title":28418,"description":55},"docs\u002Finstall\u002Fkubernetes\u002Fdigital-ocean","pSeSvJvnrs9MolcrSWMVW5ZdaS5le269ZIQzruarMQ4",{"id":29740,"title":55,"body":29741,"description":55,"extension":329,"layout":330,"meta":31288,"navGroup":330,"navOrder":330,"navTitle":31289,"navigation":187,"originalPath":31290,"path":28387,"redirect":330,"seo":31291,"stem":31292,"updated":337,"version":338,"__hash__":31293},"docs\u002Fdocs\u002Finstall\u002Fkubernetes\u002Findex.md",{"type":7,"value":29742,"toc":31263},[29743,29746,29748,29751,29753,29788,29790,29792,29863,29865,29881,29885,29888,29891,29894,29896,29903,29913,29921,29925,29927,29931,29956,29960,29966,29975,30024,30032,30036,30039,30042,30045,30059,30062,30089,30092,30117,30123,30146,30149,30155,30186,30188,30190,30194,30201,30203,30212,30214,30218,30221,30230,30241,30262,30268,30272,30282,30310,30320,30324,30327,30336,30454,30457,30459,30462,30468,30550,30554,30563,30567,30583,30596,30969,30971,30976,30992,30995,31074,31078,31082,31132,31136,31146,31156,31198,31202,31206,31211,31237,31241,31248,31252,31260],[17936,29744,29745],{},"     \n    class ChecklistItem extends HTMLElement {\n\n       static observedAttributes = [\"type\", \"task\"];\n\n       constructor() {\n          super();   \n          this.type = 'required'\n          this.task = ''\n       }\n\n       attributeChangedCallback(name, oldValue, newValue) {\n         if (name === \"type\") {\n             this.type = newValue;\n         } else if (name === \"task\") {\n             this.task = newValue;\n         }\n       }\n\n       connectedCallback () {\n         const iconRequired = `\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\">\u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z\" \u002F>\u003C\u002Fsvg>`\n         const iconRecommended = `\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\">\u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 12.75 11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 0 1-1.043 3.296 3.745 3.745 0 0 1-3.296 1.043A3.745 3.745 0 0 1 12 21c-1.268 0-2.39-.63-3.068-1.593a3.746 3.746 0 0 1-3.296-1.043 3.745 3.745 0 0 1-1.043-3.296A3.745 3.745 0 0 1 3 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 0 1 1.043-3.296 3.746 3.746 0 0 1 3.296-1.043A3.746 3.746 0 0 1 12 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 0 1 3.296 1.043 3.746 3.746 0 0 1 1.043 3.296A3.745 3.745 0 0 1 21 12Z\" \u002F>\u003C\u002Fsvg>`\n         const iconOptional = `\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\">\u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M8.625 12a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H8.25m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H12m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0h-.375M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z\" \u002F>\u003C\u002Fsvg>`\n\n         let icon = iconRequired\n         let tooltip = \"Required for Operation\"\n         if (this.type === 'recommended') {\n           icon = iconRecommended\n           tooltip = \"Recommended for Production\"\n         } else if (this.type === 'optional') {\n           icon = iconOptional\n           tooltip = \"Optional\"\n         }\n         this.innerHTML = `\u003Cdiv class=\"checklist-item checklist-item--${this.type}\">\u003Cspan class=\"tooltip\" data-tooltip=\"${tooltip}\">\u003Cspan class=\"checklist-item-status\">${icon}\u003C\u002Fspan>\u003Cspan>${this.task}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fdiv>`\n       }\n    }\n\n    customElements.define('checklist-item', ChecklistItem);\n ",[10,29747,13556],{"id":13561},[14,29749,29750],{},"This guide walks you through detailed set up of FlowFuse Platform on a container envoronment managed by Kubernetes. Typically suited for large on premise deployments or deployment in Cloud infrastructure.\nBy the end, you will have a fully functioning FlowFuse instance running on a Kubernetes cluster.",[10,29752,21369],{"id":21368},[17958,29754,17964,29756,17964,29774,29787],{"className":29755},[21373,21374,21375],[17958,29757,29759,29760,29759,29762,17964],{"className":29758},[21368],"\n     ",[17972,29761,26],{},[17958,29763,29764,29765,29764,29767,29764,29770,29764,29772,29759],{},"\n       ",[21386,29766],{"task":21388},[21386,29768],{"task":29769},"Kubernetes cluster",[21386,29771],{"type":21394,"task":21395},[21386,29773],{"type":21394,"task":21398},[17958,29775,29759,29777,29759,29779,17964],{"className":29776},[21368],[17972,29778,13547],{},[17958,29780,29764,29781,29764,29783,29764,29785,29759],{},[21386,29782],{"task":21408},[21386,29784],{"task":21411},[21386,29786],{"type":21394,"task":21414},"\n ",[23,29789,26],{"id":25},[14,29791,21419],{},[398,29793,29794,29800,29812,29823,29843,29853],{},[31,29795,29796,21427,29798,660],{},[364,29797,21426],{},[41,29799,21035],{"href":21430},[31,29801,29802,29805,29806,29808,29809],{},[364,29803,29804],{},"kubectl:"," To manage a Kubernetes cluster you will need a copy of the ",[18,29807,13627],{}," utility. Instructions on how to install it can be found ",[41,29810,785],{"href":28448,"rel":29811},[831],[31,29813,29814,29817,29818],{},[364,29815,29816],{},"Helm:"," FlowFuse provides the Helm chart to manage platform deployment. Installation can be done through the instructions on ",[41,29819,29822],{"href":29820,"rel":29821},"https:\u002F\u002Fhelm.sh",[831],"their website",[31,29824,29825,29828,29829],{},[364,29826,29827],{},"Kubernetes Cluster:"," The deployment has currently been tested on the following environments:\n",[28,29830,29831,29836,29840],{},[31,29832,29833],{},[41,29834,29835],{"href":28412},"AWS EKS",[31,29837,29838],{},[41,29839,20355],{"href":29735},[31,29841,29842],{},"MicroK8s",[31,29844,29845,3497,29848,29852],{},[364,29846,29847],{},"Ingress Controller:",[41,29849,29851],{"href":25508,"rel":29850},[831],"The Traefik"," installed on the cluster.",[31,29854,29855,29858,29859],{},[364,29856,29857],{},"EMQX Operator:"," This is required to install the required MQTT broker when the Team Broker features are enabled. Instructions for installing the operator can be found ",[41,29860,785],{"href":29861,"rel":29862},"https:\u002F\u002Fdocs.emqx.com\u002Fen\u002Femqx-operator\u002Flatest\u002Fgetting-started\u002Fgetting-started.html#install-emqx-operator",[831],[14,29864,21452],{},[28,29866,29867,29873],{},[31,29868,29869,21460,29871,21465],{},[364,29870,21459],{},[41,29872,21464],{"href":21463},[31,29874,29875,21471,29878,660],{},[364,29876,29877],{},"TLS Certificate:",[41,29879,21414],{"href":29880},"#i-would-like-to-secure-the-platform-with-https%2C-how-can-i-do-that%3F",[104,29882,29884],{"id":29883},"hardware-requirements","Hardware requirements",[14,29886,29887],{},"For a Kubernetes-based deployment, resource requirements depend on the number of FlowFuse and Node-RED instances running. As a baseline, we suggest:",[14,29889,29890],{},"Control Plane: At least 2 vCPUs, 4 GB RAM\nWorker Nodes: Minimum 2 vCPUs, 4 GB RAM per node, 2 nodes for high availability\nStorage: 20Gb of host storage (for container images), StorageClass of your choice available for Hosted Node-RED instances (optional)",[14,29892,29893],{},"Each Node-RED instance you host will uses 0.1 CPU cores and 256 MB of memory by default. This parameters can be adjusted in admin area of FlowFuse platform. Keep this in mind when sizing your hardware, especially if plan to create multiple hosted instances.",[104,29895,21035],{"id":21034},[14,29897,36,29898,29902],{},[41,29899,29901],{"href":20313,"rel":29900},[831],"wildcard DNS entry"," will be needed\nto point to the domain that is used for the project instances. This will need to point\nto the kubernetes Ingress controller.",[14,29904,29905,29906,29909,29910,29912],{},"For example if you want projects to be accessible as ",[18,29907,29908],{},"[instance-name].example.com","\nyou will need to ensure that ",[18,29911,21530],{}," is mapped to the IP address used by\nyour Kubernetes clusters's Ingress controller.",[14,29914,29915,29916,29918,29919,273],{},"By default the FlowFuse application will be mapped to ",[18,29917,21970],{}," assuming\nthat you set the domain to ",[18,29920,1312],{},[14,29922,21546,29923,273],{},[41,29924,785],{"href":21008},[23,29926,9164],{"id":9163},[104,29928,29930],{"id":29929},"add-flowfuse-helm-repository","Add FlowFuse Helm Repository",[50,29932,29934],{"className":52,"code":29933,"language":54,"meta":55,"style":55},"helm repo add flowfuse https:\u002F\u002Fflowfuse.github.io\u002Fhelm\nhelm repo update\n",[18,29935,29936,29948],{"__ignoreMap":55},[59,29937,29938,29940,29942,29944,29946],{"class":61,"line":62},[59,29939,26289],{"class":65},[59,29941,26292],{"class":69},[59,29943,22570],{"class":69},[59,29945,21914],{"class":69},[59,29947,29532],{"class":69},[59,29949,29950,29952,29954],{"class":61,"line":77},[59,29951,26289],{"class":65},[59,29953,26292],{"class":69},[59,29955,23228],{"class":69},[104,29957,29959],{"id":29958},"customize-helm-chart","Customize Helm Chart",[14,29961,29962,29963,29965],{},"All the initial configuration is handled by the Helm chart. This is done by creating a ",[18,29964,24102],{}," file that will be passed to the Helm along with the chart.",[14,29967,29968,29969,29971,29972,29974],{},"To create ",[18,29970,24102],{}," file with a minimal required configuration (replace ",[18,29973,1312],{}," with your domain):",[50,29976,29978],{"className":52,"code":29977,"language":54,"meta":55,"style":55},"cat \u003C\u003CEOF > customization.yml\nforge:\n  entryPoint: forge.example.com\n  domain: example.com\n  https: false\n  localPostgresql: true\nEOF\n",[18,29979,29980,29995,30000,30005,30010,30015,30020],{"__ignoreMap":55},[59,29981,29982,29985,29987,29990,29992],{"class":61,"line":62},[59,29983,29984],{"class":65},"cat",[59,29986,20850],{"class":1372},[59,29988,29989],{"class":69},"EOF",[59,29991,20464],{"class":1372},[59,29993,29994],{"class":69}," customization.yml\n",[59,29996,29997],{"class":61,"line":77},[59,29998,29999],{"class":69},"forge:\n",[59,30001,30002],{"class":61,"line":88},[59,30003,30004],{"class":69},"  entryPoint: forge.example.com\n",[59,30006,30007],{"class":61,"line":99},[59,30008,30009],{"class":69},"  domain: example.com\n",[59,30011,30012],{"class":61,"line":156},[59,30013,30014],{"class":69},"  https: false\n",[59,30016,30017],{"class":61,"line":216},[59,30018,30019],{"class":69},"  localPostgresql: true\n",[59,30021,30022],{"class":61,"line":224},[59,30023,20853],{"class":69},[14,30025,30026,30027,273],{},"A full list of all the configuration options can be found in the ",[41,30028,30031],{"href":30029,"rel":30030},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fhelm\u002Fblob\u002Fmain\u002Fhelm\u002Fflowfuse\u002FREADME.md#configuration-values",[831],"Helm Chart README",[104,30033,30035],{"id":30034},"label-nodes","Label Nodes",[14,30037,30038],{},"By default FlowFuse platform expects that Kubernetes nodes have specific labels applied. The main reason behind this approach is to separate core application components from Node-RED instances.",[14,30040,30041],{},"You will need to label at least one node to run the management application and one to run the Node-RED Projects:",[14,30043,30044],{},"List all nodes in the cluster:",[50,30046,30048],{"className":52,"code":30047,"language":54,"meta":55,"style":55},"kubectl get nodes\n",[18,30049,30050],{"__ignoreMap":55},[59,30051,30052,30054,30056],{"class":61,"line":62},[59,30053,13627],{"class":65},[59,30055,29217],{"class":69},[59,30057,30058],{"class":69}," nodes\n",[14,30060,30061],{},"Label management nodes:",[50,30063,30065],{"className":52,"code":30064,"language":54,"meta":55,"style":55},"kubectl label node \u003Cmanagement-node-name> role=management\n",[18,30066,30067],{"__ignoreMap":55},[59,30068,30069,30071,30074,30077,30079,30082,30084,30086],{"class":61,"line":62},[59,30070,13627],{"class":65},[59,30072,30073],{"class":69}," label",[59,30075,30076],{"class":69}," node",[59,30078,12322],{"class":1372},[59,30080,30081],{"class":69},"management-node-nam",[59,30083,12328],{"class":178},[59,30085,12732],{"class":1372},[59,30087,30088],{"class":69}," role=management\n",[14,30090,30091],{},"Label project nodes:",[50,30093,30095],{"className":52,"code":30094,"language":54,"meta":55,"style":55},"kubectl label node \u003Cprojects-node-name> role=projects\n",[18,30096,30097],{"__ignoreMap":55},[59,30098,30099,30101,30103,30105,30107,30110,30112,30114],{"class":61,"line":62},[59,30100,13627],{"class":65},[59,30102,30073],{"class":69},[59,30104,30076],{"class":69},[59,30106,12322],{"class":1372},[59,30108,30109],{"class":69},"projects-node-nam",[59,30111,12328],{"class":178},[59,30113,12732],{"class":1372},[59,30115,30116],{"class":69}," role=projects\n",[14,30118,30119,30120,30122],{},"To override this behavior, you can remove the node selectors with the following entry in the ",[18,30121,24102],{}," file which will mean that all pods can run on any nodes.",[50,30124,30126],{"className":165,"code":30125,"language":167,"meta":55,"style":55},"forge:\n  projectSelector:\n  managementSelector:\n",[18,30127,30128,30134,30140],{"__ignoreMap":55},[59,30129,30130,30132],{"class":61,"line":62},[59,30131,22305],{"class":174},[59,30133,196],{"class":178},[59,30135,30136,30138],{"class":61,"line":77},[59,30137,29594],{"class":174},[59,30139,196],{"class":178},[59,30141,30142,30144],{"class":61,"line":88},[59,30143,29601],{"class":174},[59,30145,196],{"class":178},[23,30147,30148],{"id":21873},"Start FlowFuse Platform",[14,30150,30151,30152,30154],{},"Once you have the ",[18,30153,24102],{}," file created, you can install FlowFuse using our Helm chart. This will automatically create all required objects and start services:",[50,30156,30158],{"className":52,"code":30157,"language":54,"meta":55,"style":55},"helm upgrade --atomic --install --timeout 10m flowfuse flowfuse\u002Fflowfuse -f customization.yml\n",[18,30159,30160],{"__ignoreMap":55},[59,30161,30162,30164,30166,30169,30171,30174,30177,30179,30182,30184],{"class":61,"line":62},[59,30163,26289],{"class":65},[59,30165,26315],{"class":69},[59,30167,30168],{"class":73}," --atomic",[59,30170,26318],{"class":73},[59,30172,30173],{"class":73}," --timeout",[59,30175,30176],{"class":69}," 10m",[59,30178,21914],{"class":69},[59,30180,30181],{"class":69}," flowfuse\u002Fflowfuse",[59,30183,13111],{"class":73},[59,30185,29994],{"class":69},[23,30187,584],{"id":9513},[14,30189,21982],{},[14,30191,21985,30192,273],{},[41,30193,2587],{"href":21164},[14,30195,30196,30197,273],{},"Once you have finished setting up the admin user there are some ",[41,30198,30200],{"href":30199},"#common-questions","Kubernetes specific items to consider",[23,30202,9856],{"id":9855},[14,30204,30205,30206,30211],{},"All technical aspects of the upgrade process of Flowfuse application running on Kubernetes and managed by Helm chart are maintained in our repository.\nPlease refer to the ",[41,30207,30210],{"href":30208,"rel":30209},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fhelm\u002Fblob\u002Fmain\u002Fhelm\u002Fflowfuse\u002FREADME.md#upgrading-chart",[831],"Flowfuse Helm Chart documentation"," for more details\nabout the upgrade process.",[23,30213,22178],{"id":22177},[104,30215,30217],{"id":30216},"i-would-like-to-secure-the-platform-with-https-how-can-i-do-that","I would like to secure the platform with HTTPS, how can I do that?",[14,30219,30220],{},"In cloud environments, it is recommended to use a Load Balancer to terminate SSL traffic.",[14,30222,30223,30224,30229],{},"However, if you want to use SSL termination on the Kubernetes Ingress Controller, this is possible by utilizing ",[41,30225,30228],{"href":30226,"rel":30227},"https:\u002F\u002Fcert-manager.io\u002Fdocs\u002F",[831],"Cert-Manager"," tool (not part of the FlowFuse Helm chart).",[14,30231,30232,30233,30235,30236,30240],{},"Once you have Cert-Manager installed, you can enable TLS support in the ",[18,30234,24102],{}," file by specifying the ",[41,30237,29332],{"href":30238,"rel":30239},"https:\u002F\u002Fcert-manager.io\u002Fdocs\u002Fconfiguration\u002F#cluster-resource-namespace",[831]," name:",[50,30242,30244],{"className":165,"code":30243,"language":167,"meta":55,"style":55},"ingress:\n  clusterIssuer: \u003Cyour-cluster-issuer>\n",[18,30245,30246,30252],{"__ignoreMap":55},[59,30247,30248,30250],{"class":61,"line":62},[59,30249,29645],{"class":174},[59,30251,196],{"class":178},[59,30253,30254,30257,30259],{"class":61,"line":77},[59,30255,30256],{"class":174},"  clusterIssuer",[59,30258,179],{"class":178},[59,30260,30261],{"class":69},"\u003Cyour-cluster-issuer>\n",[14,30263,30264,30265,273],{},"Apply changes with ",[41,30266,30267],{"href":21703},"platform startup command",[768,30269,30271],{"id":30270},"using-internalprivate-certificate-authorities","Using Internal\u002FPrivate Certificate Authorities",[14,30273,30274,30275,30277,30278,273],{},"If you are issuing your own HTTPS certificates (rather than using a Public Certificate Authority) you will need to tell the Hosted Node-RED Instances to trust this CA. To do this you need to pass base64 encoded CA certificate chain to the helm chart in the ",[18,30276,24102],{},". Details are in the Helm chart ",[41,30279,18566],{"href":30280,"rel":30281},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fhelm\u002Fblob\u002Fmain\u002Fhelm\u002Fflowfuse\u002FREADME.md#private-certificate-authority",[831],[50,30283,30285],{"className":165,"code":30284,"language":167,"meta":55,"style":55},"forge:\n  privateCA:\n    certs: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk...\n",[18,30286,30287,30293,30300],{"__ignoreMap":55},[59,30288,30289,30291],{"class":61,"line":62},[59,30290,22305],{"class":174},[59,30292,196],{"class":178},[59,30294,30295,30298],{"class":61,"line":77},[59,30296,30297],{"class":174},"  privateCA",[59,30299,196],{"class":178},[59,30301,30302,30305,30307],{"class":61,"line":88},[59,30303,30304],{"class":174},"    certs",[59,30306,179],{"class":178},[59,30308,30309],{"class":69},"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk...\n",[14,30311,30312,30313,30316,30317],{},"Assuming the certificate chain is held in ",[18,30314,30315],{},"chain.pem"," the value would be created by running ",[18,30318,30319],{},"base64 -w 0 certs.pem",[104,30321,30323],{"id":30322},"i-use-kubernetes-network-policies-how-can-i-configure-them","I use Kubernetes Network Policies, how can I configure them?",[14,30325,30326],{},"If your cluster uses Network Policies to restrict traffic between namespaces, you'll need to create appropriate policies.",[14,30328,30329,30330,30332,30333,30335],{},"Here's an example Network Policy that allows traffic from the ",[18,30331,6303],{}," namespace (default namespace for Node-RED instances) to the ",[18,30334,7855],{}," namespace:",[50,30337,30339],{"className":165,"code":30338,"language":167,"meta":55,"style":55},"apiVersion: networking.k8s.io\u002Fv1\nkind: NetworkPolicy\nmetadata:\n  name: allow-flowforge-to-flowfuse\n  namespace: flowfuse\nspec:\n  podSelector: {}\n  ingress:\n    - from:\n      - namespaceSelector:\n          matchLabels:\n            kubernetes.io\u002Fmetadata.name: flowforge\n  policyTypes:\n    - Ingress\n",[18,30340,30341,30350,30359,30365,30374,30384,30390,30398,30405,30414,30423,30430,30440,30447],{"__ignoreMap":55},[59,30342,30343,30345,30347],{"class":61,"line":62},[59,30344,13655],{"class":174},[59,30346,179],{"class":178},[59,30348,30349],{"class":69},"networking.k8s.io\u002Fv1\n",[59,30351,30352,30354,30356],{"class":61,"line":77},[59,30353,13665],{"class":174},[59,30355,179],{"class":178},[59,30357,30358],{"class":69},"NetworkPolicy\n",[59,30360,30361,30363],{"class":61,"line":88},[59,30362,13675],{"class":174},[59,30364,196],{"class":178},[59,30366,30367,30369,30371],{"class":61,"line":99},[59,30368,13682],{"class":174},[59,30370,179],{"class":178},[59,30372,30373],{"class":69},"allow-flowforge-to-flowfuse\n",[59,30375,30376,30379,30381],{"class":61,"line":156},[59,30377,30378],{"class":174},"  namespace",[59,30380,179],{"class":178},[59,30382,30383],{"class":69},"flowfuse\n",[59,30385,30386,30388],{"class":61,"line":216},[59,30387,13708],{"class":174},[59,30389,196],{"class":178},[59,30391,30392,30395],{"class":61,"line":224},[59,30393,30394],{"class":174},"  podSelector",[59,30396,30397],{"class":178},": {}\n",[59,30399,30400,30403],{"class":61,"line":233},[59,30401,30402],{"class":174},"  ingress",[59,30404,196],{"class":178},[59,30406,30407,30410,30412],{"class":61,"line":241},[59,30408,30409],{"class":178},"    - ",[59,30411,15793],{"class":174},[59,30413,196],{"class":178},[59,30415,30416,30418,30421],{"class":61,"line":249},[59,30417,227],{"class":178},[59,30419,30420],{"class":174},"namespaceSelector",[59,30422,196],{"class":178},[59,30424,30425,30428],{"class":61,"line":257},[59,30426,30427],{"class":174},"          matchLabels",[59,30429,196],{"class":178},[59,30431,30432,30435,30437],{"class":61,"line":3137},[59,30433,30434],{"class":174},"            kubernetes.io\u002Fmetadata.name",[59,30436,179],{"class":178},[59,30438,30439],{"class":69},"flowforge\n",[59,30441,30442,30445],{"class":61,"line":3150},[59,30443,30444],{"class":174},"  policyTypes",[59,30446,196],{"class":178},[59,30448,30449,30451],{"class":61,"line":3163},[59,30450,30409],{"class":178},[59,30452,30453],{"class":69},"Ingress\n",[14,30455,30456],{},"You may need to adjust this policy based on your specific network requirements and namespace configuration.",[104,30458,22182],{"id":22181},[14,30460,30461],{},"FlowFuse platform uses PostgreSQL database to store its data. By default, the internal database instance is created and managed by the Helm chart.",[14,30463,30464,30465,30467],{},"If you want to use an external database server, you need to edit ",[18,30466,24102],{}," file and provide the database connection details:",[50,30469,30471],{"className":165,"code":30470,"language":167,"meta":55,"style":55},"forge:\n  localPostgresql: false # Disable internal database\npostgresql:\n  host: \u003Cdatabase-host>\n  port: \u003Cdatabase-port>\n  auth:\n    username: \u003Cdatabase-username>\n    password: \u003Cdatabase-password>\n    database: \u003Cdatabase-name>\n",[18,30472,30473,30479,30490,30496,30506,30516,30523,30532,30541],{"__ignoreMap":55},[59,30474,30475,30477],{"class":61,"line":62},[59,30476,22305],{"class":174},[59,30478,196],{"class":178},[59,30480,30481,30483,30485,30487],{"class":61,"line":77},[59,30482,29585],{"class":174},[59,30484,179],{"class":178},[59,30486,659],{"class":73},[59,30488,30489],{"class":3773}," # Disable internal database\n",[59,30491,30492,30494],{"class":61,"line":88},[59,30493,24651],{"class":174},[59,30495,196],{"class":178},[59,30497,30498,30501,30503],{"class":61,"line":99},[59,30499,30500],{"class":174},"  host",[59,30502,179],{"class":178},[59,30504,30505],{"class":69},"\u003Cdatabase-host>\n",[59,30507,30508,30511,30513],{"class":61,"line":156},[59,30509,30510],{"class":174},"  port",[59,30512,179],{"class":178},[59,30514,30515],{"class":69},"\u003Cdatabase-port>\n",[59,30517,30518,30521],{"class":61,"line":216},[59,30519,30520],{"class":174},"  auth",[59,30522,196],{"class":178},[59,30524,30525,30527,30529],{"class":61,"line":224},[59,30526,24817],{"class":174},[59,30528,179],{"class":178},[59,30530,30531],{"class":69},"\u003Cdatabase-username>\n",[59,30533,30534,30536,30538],{"class":61,"line":233},[59,30535,24826],{"class":174},[59,30537,179],{"class":178},[59,30539,30540],{"class":69},"\u003Cdatabase-password>\n",[59,30542,30543,30545,30547],{"class":61,"line":241},[59,30544,24807],{"class":174},[59,30546,179],{"class":178},[59,30548,30549],{"class":69},"\u003Cdatabase-name>\n",[14,30551,30264,30552,273],{},[41,30553,30267],{"href":21703},[14,30555,30556,30557,30562],{},"Check the ",[41,30558,30561],{"href":30559,"rel":30560},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fhelm\u002Ftree\u002Fmain\u002Fhelm\u002Fflowforge#postgresql",[831],"FlowFuse Helm chart documentation"," for more details about the parameters that can be configured for the PostgreSQL database.",[104,30564,30566],{"id":30565},"how-to-backup-embedded-database","How to backup embedded database?",[14,30568,30569,30570,30573,30574,30576,30577,30582],{},"If you are using the internal database (value ",[18,30571,30572],{},"forge.localPostgresql"," set to ",[18,30575,3558],{},"), you can use Kubernetes ",[41,30578,30581],{"href":30579,"rel":30580},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Fconcepts\u002Fworkloads\u002Fcontrollers\u002Fcron-jobs\u002F",[831],"CronJobs"," to backup the database.",[14,30584,30585,30586,302,30589,30592,30593,3939],{},"Apply below ",[18,30587,30588],{},"CronJob",[18,30590,30591],{},"PersistentVolumeClaim"," definitions to create a backup job which will be executed every day at 23:05 and store the backup in a PVC named ",[18,30594,30595],{},"db-backup-pvc",[50,30597,30599],{"className":165,"code":30598,"language":167,"meta":55,"style":55},"apiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n  name: db-backup-pvc\nspec:\n  accessModes:\n    - ReadWriteOnce\n  resources:\n    requests:\n      storage: 5Gi\n---\napiVersion: batch\u002Fv1\nkind: CronJob\nmetadata:\n  name: postgres-backup\nspec:\n  schedule: \"5 23 * * *\"\n  jobTemplate:\n    spec:\n      ttlSecondsAfterFinished: 60\n      template:\n        metadata:\n          labels:\n            app: flowforge\n        spec:\n          containers:\n          - name: backup\n            image: postgres\n            env:\n            - name: PGPASSWORD\n              valueFrom:\n                secretKeyRef:\n                  name: flowfuse-postgresql\n                  key: postgres-password\n            command:\n            - \u002Fbin\u002Fsh\n            - -c\n            - |\n              pg_dump -h flowfuse-postgresql -U postgres -d flowforge -F c -b -v -f \u002Fbackup\u002Fdb_backup.dump\n            volumeMounts:\n            - name: backup-volume\n              mountPath: \u002Fbackup\n          restartPolicy: OnFailure\n          volumes:\n          - name: backup-volume\n            persistentVolumeClaim:\n              claimName: db-backup-pvc\n",[18,30600,30601,30609,30617,30623,30632,30638,30644,30650,30656,30662,30671,30675,30684,30693,30699,30708,30714,30724,30731,30737,30747,30754,30761,30768,30777,30784,30791,30803,30812,30819,30831,30838,30845,30855,30865,30872,30879,30886,30893,30898,30905,30916,30926,30936,30943,30953,30960],{"__ignoreMap":55},[59,30602,30603,30605,30607],{"class":61,"line":62},[59,30604,13655],{"class":174},[59,30606,179],{"class":178},[59,30608,13991],{"class":69},[59,30610,30611,30613,30615],{"class":61,"line":77},[59,30612,13665],{"class":174},[59,30614,179],{"class":178},[59,30616,14674],{"class":69},[59,30618,30619,30621],{"class":61,"line":88},[59,30620,13675],{"class":174},[59,30622,196],{"class":178},[59,30624,30625,30627,30629],{"class":61,"line":99},[59,30626,13682],{"class":174},[59,30628,179],{"class":178},[59,30630,30631],{"class":69},"db-backup-pvc\n",[59,30633,30634,30636],{"class":61,"line":156},[59,30635,13708],{"class":174},[59,30637,196],{"class":178},[59,30639,30640,30642],{"class":61,"line":216},[59,30641,14703],{"class":174},[59,30643,196],{"class":178},[59,30645,30646,30648],{"class":61,"line":224},[59,30647,30409],{"class":178},[59,30649,14713],{"class":69},[59,30651,30652,30654],{"class":61,"line":233},[59,30653,14719],{"class":174},[59,30655,196],{"class":178},[59,30657,30658,30660],{"class":61,"line":241},[59,30659,14727],{"class":174},[59,30661,196],{"class":178},[59,30663,30664,30666,30668],{"class":61,"line":249},[59,30665,14735],{"class":174},[59,30667,179],{"class":178},[59,30669,30670],{"class":69},"5Gi\n",[59,30672,30673],{"class":61,"line":257},[59,30674,13982],{"class":65},[59,30676,30677,30679,30681],{"class":61,"line":3137},[59,30678,13655],{"class":174},[59,30680,179],{"class":178},[59,30682,30683],{"class":69},"batch\u002Fv1\n",[59,30685,30686,30688,30690],{"class":61,"line":3150},[59,30687,13665],{"class":174},[59,30689,179],{"class":178},[59,30691,30692],{"class":69},"CronJob\n",[59,30694,30695,30697],{"class":61,"line":3163},[59,30696,13675],{"class":174},[59,30698,196],{"class":178},[59,30700,30701,30703,30705],{"class":61,"line":3176},[59,30702,13682],{"class":174},[59,30704,179],{"class":178},[59,30706,30707],{"class":69},"postgres-backup\n",[59,30709,30710,30712],{"class":61,"line":3187},[59,30711,13708],{"class":174},[59,30713,196],{"class":178},[59,30715,30716,30719,30721],{"class":61,"line":3193},[59,30717,30718],{"class":174},"  schedule",[59,30720,179],{"class":178},[59,30722,30723],{"class":69},"\"5 23 * * *\"\n",[59,30725,30726,30729],{"class":61,"line":3201},[59,30727,30728],{"class":174},"  jobTemplate",[59,30730,196],{"class":178},[59,30732,30733,30735],{"class":61,"line":3214},[59,30734,13789],{"class":174},[59,30736,196],{"class":178},[59,30738,30739,30742,30744],{"class":61,"line":3222},[59,30740,30741],{"class":174},"      ttlSecondsAfterFinished",[59,30743,179],{"class":178},[59,30745,30746],{"class":73},"60\n",[59,30748,30749,30752],{"class":61,"line":3233},[59,30750,30751],{"class":174},"      template",[59,30753,196],{"class":178},[59,30755,30756,30759],{"class":61,"line":3239},[59,30757,30758],{"class":174},"        metadata",[59,30760,196],{"class":178},[59,30762,30763,30766],{"class":61,"line":3247},[59,30764,30765],{"class":174},"          labels",[59,30767,196],{"class":178},[59,30769,30770,30773,30775],{"class":61,"line":3256},[59,30771,30772],{"class":174},"            app",[59,30774,179],{"class":178},[59,30776,30439],{"class":69},[59,30778,30779,30782],{"class":61,"line":3261},[59,30780,30781],{"class":174},"        spec",[59,30783,196],{"class":178},[59,30785,30786,30789],{"class":61,"line":3269},[59,30787,30788],{"class":174},"          containers",[59,30790,196],{"class":178},[59,30792,30793,30796,30798,30800],{"class":61,"line":3278},[59,30794,30795],{"class":178},"          - ",[59,30797,5782],{"class":174},[59,30799,179],{"class":178},[59,30801,30802],{"class":69},"backup\n",[59,30804,30805,30808,30810],{"class":61,"line":3284},[59,30806,30807],{"class":174},"            image",[59,30809,179],{"class":178},[59,30811,8523],{"class":69},[59,30813,30814,30817],{"class":61,"line":3289},[59,30815,30816],{"class":174},"            env",[59,30818,196],{"class":178},[59,30820,30821,30824,30826,30828],{"class":61,"line":3297},[59,30822,30823],{"class":178},"            - ",[59,30825,5782],{"class":174},[59,30827,179],{"class":178},[59,30829,30830],{"class":69},"PGPASSWORD\n",[59,30832,30833,30836],{"class":61,"line":3310},[59,30834,30835],{"class":174},"              valueFrom",[59,30837,196],{"class":178},[59,30839,30840,30843],{"class":61,"line":3321},[59,30841,30842],{"class":174},"                secretKeyRef",[59,30844,196],{"class":178},[59,30846,30847,30850,30852],{"class":61,"line":3327},[59,30848,30849],{"class":174},"                  name",[59,30851,179],{"class":178},[59,30853,30854],{"class":69},"flowfuse-postgresql\n",[59,30856,30857,30860,30862],{"class":61,"line":3333},[59,30858,30859],{"class":174},"                  key",[59,30861,179],{"class":178},[59,30863,30864],{"class":69},"postgres-password\n",[59,30866,30867,30870],{"class":61,"line":6115},[59,30868,30869],{"class":174},"            command",[59,30871,196],{"class":178},[59,30873,30874,30876],{"class":61,"line":6152},[59,30875,30823],{"class":178},[59,30877,30878],{"class":69},"\u002Fbin\u002Fsh\n",[59,30880,30881,30883],{"class":61,"line":6158},[59,30882,30823],{"class":178},[59,30884,30885],{"class":69},"-c\n",[59,30887,30888,30890],{"class":61,"line":6164},[59,30889,30823],{"class":178},[59,30891,30892],{"class":1372},"|\n",[59,30894,30895],{"class":61,"line":6170},[59,30896,30897],{"class":69},"              pg_dump -h flowfuse-postgresql -U postgres -d flowforge -F c -b -v -f \u002Fbackup\u002Fdb_backup.dump\n",[59,30899,30900,30903],{"class":61,"line":6175},[59,30901,30902],{"class":174},"            volumeMounts",[59,30904,196],{"class":178},[59,30906,30907,30909,30911,30913],{"class":61,"line":6619},[59,30908,30823],{"class":178},[59,30910,5782],{"class":174},[59,30912,179],{"class":178},[59,30914,30915],{"class":69},"backup-volume\n",[59,30917,30918,30921,30923],{"class":61,"line":6625},[59,30919,30920],{"class":174},"              mountPath",[59,30922,179],{"class":178},[59,30924,30925],{"class":69},"\u002Fbackup\n",[59,30927,30928,30931,30933],{"class":61,"line":8974},[59,30929,30930],{"class":174},"          restartPolicy",[59,30932,179],{"class":178},[59,30934,30935],{"class":69},"OnFailure\n",[59,30937,30938,30941],{"class":61,"line":8979},[59,30939,30940],{"class":174},"          volumes",[59,30942,196],{"class":178},[59,30944,30945,30947,30949,30951],{"class":61,"line":8985},[59,30946,30795],{"class":178},[59,30948,5782],{"class":174},[59,30950,179],{"class":178},[59,30952,30915],{"class":69},[59,30954,30955,30958],{"class":61,"line":14030},[59,30956,30957],{"class":174},"            persistentVolumeClaim",[59,30959,196],{"class":178},[59,30961,30962,30965,30967],{"class":61,"line":14039},[59,30963,30964],{"class":174},"              claimName",[59,30966,179],{"class":178},[59,30968,30631],{"class":69},[104,30970,22377],{"id":22376},[14,30972,30973,30974,17479],{},"FlowFuse platform allows you to invite team members to the platform using their e-mail addresses.\nTo enable this feature, you need to configure the e-mail settings in the ",[18,30975,24102],{},[14,30977,30978,30979,30983,30984,30989,30990,17479],{},"Check this ",[41,30980,30982],{"href":30981},"\u002Fdocs\u002Finstall\u002Fconfiguration#email-configuration","page"," for more details about the parameters.\nCheck ",[41,30985,30988],{"href":30986,"rel":30987},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fhelm\u002Ftree\u002Fmain\u002Fhelm\u002Fflowfuse#email",[831],"FlowFuseHelm chart documentation"," for information where configuration values should be placed in ",[18,30991,24102],{},[14,30993,30994],{},"If you use AWS EKS (Elastic Kubernetes Service) and want to use AWS SES (Simple Email Service) for sending e-mails, you need to provide the IAM role with the required permissions to use SES.",[50,30996,30998],{"className":165,"code":30997,"language":167,"meta":55,"style":55},"forge:\n  entryPoint: forge.example.com\n  domain: example.com\n  cloudProvider: aws\n  aws:\n    IAMRole: arn:aws:iam::\u003Caws-account-id>:role\u002Fflowforge_service_account_role\n  email:\n    ses:\n      region: eu-west-1\n",[18,30999,31000,31006,31016,31024,31034,31041,31051,31058,31065],{"__ignoreMap":55},[59,31001,31002,31004],{"class":61,"line":62},[59,31003,22305],{"class":174},[59,31005,196],{"class":178},[59,31007,31008,31011,31013],{"class":61,"line":77},[59,31009,31010],{"class":174},"  entryPoint",[59,31012,179],{"class":178},[59,31014,31015],{"class":69},"forge.example.com\n",[59,31017,31018,31020,31022],{"class":61,"line":88},[59,31019,29566],{"class":174},[59,31021,179],{"class":178},[59,31023,29571],{"class":69},[59,31025,31026,31029,31031],{"class":61,"line":99},[59,31027,31028],{"class":174},"  cloudProvider",[59,31030,179],{"class":178},[59,31032,31033],{"class":69},"aws\n",[59,31035,31036,31039],{"class":61,"line":156},[59,31037,31038],{"class":174},"  aws",[59,31040,196],{"class":178},[59,31042,31043,31046,31048],{"class":61,"line":216},[59,31044,31045],{"class":174},"    IAMRole",[59,31047,179],{"class":178},[59,31049,31050],{"class":69},"arn:aws:iam::\u003Caws-account-id>:role\u002Fflowforge_service_account_role\n",[59,31052,31053,31056],{"class":61,"line":224},[59,31054,31055],{"class":174},"  email",[59,31057,196],{"class":178},[59,31059,31060,31063],{"class":61,"line":233},[59,31061,31062],{"class":174},"    ses",[59,31064,196],{"class":178},[59,31066,31067,31070,31072],{"class":61,"line":241},[59,31068,31069],{"class":174},"      region",[59,31071,179],{"class":178},[59,31073,25245],{"class":69},[14,31075,30264,31076,273],{},[41,31077,30267],{"href":21703},[104,31079,31081],{"id":31080},"i-would-like-to-use-embeded-mqtt-broker-how-can-i-do-that","I would like to use embeded MQTT broker, how can I do that?",[16433,31083,21376,31084,31088,31091,31096,31121,31125],{},[31085,31086,31087],"summary",{},"Click to expand",[14,31089,31090],{},"The FlowFuse Helm chart provides the MQTT broker service.",[14,31092,31093,31094,21627],{},"To enable the MQTT broker you need to add the following to the ",[18,31095,24102],{},[50,31097,31099],{"className":165,"code":31098,"language":167,"meta":55,"style":55},"forge:\n  broker:\n    enabled: true\n",[18,31100,31101,31107,31113],{"__ignoreMap":55},[59,31102,31103,31105],{"class":61,"line":62},[59,31104,22305],{"class":174},[59,31106,196],{"class":178},[59,31108,31109,31111],{"class":61,"line":77},[59,31110,29608],{"class":174},[59,31112,196],{"class":178},[59,31114,31115,31117,31119],{"class":61,"line":88},[59,31116,16930],{"class":174},[59,31118,179],{"class":178},[59,31120,3230],{"class":73},[14,31122,30264,31123,273],{},[41,31124,30267],{"href":21703},[14,31126,30556,31127,31131],{},[41,31128,30561],{"href":31129,"rel":31130},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fhelm\u002Ftree\u002Fmain\u002Fhelm\u002Fflowfuse#mqtt-broker",[831]," for more details about the parameters that can be configured for the MQTT broker.",[104,31133,31135],{"id":31134},"i-would-like-to-use-kubernetes-persistent-storage-to-store-data-how-can-i-do-that","I would like to use Kubernetes Persistent storage to store data, how can I do that?",[14,31137,31138,31139,31142,31143,31145],{},"Starting with the ",[18,31140,31141],{},"2.6.0"," release the Pods running the Node-RED Instances have a Persistent Volume mounted on ",[18,31144,23789],{}," in which files can be written.\nThese files will persist for the lifetime of the Instance including across Susspend\u002FResume and Stack upgrades.",[14,31147,31148,31149,31151,31152],{},"To enable this feature the following configuration needs to be added to the ",[18,31150,24102],{}," file (replace '",[31153,31154,31155],"storage-class-name",{},"' with the name of the StorageClass you have in the cluster):",[50,31157,31159],{"className":165,"code":31158,"language":167,"meta":55,"style":55},"forge:\n  persistentStorage:\n    enabled: true\n    size: 5Gi\n    storageClass: \u003Cstorage-class-name>\n",[18,31160,31161,31167,31173,31181,31189],{"__ignoreMap":55},[59,31162,31163,31165],{"class":61,"line":62},[59,31164,22305],{"class":174},[59,31166,196],{"class":178},[59,31168,31169,31171],{"class":61,"line":77},[59,31170,23945],{"class":174},[59,31172,196],{"class":178},[59,31174,31175,31177,31179],{"class":61,"line":88},[59,31176,16930],{"class":174},[59,31178,179],{"class":178},[59,31180,3230],{"class":73},[59,31182,31183,31185,31187],{"class":61,"line":99},[59,31184,23970],{"class":174},[59,31186,179],{"class":178},[59,31188,30670],{"class":69},[59,31190,31191,31193,31195],{"class":61,"line":156},[59,31192,23960],{"class":174},[59,31194,179],{"class":178},[59,31196,31197],{"class":69},"\u003Cstorage-class-name>\n",[14,31199,30264,31200,273],{},[41,31201,30267],{"href":21703},[104,31203,31205],{"id":31204},"i-would-like-to-use-flowfuse-file-storage-to-store-context-data-how-can-i-do-that","I would like to use FlowFuse File Storage to store context data, how can I do that?",[14,31207,31208,31209,21627],{},"To enable the FlowFuse File Storage component add the following to the ",[18,31210,24102],{},[50,31212,31214],{"className":165,"code":31213,"language":167,"meta":55,"style":55},"forge:\n  fileStore:\n    enabled: true\n",[18,31215,31216,31222,31229],{"__ignoreMap":55},[59,31217,31218,31220],{"class":61,"line":62},[59,31219,22305],{"class":174},[59,31221,196],{"class":178},[59,31223,31224,31227],{"class":61,"line":77},[59,31225,31226],{"class":174},"  fileStore",[59,31228,196],{"class":178},[59,31230,31231,31233,31235],{"class":61,"line":88},[59,31232,16930],{"class":174},[59,31234,179],{"class":178},[59,31236,3230],{"class":73},[14,31238,30264,31239,273],{},[41,31240,30267],{"href":21703},[14,31242,30556,31243,31247],{},[41,31244,30561],{"href":31245,"rel":31246},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fhelm\u002Ftree\u002Fmain\u002Fhelm\u002Fflowfuse#file-storage",[831]," for more details about the parameters that can be configured for the File Storage.",[104,31249,31251],{"id":31250},"i-would-like-to-run-flowfuse-on-aws-eks-do-you-have-any-guidance","I would like to run FlowFuse on AWS EKS. Do you have any guidance?",[14,31253,31254,31255,31257,31258,273],{},"Yes, we have a dedicated guide on how to deploy FlowFuse on AWS EKS. You can find it ",[41,31256,785],{"href":26941},".\nFurthermore, we also provide terraform scripts to automate the deployment process of all required AWS service. You can find the guide ",[41,31259,785],{"href":28412},[316,31261,31262],{},"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);}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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":55,"searchDepth":77,"depth":77,"links":31264},[31265,31269,31274,31275,31276,31277],{"id":25,"depth":77,"text":26,"children":31266},[31267,31268],{"id":29883,"depth":88,"text":29884},{"id":21034,"depth":88,"text":21035},{"id":9163,"depth":77,"text":9164,"children":31270},[31271,31272,31273],{"id":29929,"depth":88,"text":29930},{"id":29958,"depth":88,"text":29959},{"id":30034,"depth":88,"text":30035},{"id":21873,"depth":77,"text":30148},{"id":9513,"depth":77,"text":584},{"id":9855,"depth":77,"text":9856},{"id":22177,"depth":77,"text":22178,"children":31278},[31279,31280,31281,31282,31283,31284,31285,31286,31287],{"id":30216,"depth":88,"text":30217},{"id":30322,"depth":88,"text":30323},{"id":22181,"depth":88,"text":22182},{"id":30565,"depth":88,"text":30566},{"id":22376,"depth":88,"text":22377},{"id":31080,"depth":88,"text":31081},{"id":31134,"depth":88,"text":31135},{"id":31204,"depth":88,"text":31205},{"id":31250,"depth":88,"text":31251},{},"Install FlowFuse on Kubernetes","install\u002Fkubernetes\u002FREADME.md",{"description":55},"docs\u002Finstall\u002Fkubernetes\u002Findex","aXr7clT3ZBaKm-dD6t1iVCDg5HTL3zU88Kq3klGu_nY",{"id":31295,"title":31296,"body":31297,"description":55,"extension":329,"layout":330,"meta":32602,"navGroup":330,"navOrder":330,"navTitle":31296,"navigation":187,"originalPath":32603,"path":32604,"redirect":330,"seo":32605,"stem":32606,"updated":337,"version":338,"__hash__":32607},"docs\u002Fdocs\u002Finstall\u002Fkubernetes\u002Fingress-controller-migration.md","Ingress Controller Migration",{"type":7,"value":31298,"toc":32581},[31299,31302,31321,31324,31333,31336,31350,31353,31355,31357,31420,31423,31434,31437,31444,31447,31462,31465,31473,31477,31483,31486,31500,31504,31507,31510,31529,31532,31536,31545,31548,31605,31608,31654,31657,31681,31684,31718,31721,31725,31728,31731,31734,31761,31764,31768,31771,31773,31824,31827,31869,31872,31876,31879,31881,31905,31908,31912,31915,31918,31930,31933,31937,31940,31943,31963,31966,32008,32012,32015,32018,32035,32038,32042,32045,32047,32067,32069,32111,32115,32118,32121,32123,32160,32163,32166,32211,32215,32221,32224,32226,32288,32290,32332,32338,32342,32351,32354,32378,32385,32417,32421,32424,32443,32446,32488,32492,32495,32535,32543,32547,32552,32556,32578],[10,31300,31296],{"id":31301},"ingress-controller-migration",[17958,31303,31305,31308],{"className":31304},[21721,21722],[14,31306,1760],{"className":31307},[21726],[17958,31309,31311,31314],{"className":31310},[21730],[14,31312,31313],{},"This guide should not be treated as a one-size-fits-all solution. Consider it as a blueprint and adapt it to your specific Kubernetes cluster setup.\nTest the migration in a testing\u002Fstaging environment before applying it to production.",[14,31315,31316,31317,273],{},"If you have any questions about the migration, please contact ",[41,31318,31320],{"href":31319},"mailto:support@flowfuse.com","support@flowfuse.com",[14,31322,31323],{},"This document describes how to migrate a FlowFuse Platform Kubernetes deployment from the NGINX Ingress Controller to Traefik.",[14,31325,31326,31327,31332],{},"The main reason for this migration is ",[41,31328,31331],{"href":31329,"rel":31330},"https:\u002F\u002Fkubernetes.io\u002Fblog\u002F2025\u002F11\u002F11\u002Fingress-nginx-retirement\u002F",[831],"the retirement of Ingress NGINX"," by the Kubernetes project due to long-term maintenance and security sustainability challenges. Best-effort maintenance continues only until March 2026, after which there will be no further releases, bug fixes, or security updates.",[14,31334,31335],{},"It describes a DNS-based migration approach where the actual traffic switch happens at the domain level by changing DNS records to point to the new ingress controller.\nIt is based on the FlowFuse Ingress Migration Tool and using the ingress classes separation capability provided by the FlowFuse Helm chart:",[28,31337,31338,31344],{},[31,31339,31340,31341],{},"Hosted Instances (project ingresses) via ",[18,31342,31343],{},"forge.projectIngressClassName",[31,31345,31346,31347],{},"Core application components via ",[18,31348,31349],{},"ingress.className",[14,31351,31352],{},"That separation allows you to migrate project traffic and core application traffic in stages.",[23,31354,26],{"id":25},[14,31356,21419],{},[398,31358,31359,31378,31390,31397,31406,31417],{},[31,31360,31361,31362,31365,31366],{},"A working FlowFuse deployment installed with the ",[18,31363,31364],{},"flowfuse\u002Fflowfuse"," Helm chart\n",[28,31367,31368],{},[31,31369,31370,31371,302,31374,31377],{},"note down the FlowFuse Platform Helm release name and namespace, guide uses ",[18,31372,31373],{},"\u003Cflowfuse-release-name>",[18,31375,31376],{},"\u003Cflowfuse-release-namespace>"," placeholders for these values.",[31,31379,31380,31381],{},"Access to the Helm values file used for your deployment\n",[28,31382,31383],{},[31,31384,31385,31386,31389],{},"note down the path to the values file, guide uses ",[18,31387,31388],{},"\u003Cflowfuse-values-file>"," placeholder as a reference",[31,31391,31392,302,31394,31396],{},[18,31393,13627],{},[18,31395,26289],{}," configured for the target cluster",[31,31398,31399,31400,31405],{},"A new Traefik ingress controller installed alongside the existing NGINX ingress controller. Check our ",[41,31401,31404],{"href":31402,"rel":31403},"https:\u002F\u002Fflowfuse.com\u002Fdocs\u002Finstall\u002Fkubernetes\u002Faws\u002F#ingress-controller",[831],"ingress installation guide"," for instructions on how to do this.",[31,31407,31408,31409],{},"Access to the DNS records for:\n",[28,31410,31411,31414],{},[31,31412,31413],{},"the FlowFuse application domain",[31,31415,31416],{},"the wildcard domain used by Hosted Instances",[31,31418,31419],{},"A maintenance window",[14,31421,31422],{},"We also recommend the following before starting the migration:",[28,31424,31425,31428,31431],{},[31,31426,31427],{},"Reduce DNS TTL values in advance to minimize propagation delay",[31,31429,31430],{},"Do not create new Hosted Instances during the migration window",[31,31432,31433],{},"Test the procedure in a non-production cluster first",[23,31435,31436],{"id":12100},"Important notes",[14,31438,31439,31440,31443],{},"The FlowFuse Helm chart provides an ingress migration job controlled by the ",[18,31441,31442],{},"ingressMigration"," values block.",[14,31445,31446],{},"The migration job operates on:",[28,31448,31449,31455],{},[31,31450,31451,31452,660],{},"the project namespace (",[18,31453,31454],{},"forge.projectNamespace",[31,31456,31457,31458,31461],{},"the release namespace, but only in ",[18,31459,31460],{},"copy"," mode",[14,31463,31464],{},"This guide therefore separates the process into two phases:",[398,31466,31467,31470],{},[31,31468,31469],{},"Copy existing ingress resources to the new ingress class",[31,31471,31472],{},"Clean up old ingress resources after traffic has moved",[23,31474,31476],{"id":31475},"step-1-install-traefik-alongside-nginx","Step 1: Install Traefik alongside NGINX",[14,31478,31479,31480,31405],{},"Install Traefik without removing the existing NGINX ingress controller. Check our ",[41,31481,31404],{"href":31402,"rel":31482},[831],[14,31484,31485],{},"Before continuing, verify that both ingress controllers are running.\nAlso, write down the ingress class name used by Traefik, as you will need it for the migration configuration:",[50,31487,31489],{"className":52,"code":31488,"language":54,"meta":55,"style":55},"kubectl get ingressclass\n",[18,31490,31491],{"__ignoreMap":55},[59,31492,31493,31495,31497],{"class":61,"line":62},[59,31494,13627],{"class":65},[59,31496,29217],{"class":69},[59,31498,31499],{"class":69}," ingressclass\n",[23,31501,31503],{"id":31502},"step-2-record-the-traefik-loadbalancer-address","Step 2: Record the Traefik LoadBalancer address",[14,31505,31506],{},"You will need the public address of the new ingress controller before changing DNS.",[14,31508,31509],{},"For example, list services in the Traefik namespace:",[50,31511,31513],{"className":52,"code":31512,"language":54,"meta":55,"style":55},"kubectl get svc -n traefik\n",[18,31514,31515],{"__ignoreMap":55},[59,31516,31517,31519,31521,31524,31527],{"class":61,"line":62},[59,31518,13627],{"class":65},[59,31520,29217],{"class":69},[59,31522,31523],{"class":69}," svc",[59,31525,31526],{"class":73}," -n",[59,31528,29222],{"class":69},[14,31530,31531],{},"Record the external hostname or IP address of the Traefik LoadBalancer service.",[23,31533,31535],{"id":31534},"step-3-run-the-migration-job-in-dry-run-mode","Step 3: Run the migration job in dry-run mode",[14,31537,31538,31539,31541,31542,273],{},"Start by enabling the migration tool in ",[18,31540,31460],{}," mode with ",[18,31543,31544],{},"dryRun: true",[14,31546,31547],{},"Update your values file:",[50,31549,31551],{"className":165,"code":31550,"language":167,"meta":55,"style":55},"ingressMigration:\n  enabled: true\n  mode: copy\n  dryRun: true\n  newIngressClassName: traefik\n  nameSuffix: -tfk\n",[18,31552,31553,31559,31567,31577,31586,31595],{"__ignoreMap":55},[59,31554,31555,31557],{"class":61,"line":62},[59,31556,31442],{"class":174},[59,31558,196],{"class":178},[59,31560,31561,31563,31565],{"class":61,"line":77},[59,31562,2957],{"class":174},[59,31564,179],{"class":178},[59,31566,3230],{"class":73},[59,31568,31569,31572,31574],{"class":61,"line":88},[59,31570,31571],{"class":174},"  mode",[59,31573,179],{"class":178},[59,31575,31576],{"class":69},"copy\n",[59,31578,31579,31582,31584],{"class":61,"line":99},[59,31580,31581],{"class":174},"  dryRun",[59,31583,179],{"class":178},[59,31585,3230],{"class":73},[59,31587,31588,31591,31593],{"class":61,"line":156},[59,31589,31590],{"class":174},"  newIngressClassName",[59,31592,179],{"class":178},[59,31594,25950],{"class":69},[59,31596,31597,31600,31602],{"class":61,"line":216},[59,31598,31599],{"class":174},"  nameSuffix",[59,31601,179],{"class":178},[59,31603,31604],{"class":69},"-tfk\n",[14,31606,31607],{},"Then run the Helm upgrade:",[50,31609,31611],{"className":52,"code":31610,"language":54,"meta":55,"style":55},"helm upgrade --install \u003Cflowfuse-release-name> flowfuse\u002Fflowfuse -n \u003Cflowfuse-release-namespace> -f \u003Cflowfuse-values-file>\n",[18,31612,31613],{"__ignoreMap":55},[59,31614,31615,31617,31619,31621,31623,31626,31628,31630,31632,31634,31636,31639,31641,31643,31645,31647,31650,31652],{"class":61,"line":62},[59,31616,26289],{"class":65},[59,31618,26315],{"class":69},[59,31620,26318],{"class":73},[59,31622,12322],{"class":1372},[59,31624,31625],{"class":69},"flowfuse-release-nam",[59,31627,12328],{"class":178},[59,31629,12732],{"class":1372},[59,31631,30181],{"class":69},[59,31633,31526],{"class":73},[59,31635,12322],{"class":1372},[59,31637,31638],{"class":69},"flowfuse-release-namespac",[59,31640,12328],{"class":178},[59,31642,12732],{"class":1372},[59,31644,13111],{"class":73},[59,31646,12322],{"class":1372},[59,31648,31649],{"class":69},"flowfuse-values-fil",[59,31651,12328],{"class":178},[59,31653,12331],{"class":1372},[14,31655,31656],{},"After the upgrade completes, inspect the Job created by the chart in the release namespace.\nThe Job name is generated by Helm, so first list the Jobs:",[50,31658,31660],{"className":52,"code":31659,"language":54,"meta":55,"style":55},"kubectl get jobs -n \u003Cflowfuse-release-namespace>\n",[18,31661,31662],{"__ignoreMap":55},[59,31663,31664,31666,31668,31671,31673,31675,31677,31679],{"class":61,"line":62},[59,31665,13627],{"class":65},[59,31667,29217],{"class":69},[59,31669,31670],{"class":69}," jobs",[59,31672,31526],{"class":73},[59,31674,12322],{"class":1372},[59,31676,31638],{"class":69},[59,31678,12328],{"class":178},[59,31680,12331],{"class":1372},[14,31682,31683],{},"Then inspect the logs of the migration job:",[50,31685,31687],{"className":52,"code":31686,"language":54,"meta":55,"style":55},"kubectl logs -n \u003Cflowfuse-release-namespace> job\u002F\u003Cmigration-job-name>\n",[18,31688,31689],{"__ignoreMap":55},[59,31690,31691,31693,31696,31698,31700,31702,31704,31706,31709,31711,31714,31716],{"class":61,"line":62},[59,31692,13627],{"class":65},[59,31694,31695],{"class":69}," logs",[59,31697,31526],{"class":73},[59,31699,12322],{"class":1372},[59,31701,31638],{"class":69},[59,31703,12328],{"class":178},[59,31705,12732],{"class":1372},[59,31707,31708],{"class":69}," job\u002F",[59,31710,12660],{"class":1372},[59,31712,31713],{"class":69},"migration-job-nam",[59,31715,12328],{"class":178},[59,31717,12331],{"class":1372},[14,31719,31720],{},"Verify that the dry run identifies all Ingress resources.",[23,31722,31724],{"id":31723},"step-4-optionally-scale-down-traefik-before-copying-resources","Step 4: Optionally scale down Traefik before copying resources",[14,31726,31727],{},"If your environment allows it, and especially if you have many ingress resources, you can temporarily scale Traefik down before executing the ingress resources copy step.",[14,31729,31730],{},"This is optional, but it can reduce repeated Traefik configuration reloads during the migration.",[14,31732,31733],{},"Example:",[50,31735,31737],{"className":52,"code":31736,"language":54,"meta":55,"style":55},"kubectl -n traefik scale deployment traefik --replicas 0\n",[18,31738,31739],{"__ignoreMap":55},[59,31740,31741,31743,31745,31747,31750,31753,31755,31758],{"class":61,"line":62},[59,31742,13627],{"class":65},[59,31744,31526],{"class":73},[59,31746,26297],{"class":69},[59,31748,31749],{"class":69}," scale",[59,31751,31752],{"class":69}," deployment",[59,31754,26297],{"class":69},[59,31756,31757],{"class":73}," --replicas",[59,31759,31760],{"class":73}," 0\n",[14,31762,31763],{},"Only do this if your Kubernetes cluster hosts the FlowFuse Platform and stopping Traefik will not cause a downtime for other applications.",[23,31765,31767],{"id":31766},"step-5-copy-ingress-resources-to-the-new-ingress-class","Step 5: Copy ingress resources to the new ingress class",[14,31769,31770],{},"Once the dry run has been validated, disable dry-run mode and run the actual ingress resources copy.",[14,31772,31547],{},[50,31774,31776],{"className":165,"code":31775,"language":167,"meta":55,"style":55},"ingressMigration:\n  enabled: true\n  mode: copy\n  dryRun: false\n  newIngressClassName: traefik\n  nameSuffix: -tfk\n",[18,31777,31778,31784,31792,31800,31808,31816],{"__ignoreMap":55},[59,31779,31780,31782],{"class":61,"line":62},[59,31781,31442],{"class":174},[59,31783,196],{"class":178},[59,31785,31786,31788,31790],{"class":61,"line":77},[59,31787,2957],{"class":174},[59,31789,179],{"class":178},[59,31791,3230],{"class":73},[59,31793,31794,31796,31798],{"class":61,"line":88},[59,31795,31571],{"class":174},[59,31797,179],{"class":178},[59,31799,31576],{"class":69},[59,31801,31802,31804,31806],{"class":61,"line":99},[59,31803,31581],{"class":174},[59,31805,179],{"class":178},[59,31807,2962],{"class":73},[59,31809,31810,31812,31814],{"class":61,"line":156},[59,31811,31590],{"class":174},[59,31813,179],{"class":178},[59,31815,25950],{"class":69},[59,31817,31818,31820,31822],{"class":61,"line":216},[59,31819,31599],{"class":174},[59,31821,179],{"class":178},[59,31823,31604],{"class":69},[14,31825,31826],{},"Run the Helm upgrade again:",[50,31828,31829],{"className":52,"code":31610,"language":54,"meta":55,"style":55},[18,31830,31831],{"__ignoreMap":55},[59,31832,31833,31835,31837,31839,31841,31843,31845,31847,31849,31851,31853,31855,31857,31859,31861,31863,31865,31867],{"class":61,"line":62},[59,31834,26289],{"class":65},[59,31836,26315],{"class":69},[59,31838,26318],{"class":73},[59,31840,12322],{"class":1372},[59,31842,31625],{"class":69},[59,31844,12328],{"class":178},[59,31846,12732],{"class":1372},[59,31848,30181],{"class":69},[59,31850,31526],{"class":73},[59,31852,12322],{"class":1372},[59,31854,31638],{"class":69},[59,31856,12328],{"class":178},[59,31858,12732],{"class":1372},[59,31860,13111],{"class":73},[59,31862,12322],{"class":1372},[59,31864,31649],{"class":69},[59,31866,12328],{"class":178},[59,31868,12331],{"class":1372},[14,31870,31871],{},"This creates new Ingress resources that point to the new ingress class and use the configured suffix.",[23,31873,31875],{"id":31874},"step-6-scale-traefik-back-up","Step 6: Scale Traefik back up",[14,31877,31878],{},"If you scaled Traefik down in step 4, scale it back up now.",[14,31880,31733],{},[50,31882,31884],{"className":52,"code":31883,"language":54,"meta":55,"style":55},"kubectl -n traefik scale deployment traefik --replicas 2\n",[18,31885,31886],{"__ignoreMap":55},[59,31887,31888,31890,31892,31894,31896,31898,31900,31902],{"class":61,"line":62},[59,31889,13627],{"class":65},[59,31891,31526],{"class":73},[59,31893,26297],{"class":69},[59,31895,31749],{"class":69},[59,31897,31752],{"class":69},[59,31899,26297],{"class":69},[59,31901,31757],{"class":73},[59,31903,31904],{"class":73}," 2\n",[14,31906,31907],{},"Use the replica count that matches your environment.",[23,31909,31911],{"id":31910},"step-7-change-dns-to-point-to-traefik","Step 7: Change DNS to point to Traefik",[14,31913,31914],{},"Update the DNS records used by the FlowFuse Platform so that incoming traffic is sent to the Traefik LoadBalancer address recorded earlier.",[14,31916,31917],{},"This includes:",[28,31919,31920,31925],{},[31,31921,31922,31923,660],{},"the FlowFuse application hostname (e.g. ",[18,31924,21970],{},[31,31926,31927,31928,660],{},"the wildcard DNS record used by Hosted Instances (e.g. ",[18,31929,21530],{},[14,31931,31932],{},"Do not proceed to cleanup until you have confirmed that DNS is resolving to the new ingress controller.",[23,31934,31936],{"id":31935},"step-8-make-new-hosted-instances-use-traefik","Step 8: Make new Hosted Instances use Traefik",[14,31938,31939],{},"After the copied ingress resources are in place, update the project ingress class so that newly created Hosted Instances use Traefik.",[14,31941,31942],{},"Add or update the following value in the FlowFuse Helm values file:",[50,31944,31946],{"className":165,"code":31945,"language":167,"meta":55,"style":55},"forge:\n  projectIngressClassName: traefik\n",[18,31947,31948,31954],{"__ignoreMap":55},[59,31949,31950,31952],{"class":61,"line":62},[59,31951,22305],{"class":174},[59,31953,196],{"class":178},[59,31955,31956,31959,31961],{"class":61,"line":77},[59,31957,31958],{"class":174},"  projectIngressClassName",[59,31960,179],{"class":178},[59,31962,25950],{"class":69},[14,31964,31965],{},"Apply the change:",[50,31967,31968],{"className":52,"code":31610,"language":54,"meta":55,"style":55},[18,31969,31970],{"__ignoreMap":55},[59,31971,31972,31974,31976,31978,31980,31982,31984,31986,31988,31990,31992,31994,31996,31998,32000,32002,32004,32006],{"class":61,"line":62},[59,31973,26289],{"class":65},[59,31975,26315],{"class":69},[59,31977,26318],{"class":73},[59,31979,12322],{"class":1372},[59,31981,31625],{"class":69},[59,31983,12328],{"class":178},[59,31985,12732],{"class":1372},[59,31987,30181],{"class":69},[59,31989,31526],{"class":73},[59,31991,12322],{"class":1372},[59,31993,31638],{"class":69},[59,31995,12328],{"class":178},[59,31997,12732],{"class":1372},[59,31999,13111],{"class":73},[59,32001,12322],{"class":1372},[59,32003,31649],{"class":69},[59,32005,12328],{"class":178},[59,32007,12331],{"class":1372},[23,32009,32011],{"id":32010},"step-9-wait-for-dns-propagation-and-validate-traffic","Step 9: Wait for DNS propagation and validate traffic",[14,32013,32014],{},"Allow time for DNS propagation according to your TTL settings.",[14,32016,32017],{},"During this period, validate the migration by checking:",[28,32019,32020,32023,32026,32029,32032],{},[31,32021,32022],{},"the FlowFuse Platform is reachable",[31,32024,32025],{},"Hosted Instances are reachable",[31,32027,32028],{},"TLS certificates are served correctly",[31,32030,32031],{},"Traefik logs and metrics do not show routing errors",[31,32033,32034],{},"Ingress NGINX logs do not show incoming traffic",[14,32036,32037],{},"Do not continue to cleanup until traffic is consistently served by Traefik. Otherwise, you risk potential downtime of the FlowFuse Platform and Hosted Instances.",[23,32039,32041],{"id":32040},"step-10-move-core-application-components-to-traefik","Step 10: Move core application components to Traefik",[14,32043,32044],{},"Once you are confident that the Traefik ingress controller is handling all the incoming traffic, update the ingress class used by the core application components.",[14,32046,31942],{},[50,32048,32050],{"className":165,"code":32049,"language":167,"meta":55,"style":55},"ingress:\n  className: traefik\n",[18,32051,32052,32058],{"__ignoreMap":55},[59,32053,32054,32056],{"class":61,"line":62},[59,32055,29645],{"class":174},[59,32057,196],{"class":178},[59,32059,32060,32063,32065],{"class":61,"line":77},[59,32061,32062],{"class":174},"  className",[59,32064,179],{"class":178},[59,32066,25950],{"class":69},[14,32068,31965],{},[50,32070,32071],{"className":52,"code":31610,"language":54,"meta":55,"style":55},[18,32072,32073],{"__ignoreMap":55},[59,32074,32075,32077,32079,32081,32083,32085,32087,32089,32091,32093,32095,32097,32099,32101,32103,32105,32107,32109],{"class":61,"line":62},[59,32076,26289],{"class":65},[59,32078,26315],{"class":69},[59,32080,26318],{"class":73},[59,32082,12322],{"class":1372},[59,32084,31625],{"class":69},[59,32086,12328],{"class":178},[59,32088,12732],{"class":1372},[59,32090,30181],{"class":69},[59,32092,31526],{"class":73},[59,32094,12322],{"class":1372},[59,32096,31638],{"class":69},[59,32098,12328],{"class":178},[59,32100,12732],{"class":1372},[59,32102,13111],{"class":73},[59,32104,12322],{"class":1372},[59,32106,31649],{"class":69},[59,32108,12328],{"class":178},[59,32110,12331],{"class":1372},[23,32112,32114],{"id":32113},"step-11-optionally-scale-down-nginx","Step 11: Optionally scale down NGINX",[14,32116,32117],{},"If the NGINX ingress controller is still used for resources outside FlowFuse, leave it in place.",[14,32119,32120],{},"If it is no longer needed, you can scale it down after you have confirmed that FlowFuse Platform traffic is fully handled by Traefik.",[14,32122,31733],{},[50,32124,32126],{"className":52,"code":32125,"language":54,"meta":55,"style":55},"kubectl -n \u003Cnginx-namespace> scale deployment \u003Cnginx-deployment> --replicas 0\n",[18,32127,32128],{"__ignoreMap":55},[59,32129,32130,32132,32134,32136,32139,32141,32143,32145,32147,32149,32152,32154,32156,32158],{"class":61,"line":62},[59,32131,13627],{"class":65},[59,32133,31526],{"class":73},[59,32135,12322],{"class":1372},[59,32137,32138],{"class":69},"nginx-namespac",[59,32140,12328],{"class":178},[59,32142,12732],{"class":1372},[59,32144,31749],{"class":69},[59,32146,31752],{"class":69},[59,32148,12322],{"class":1372},[59,32150,32151],{"class":69},"nginx-deploymen",[59,32153,12666],{"class":178},[59,32155,12732],{"class":1372},[59,32157,31757],{"class":73},[59,32159,31760],{"class":73},[14,32161,32162],{},"If your Ingress-Nginx installation created admission webhooks and you are about to retire that controller entirely soon, remove those webhook resources.",[14,32164,32165],{},"Examples:",[50,32167,32169],{"className":52,"code":32168,"language":54,"meta":55,"style":55},"kubectl delete validatingwebhookconfiguration \u003Cnginx-validating-webhook-name>\nkubectl delete mutatingwebhookconfiguration \u003Cnginx-mutating-webhook-name> --ignore-not-found\n",[18,32170,32171,32190],{"__ignoreMap":55},[59,32172,32173,32175,32178,32181,32183,32186,32188],{"class":61,"line":62},[59,32174,13627],{"class":65},[59,32176,32177],{"class":69}," delete",[59,32179,32180],{"class":69}," validatingwebhookconfiguration",[59,32182,12322],{"class":1372},[59,32184,32185],{"class":69},"nginx-validating-webhook-nam",[59,32187,12328],{"class":178},[59,32189,12331],{"class":1372},[59,32191,32192,32194,32196,32199,32201,32204,32206,32208],{"class":61,"line":77},[59,32193,13627],{"class":65},[59,32195,32177],{"class":69},[59,32197,32198],{"class":69}," mutatingwebhookconfiguration",[59,32200,12322],{"class":1372},[59,32202,32203],{"class":69},"nginx-mutating-webhook-nam",[59,32205,12328],{"class":178},[59,32207,12732],{"class":1372},[59,32209,32210],{"class":73}," --ignore-not-found\n",[23,32212,32214],{"id":32213},"step-12-clean-up-old-ingress-resources","Step 12: Clean up old ingress resources",[14,32216,32217,32218,17569],{},"After traffic has fully moved to Traefik, switch the migration tool to ",[18,32219,32220],{},"cleanup",[14,32222,32223],{},"In this mode, the tool removes old ingress resources and renames the migrated ones back to their original names.\nThis step can cause a short interruption while Kubernetes updates the resources.",[14,32225,31547],{},[50,32227,32229],{"className":165,"code":32228,"language":167,"meta":55,"style":55},"ingressMigration:\n  enabled: true\n  mode: cleanup\n  dryRun: false\n  oldIngressClassName: nginx\n  newIngressClassName: traefik\n  nameSuffix: -tfk\n",[18,32230,32231,32237,32245,32254,32262,32272,32280],{"__ignoreMap":55},[59,32232,32233,32235],{"class":61,"line":62},[59,32234,31442],{"class":174},[59,32236,196],{"class":178},[59,32238,32239,32241,32243],{"class":61,"line":77},[59,32240,2957],{"class":174},[59,32242,179],{"class":178},[59,32244,3230],{"class":73},[59,32246,32247,32249,32251],{"class":61,"line":88},[59,32248,31571],{"class":174},[59,32250,179],{"class":178},[59,32252,32253],{"class":69},"cleanup\n",[59,32255,32256,32258,32260],{"class":61,"line":99},[59,32257,31581],{"class":174},[59,32259,179],{"class":178},[59,32261,2962],{"class":73},[59,32263,32264,32267,32269],{"class":61,"line":156},[59,32265,32266],{"class":174},"  oldIngressClassName",[59,32268,179],{"class":178},[59,32270,32271],{"class":69},"nginx\n",[59,32273,32274,32276,32278],{"class":61,"line":216},[59,32275,31590],{"class":174},[59,32277,179],{"class":178},[59,32279,25950],{"class":69},[59,32281,32282,32284,32286],{"class":61,"line":224},[59,32283,31599],{"class":174},[59,32285,179],{"class":178},[59,32287,31604],{"class":69},[14,32289,31826],{},[50,32291,32292],{"className":52,"code":31610,"language":54,"meta":55,"style":55},[18,32293,32294],{"__ignoreMap":55},[59,32295,32296,32298,32300,32302,32304,32306,32308,32310,32312,32314,32316,32318,32320,32322,32324,32326,32328,32330],{"class":61,"line":62},[59,32297,26289],{"class":65},[59,32299,26315],{"class":69},[59,32301,26318],{"class":73},[59,32303,12322],{"class":1372},[59,32305,31625],{"class":69},[59,32307,12328],{"class":178},[59,32309,12732],{"class":1372},[59,32311,30181],{"class":69},[59,32313,31526],{"class":73},[59,32315,12322],{"class":1372},[59,32317,31638],{"class":69},[59,32319,12328],{"class":178},[59,32321,12732],{"class":1372},[59,32323,13111],{"class":73},[59,32325,12322],{"class":1372},[59,32327,31649],{"class":69},[59,32329,12328],{"class":178},[59,32331,12331],{"class":1372},[14,32333,32334,32335,32337],{},"If you want to validate the cleanup plan first, run the same configuration with ",[18,32336,31544],{}," before applying it.",[23,32339,32341],{"id":32340},"step-13-remove-any-remaining-suffixed-ingress-resources-from-the-release-namespace","Step 13: Remove any remaining suffixed ingress resources from the release namespace",[14,32343,32344,32345,32347,32348,32350],{},"The migration job in ",[18,32346,31460],{}," mode creates new ingress resources with a suffix in the release namespace.\nIn ",[18,32349,32220],{}," mode, the job deletes old ingress resources and renames the new ones to match the original names, but it does not remove the suffixed copies from the release namespace.\nThis is to avoid any potential issues with the release namespace if it contains non-ingress resources or custom configurations.\nBecause of that, after cleanup you should inspect the release namespace and remove any remaining suffixed ingress resources that were copied there.",[14,32352,32353],{},"List ingress resources in the release namespace:",[50,32355,32357],{"className":52,"code":32356,"language":54,"meta":55,"style":55},"kubectl get ingress -n \u003Cflowfuse-release-namespace>\n",[18,32358,32359],{"__ignoreMap":55},[59,32360,32361,32363,32365,32368,32370,32372,32374,32376],{"class":61,"line":62},[59,32362,13627],{"class":65},[59,32364,29217],{"class":69},[59,32366,32367],{"class":69}," ingress",[59,32369,31526],{"class":73},[59,32371,12322],{"class":1372},[59,32373,31638],{"class":69},[59,32375,12328],{"class":178},[59,32377,12331],{"class":1372},[14,32379,32380,32381,32384],{},"If any copied ingress resources with the migration suffix still exist (",[18,32382,32383],{},"-tfk","), delete them manually:",[50,32386,32388],{"className":52,"code":32387,"language":54,"meta":55,"style":55},"kubectl delete ingress \u003Cingress-name> -n \u003Cflowfuse-release-namespace>\n",[18,32389,32390],{"__ignoreMap":55},[59,32391,32392,32394,32396,32398,32400,32403,32405,32407,32409,32411,32413,32415],{"class":61,"line":62},[59,32393,13627],{"class":65},[59,32395,32177],{"class":69},[59,32397,32367],{"class":69},[59,32399,12322],{"class":1372},[59,32401,32402],{"class":69},"ingress-nam",[59,32404,12328],{"class":178},[59,32406,12732],{"class":1372},[59,32408,31526],{"class":73},[59,32410,12322],{"class":1372},[59,32412,31638],{"class":69},[59,32414,12328],{"class":178},[59,32416,12331],{"class":1372},[23,32418,32420],{"id":32419},"step-14-disable-the-migration-job","Step 14: Disable the migration job",[14,32422,32423],{},"After the migration and cleanup are complete, disable the migration tool:",[50,32425,32427],{"className":165,"code":32426,"language":167,"meta":55,"style":55},"ingressMigration:\n  enabled: false\n",[18,32428,32429,32435],{"__ignoreMap":55},[59,32430,32431,32433],{"class":61,"line":62},[59,32432,31442],{"class":174},[59,32434,196],{"class":178},[59,32436,32437,32439,32441],{"class":61,"line":77},[59,32438,2957],{"class":174},[59,32440,179],{"class":178},[59,32442,2962],{"class":73},[14,32444,32445],{},"Apply the change one final time:",[50,32447,32448],{"className":52,"code":31610,"language":54,"meta":55,"style":55},[18,32449,32450],{"__ignoreMap":55},[59,32451,32452,32454,32456,32458,32460,32462,32464,32466,32468,32470,32472,32474,32476,32478,32480,32482,32484,32486],{"class":61,"line":62},[59,32453,26289],{"class":65},[59,32455,26315],{"class":69},[59,32457,26318],{"class":73},[59,32459,12322],{"class":1372},[59,32461,31625],{"class":69},[59,32463,12328],{"class":178},[59,32465,12732],{"class":1372},[59,32467,30181],{"class":69},[59,32469,31526],{"class":73},[59,32471,12322],{"class":1372},[59,32473,31638],{"class":69},[59,32475,12328],{"class":178},[59,32477,12732],{"class":1372},[59,32479,13111],{"class":73},[59,32481,12322],{"class":1372},[59,32483,31649],{"class":69},[59,32485,12328],{"class":178},[59,32487,12331],{"class":1372},[23,32489,32491],{"id":32490},"related-configuration-values","Related configuration values",[14,32493,32494],{},"This guide uses the following FlowFuse Helm chart values:",[28,32496,32497,32501,32505,32510,32515,32520,32525,32530],{},[31,32498,32499],{},[18,32500,31349],{},[31,32502,32503],{},[18,32504,31343],{},[31,32506,32507],{},[18,32508,32509],{},"ingressMigration.enabled",[31,32511,32512],{},[18,32513,32514],{},"ingressMigration.mode",[31,32516,32517],{},[18,32518,32519],{},"ingressMigration.dryRun",[31,32521,32522],{},[18,32523,32524],{},"ingressMigration.newIngressClassName",[31,32526,32527],{},[18,32528,32529],{},"ingressMigration.oldIngressClassName",[31,32531,32532],{},[18,32533,32534],{},"ingressMigration.nameSuffix",[14,32536,32537,32538,273],{},"For the full list of ingress migration options, refer to the ",[41,32539,32542],{"href":32540,"rel":32541},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fhelm\u002Fblob\u002Fmain\u002Fhelm\u002Fflowfuse\u002FREADME.md#ingress-migration-tool",[831],"FlowFuse Helm chart README",[23,32544,32546],{"id":32545},"need-help","Need help?",[14,32548,32549,32550,273],{},"If you would like guidance on planning this migration, we can provide migration consultation. To discuss this option, contact ",[41,32551,31320],{"href":31319},[23,32553,32555],{"id":32554},"resources","Resources",[28,32557,32558,32564,32571],{},[31,32559,32560],{},[41,32561,32563],{"href":31329,"rel":32562},[831],"Ingress Nginx retirement announcement",[31,32565,32566],{},[41,32567,32570],{"href":32568,"rel":32569},"https:\u002F\u002Fgeorg-schwarz.com\u002Fblog\u002Fzero-downtime-ingress-controller-migration-kubernetes\u002F",[831],"Zero-downtime Ingress controller migration guide",[31,32572,32573],{},[41,32574,32577],{"href":32575,"rel":32576},"https:\u002F\u002Fdoc.traefik.io\u002Ftraefik\u002Fmigrate\u002Fnginx-to-traefik\u002F",[831],"Migrate from Ingress NGINX Controller to Traefik",[316,32579,32580],{},"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);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":55,"searchDepth":77,"depth":77,"links":32582},[32583,32584,32585,32586,32587,32588,32589,32590,32591,32592,32593,32594,32595,32596,32597,32598,32599,32600,32601],{"id":25,"depth":77,"text":26},{"id":12100,"depth":77,"text":31436},{"id":31475,"depth":77,"text":31476},{"id":31502,"depth":77,"text":31503},{"id":31534,"depth":77,"text":31535},{"id":31723,"depth":77,"text":31724},{"id":31766,"depth":77,"text":31767},{"id":31874,"depth":77,"text":31875},{"id":31910,"depth":77,"text":31911},{"id":31935,"depth":77,"text":31936},{"id":32010,"depth":77,"text":32011},{"id":32040,"depth":77,"text":32041},{"id":32113,"depth":77,"text":32114},{"id":32213,"depth":77,"text":32214},{"id":32340,"depth":77,"text":32341},{"id":32419,"depth":77,"text":32420},{"id":32490,"depth":77,"text":32491},{"id":32545,"depth":77,"text":32546},{"id":32554,"depth":77,"text":32555},{},"install\u002Fkubernetes\u002Fingress-controller-migration.md","\u002Fdocs\u002Finstall\u002Fkubernetes\u002Fingress-controller-migration",{"title":31296,"description":55},"docs\u002Finstall\u002Fkubernetes\u002Fingress-controller-migration","K0EPfJdbTVV7wwMbGSkKqNHB0UWj_e7qqYJH8N6t3mQ",{"id":32609,"title":32610,"body":32611,"description":55,"extension":329,"layout":330,"meta":33338,"navGroup":330,"navOrder":330,"navTitle":33339,"navigation":187,"originalPath":33340,"path":33341,"redirect":330,"seo":33342,"stem":33343,"updated":337,"version":338,"__hash__":33344},"docs\u002Fdocs\u002Finstall\u002Fkubernetes\u002Fopenshift.md","Openshift",{"type":7,"value":32612,"toc":33320},[32613,32615,32619,32622,32624,32659,32661,32663,32718,32720,32736,32738,32744,32751,32757,32761,32763,32767,32770,32796,32799,32817,32841,32843,32867,32869,32873,32888,32988,32993,32995,32998,33000,33002,33015,33017,33040,33042,33065,33069,33091,33093,33097,33125,33127,33129,33133,33137,33139,33144,33146,33153,33155,33239,33241,33317],[17936,32614,29745],{},[10,32616,32618],{"id":32617},"openshift-install","OpenShift Install",[14,32620,32621],{},"This guide walks you through detailed set up of FlowFuse Platform on a container envoronment managed by OpenShift. Typically suited for large on premise deployments or deployment in Cloud infrastructure.\nBy the end, you will have a fully functioning FlowFuse instance running on a OpenShift cluster.",[10,32623,21369],{"id":21368},[17958,32625,17964,32627,17964,32646,29787],{"className":32626},[21373,21374,21375],[17958,32628,29759,32630,29759,32632,17964],{"className":32629},[21368],[17972,32631,26],{},[17958,32633,29764,32634,29764,32636,29764,32639,29764,32642,29764,32644,29759],{},[21386,32635],{"task":21388},[21386,32637],{"task":32638},"OpenShift cluster",[21386,32640],{"task":32641},"FlowFuse License",[21386,32643],{"type":21394,"task":21395},[21386,32645],{"type":21394,"task":21398},[17958,32647,29759,32649,29759,32651,17964],{"className":32648},[21368],[17972,32650,13547],{},[17958,32652,29764,32653,29764,32655,29764,32657,29759],{},[21386,32654],{"task":21408},[21386,32656],{"task":21411},[21386,32658],{"type":21394,"task":21414},[23,32660,26],{"id":25},[14,32662,21419],{},[398,32664,32665,32671,32688,32695,32701,32708],{},[31,32666,32667,21427,32669,660],{},[364,32668,21426],{},[41,32670,21035],{"href":21430},[31,32672,32673,32676,32677,32680,32681,32683,32684],{},[364,32674,32675],{},"oc:"," To manage a OpenShift cluster you will need a copy of the ",[18,32678,32679],{},"oc"," utility. Instructions on how to install ",[18,32682,32679],{}," can be found ",[41,32685,785],{"href":32686,"rel":32687},"https:\u002F\u002Fdocs.openshift.com\u002Fcontainer-platform\u002F4.17\u002Fcli_reference\u002Fopenshift_cli\u002Fgetting-started-cli.html",[831],[31,32689,32690,29817,32692],{},[364,32691,29816],{},[41,32693,29822],{"href":29820,"rel":32694},[831],[31,32696,32697,32700],{},[364,32698,32699],{},"OpenShift Cluster:"," an OpenShift cluster instance with at least two worker nodes",[31,32702,32703,3497,32705,29852],{},[364,32704,29847],{},[41,32706,29851],{"href":25508,"rel":32707},[831],[31,32709,32710,32713,32714],{},[364,32711,32712],{},"FlowFuse License:"," A valid FlowFuse license key is required to run on OpenShift. You can request a quote ",[41,32715,785],{"href":32716,"rel":32717},"https:\u002F\u002Fflowfuse.com\u002Fpricing\u002Frequest-quote\u002F",[831],[14,32719,21452],{},[28,32721,32722,32729],{},[31,32723,32724,21460,32726,21465],{},[364,32725,21459],{},[41,32727,21464],{"href":32728},"\u002Fdocs\u002Finstall\u002Fkubernetes#how-to-use-external-database-server%3F",[31,32730,32731,21471,32733,660],{},[364,32732,29877],{},[41,32734,21414],{"href":32735},"\u002Fdocs\u002Finstall\u002Fkubernetes#i-would-like-to-secure-the-platform-with-https%2C-how-can-i-do-that%3F",[104,32737,21035],{"id":21034},[14,32739,36,32740,32743],{},[41,32741,29901],{"href":20313,"rel":32742},[831]," will be needed\nto point to the domain that is used for the project instances. This will need to point\nto the Ingress controller.",[14,32745,29905,32746,29909,32748,32750],{},[18,32747,29908],{},[18,32749,21530],{}," is mapped to the IP address used by\nyour OpenShift clusters's Ingress controller.",[14,32752,29915,32753,29918,32755,273],{},[18,32754,21970],{},[18,32756,1312],{},[14,32758,21546,32759,273],{},[41,32760,785],{"href":21008},[23,32762,9164],{"id":9163},[104,32764,32766],{"id":32765},"create-project-in-the-openshift-cluster","Create project in the OpenShift cluster",[14,32768,32769],{},"To maintain a clean environment, it is recommended to create a new project for the FlowFuse platform:",[50,32771,32773],{"className":52,"code":32772,"language":54,"meta":55,"style":55},"oc new-project flowfuse --description=\"FlowFuse Platform\" --display-name=\"FlowFuse\"\n",[18,32774,32775],{"__ignoreMap":55},[59,32776,32777,32779,32782,32784,32787,32790,32793],{"class":61,"line":62},[59,32778,32679],{"class":65},[59,32780,32781],{"class":69}," new-project",[59,32783,21914],{"class":69},[59,32785,32786],{"class":73}," --description=",[59,32788,32789],{"class":69},"\"FlowFuse Platform\"",[59,32791,32792],{"class":73}," --display-name=",[59,32794,32795],{"class":69},"\"FlowFuse\"\n",[14,32797,32798],{},"Describe the project to get the SCC information:",[50,32800,32802],{"className":52,"code":32801,"language":54,"meta":55,"style":55},"oc describe project flowfuse\n",[18,32803,32804],{"__ignoreMap":55},[59,32805,32806,32808,32811,32814],{"class":61,"line":62},[59,32807,32679],{"class":65},[59,32809,32810],{"class":69}," describe",[59,32812,32813],{"class":69}," project",[59,32815,32816],{"class":69}," flowfuse\n",[14,32818,32819,32820,302,32823,32826,32827,32829,32830,32833,32834,32837,32838,273],{},"Note the ",[18,32821,32822],{},"openshift.io\u002Fsa.scc.uid-range",[18,32824,32825],{},"openshift.io\u002Fsa.scc.supplemental-groups"," values. You will need to use these values when customizing the FlowFuse platform installation.\nIn example, if the ",[18,32828,32822],{}," value is ",[18,32831,32832],{},"1000710000\u002F10000",", the ",[18,32835,32836],{},"\u003Cproject-uid>"," value will be ",[18,32839,32840],{},"1000710000",[104,32842,29930],{"id":29929},[50,32844,32845],{"className":52,"code":29933,"language":54,"meta":55,"style":55},[18,32846,32847,32859],{"__ignoreMap":55},[59,32848,32849,32851,32853,32855,32857],{"class":61,"line":62},[59,32850,26289],{"class":65},[59,32852,26292],{"class":69},[59,32854,22570],{"class":69},[59,32856,21914],{"class":69},[59,32858,29532],{"class":69},[59,32860,32861,32863,32865],{"class":61,"line":77},[59,32862,26289],{"class":65},[59,32864,26292],{"class":69},[59,32866,23228],{"class":69},[104,32868,29959],{"id":29958},[14,32870,29962,32871,29965],{},[18,32872,24102],{},[14,32874,29968,32875,29971,32877,32879,32880,32882,32883,32887],{},[18,32876,24102],{},[18,32878,1312],{}," with your domain\nand ",[18,32881,32836],{}," with the value from the project description collected on ",[41,32884,32886],{"href":32885},"#create-project-in-the-openshift-cluster","project creation step","):",[50,32889,32891],{"className":52,"code":32890,"language":54,"meta":55,"style":55},"cat \u003C\u003CEOF > customization.yml\nforge:\n  entryPoint: forge.example.com\n  domain: example.com\n  https: false\n  localPostgresql: true\n  cloudProvider: openshift\n  podSecurityContext:\n    runAsUser: \u003Cproject-uid>\n    runAsGroup: \u003Cproject-uid>\n    fsGroup: \u003Cproject-uid>\n\npostgresql:\n  primary:\n    podSecurityContext:\n      fsGroup: \u003Cproject-uid>\n    containerSecurityContext:\n      runAsUser: \u003Cproject-uid>\nEOF\n",[18,32892,32893,32905,32909,32913,32917,32921,32925,32930,32935,32940,32945,32950,32954,32959,32964,32969,32974,32979,32984],{"__ignoreMap":55},[59,32894,32895,32897,32899,32901,32903],{"class":61,"line":62},[59,32896,29984],{"class":65},[59,32898,20850],{"class":1372},[59,32900,29989],{"class":69},[59,32902,20464],{"class":1372},[59,32904,29994],{"class":69},[59,32906,32907],{"class":61,"line":77},[59,32908,29999],{"class":69},[59,32910,32911],{"class":61,"line":88},[59,32912,30004],{"class":69},[59,32914,32915],{"class":61,"line":99},[59,32916,30009],{"class":69},[59,32918,32919],{"class":61,"line":156},[59,32920,30014],{"class":69},[59,32922,32923],{"class":61,"line":216},[59,32924,30019],{"class":69},[59,32926,32927],{"class":61,"line":224},[59,32928,32929],{"class":69},"  cloudProvider: openshift\n",[59,32931,32932],{"class":61,"line":233},[59,32933,32934],{"class":69},"  podSecurityContext:\n",[59,32936,32937],{"class":61,"line":241},[59,32938,32939],{"class":69},"    runAsUser: \u003Cproject-uid>\n",[59,32941,32942],{"class":61,"line":249},[59,32943,32944],{"class":69},"    runAsGroup: \u003Cproject-uid>\n",[59,32946,32947],{"class":61,"line":257},[59,32948,32949],{"class":69},"    fsGroup: \u003Cproject-uid>\n",[59,32951,32952],{"class":61,"line":3137},[59,32953,188],{"emptyLinePlaceholder":187},[59,32955,32956],{"class":61,"line":3150},[59,32957,32958],{"class":69},"postgresql:\n",[59,32960,32961],{"class":61,"line":3163},[59,32962,32963],{"class":69},"  primary:\n",[59,32965,32966],{"class":61,"line":3176},[59,32967,32968],{"class":69},"    podSecurityContext:\n",[59,32970,32971],{"class":61,"line":3187},[59,32972,32973],{"class":69},"      fsGroup: \u003Cproject-uid>\n",[59,32975,32976],{"class":61,"line":3193},[59,32977,32978],{"class":69},"    containerSecurityContext:\n",[59,32980,32981],{"class":61,"line":3201},[59,32982,32983],{"class":69},"      runAsUser: \u003Cproject-uid>\n",[59,32985,32986],{"class":61,"line":3214},[59,32987,20853],{"class":69},[14,32989,30026,32990,273],{},[41,32991,30031],{"href":30029,"rel":32992},[831],[104,32994,30035],{"id":30034},[14,32996,32997],{},"By default FlowFuse platform expects that worker nodes have specific labels applied. The main reason behind this approach is to separate core application components from Node-RED instances.",[14,32999,30041],{},[14,33001,30044],{},[50,33003,33005],{"className":52,"code":33004,"language":54,"meta":55,"style":55},"oc get nodes\n",[18,33006,33007],{"__ignoreMap":55},[59,33008,33009,33011,33013],{"class":61,"line":62},[59,33010,32679],{"class":65},[59,33012,29217],{"class":69},[59,33014,30058],{"class":69},[14,33016,30061],{},[50,33018,33020],{"className":52,"code":33019,"language":54,"meta":55,"style":55},"oc label node \u003Cmanagement-node-name> role=management\n",[18,33021,33022],{"__ignoreMap":55},[59,33023,33024,33026,33028,33030,33032,33034,33036,33038],{"class":61,"line":62},[59,33025,32679],{"class":65},[59,33027,30073],{"class":69},[59,33029,30076],{"class":69},[59,33031,12322],{"class":1372},[59,33033,30081],{"class":69},[59,33035,12328],{"class":178},[59,33037,12732],{"class":1372},[59,33039,30088],{"class":69},[14,33041,30091],{},[50,33043,33045],{"className":52,"code":33044,"language":54,"meta":55,"style":55},"oc label node \u003Cprojects-node-name> role=projects\n",[18,33046,33047],{"__ignoreMap":55},[59,33048,33049,33051,33053,33055,33057,33059,33061,33063],{"class":61,"line":62},[59,33050,32679],{"class":65},[59,33052,30073],{"class":69},[59,33054,30076],{"class":69},[59,33056,12322],{"class":1372},[59,33058,30109],{"class":69},[59,33060,12328],{"class":178},[59,33062,12732],{"class":1372},[59,33064,30116],{"class":69},[14,33066,30119,33067,30122],{},[18,33068,24102],{},[50,33070,33071],{"className":165,"code":30125,"language":167,"meta":55,"style":55},[18,33072,33073,33079,33085],{"__ignoreMap":55},[59,33074,33075,33077],{"class":61,"line":62},[59,33076,22305],{"class":174},[59,33078,196],{"class":178},[59,33080,33081,33083],{"class":61,"line":77},[59,33082,29594],{"class":174},[59,33084,196],{"class":178},[59,33086,33087,33089],{"class":61,"line":88},[59,33088,29601],{"class":174},[59,33090,196],{"class":178},[23,33092,30148],{"id":21873},[14,33094,30151,33095,30154],{},[18,33096,24102],{},[50,33098,33100],{"className":52,"code":33099,"language":54,"meta":55,"style":55},"helm upgrade --atomic --install --timeout 10m flowfuse flowfuse\u002Fflowforge -f customization.yml\n",[18,33101,33102],{"__ignoreMap":55},[59,33103,33104,33106,33108,33110,33112,33114,33116,33118,33121,33123],{"class":61,"line":62},[59,33105,26289],{"class":65},[59,33107,26315],{"class":69},[59,33109,30168],{"class":73},[59,33111,26318],{"class":73},[59,33113,30173],{"class":73},[59,33115,30176],{"class":69},[59,33117,21914],{"class":69},[59,33119,33120],{"class":69}," flowfuse\u002Fflowforge",[59,33122,13111],{"class":73},[59,33124,29994],{"class":69},[23,33126,584],{"id":9513},[14,33128,21982],{},[14,33130,21985,33131,273],{},[41,33132,2587],{"href":21164},[14,33134,30196,33135,273],{},[41,33136,30200],{"href":30199},[23,33138,9856],{"id":9855},[14,33140,30205,33141,30211],{},[41,33142,30210],{"href":30208,"rel":33143},[831],[23,33145,22178],{"id":22177},[14,33147,33148,33149,273],{},"For non-OpenShift specific questions, please refer to the ",[41,33150,33152],{"href":33151},"\u002Fdocs\u002Finstall\u002Fkubernetes#common-questions","main kubernetes documentation",[104,33154,31081],{"id":31080},[16433,33156,21376,33157,33159,33161,33170,33230,33234],{},[31085,33158,31087],{},[14,33160,31090],{},[14,33162,31093,33163,33165,33166,32882,33168,32887],{},[18,33164,24102],{}," file\n(replace the ",[18,33167,32836],{},[41,33169,32886],{"href":32885},[50,33171,33173],{"className":165,"code":33172,"language":167,"meta":55,"style":55},"forge:\n  broker:\n    enabled: true\n    podSecurityContext:\n      runAsUser: \u003Cproject-uid>\n      runAsGroup: \u003Cproject-uid>\n      fsGroup: \u003Cproject-uid>\n",[18,33174,33175,33181,33187,33195,33202,33212,33221],{"__ignoreMap":55},[59,33176,33177,33179],{"class":61,"line":62},[59,33178,22305],{"class":174},[59,33180,196],{"class":178},[59,33182,33183,33185],{"class":61,"line":77},[59,33184,29608],{"class":174},[59,33186,196],{"class":178},[59,33188,33189,33191,33193],{"class":61,"line":88},[59,33190,16930],{"class":174},[59,33192,179],{"class":178},[59,33194,3230],{"class":73},[59,33196,33197,33200],{"class":61,"line":99},[59,33198,33199],{"class":174},"    podSecurityContext",[59,33201,196],{"class":178},[59,33203,33204,33207,33209],{"class":61,"line":156},[59,33205,33206],{"class":174},"      runAsUser",[59,33208,179],{"class":178},[59,33210,33211],{"class":69},"\u003Cproject-uid>\n",[59,33213,33214,33217,33219],{"class":61,"line":216},[59,33215,33216],{"class":174},"      runAsGroup",[59,33218,179],{"class":178},[59,33220,33211],{"class":69},[59,33222,33223,33226,33228],{"class":61,"line":224},[59,33224,33225],{"class":174},"      fsGroup",[59,33227,179],{"class":178},[59,33229,33211],{"class":69},[14,33231,30264,33232,273],{},[41,33233,30267],{"href":21703},[14,33235,30556,33236,31131],{},[41,33237,30561],{"href":31129,"rel":33238},[831],[104,33240,31205],{"id":31204},[16433,33242,21376,33243,33245,33253,33308,33312],{},[31085,33244,31087],{},[14,33246,31208,33247,33165,33249,32882,33251,32887],{},[18,33248,24102],{},[18,33250,32836],{},[41,33252,32886],{"href":32885},[50,33254,33256],{"className":165,"code":33255,"language":167,"meta":55,"style":55},"forge:\n  fileStore:\n    enabled: true\n    podSecurityContext:\n      runAsUser: \u003Cproject-uid>\n      runAsGroup: \u003Cproject-uid>\n      fsGroup: \u003Cproject-uid>\n",[18,33257,33258,33264,33270,33278,33284,33292,33300],{"__ignoreMap":55},[59,33259,33260,33262],{"class":61,"line":62},[59,33261,22305],{"class":174},[59,33263,196],{"class":178},[59,33265,33266,33268],{"class":61,"line":77},[59,33267,31226],{"class":174},[59,33269,196],{"class":178},[59,33271,33272,33274,33276],{"class":61,"line":88},[59,33273,16930],{"class":174},[59,33275,179],{"class":178},[59,33277,3230],{"class":73},[59,33279,33280,33282],{"class":61,"line":99},[59,33281,33199],{"class":174},[59,33283,196],{"class":178},[59,33285,33286,33288,33290],{"class":61,"line":156},[59,33287,33206],{"class":174},[59,33289,179],{"class":178},[59,33291,33211],{"class":69},[59,33293,33294,33296,33298],{"class":61,"line":216},[59,33295,33216],{"class":174},[59,33297,179],{"class":178},[59,33299,33211],{"class":69},[59,33301,33302,33304,33306],{"class":61,"line":224},[59,33303,33225],{"class":174},[59,33305,179],{"class":178},[59,33307,33211],{"class":69},[14,33309,30264,33310,273],{},[41,33311,30267],{"href":21703},[14,33313,30556,33314,31247],{},[41,33315,30561],{"href":31245,"rel":33316},[831],[316,33318,33319],{},"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 .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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":55,"searchDepth":77,"depth":77,"links":33321},[33322,33325,33331,33332,33333,33334],{"id":25,"depth":77,"text":26,"children":33323},[33324],{"id":21034,"depth":88,"text":21035},{"id":9163,"depth":77,"text":9164,"children":33326},[33327,33328,33329,33330],{"id":32765,"depth":88,"text":32766},{"id":29929,"depth":88,"text":29930},{"id":29958,"depth":88,"text":29959},{"id":30034,"depth":88,"text":30035},{"id":21873,"depth":77,"text":30148},{"id":9513,"depth":77,"text":584},{"id":9855,"depth":77,"text":9856},{"id":22177,"depth":77,"text":22178,"children":33335},[33336,33337],{"id":31080,"depth":88,"text":31081},{"id":31204,"depth":88,"text":31205},{},"OpenShift Installation","install\u002Fkubernetes\u002Fopenshift.md","\u002Fdocs\u002Finstall\u002Fkubernetes\u002Fopenshift",{"description":55},"docs\u002Finstall\u002Fkubernetes\u002Fopenshift","yRQOVGvIhzyvQiTZJ5e1Ea4Zqj30QJQkLI7stYHQkk0",{"id":33346,"title":1140,"body":33347,"description":22763,"extension":329,"layout":330,"meta":33521,"navGroup":330,"navOrder":330,"navTitle":33522,"navigation":187,"originalPath":33523,"path":33524,"redirect":330,"seo":33525,"stem":33526,"updated":337,"version":338,"__hash__":33527},"docs\u002Fdocs\u002Finstall\u002Fkubernetes\u002Fstacks.md",{"type":7,"value":33348,"toc":33518},[33349,33352,33354,33356,33374,33383,33385,33405,33410,33412,33444,33446,33474,33476,33503,33510,33515],[10,33350,1140],{"id":33351},"kubernetes-stacks",[14,33353,22763],{},[14,33355,22766],{},[28,33357,33358,33364,33368],{},[31,33359,33360,22773,33362,273],{},[18,33361,10054],{},[18,33363,10062],{},[31,33365,33366,22781],{},[18,33367,22780],{},[31,33369,33370,22787,33372],{},[18,33371,22786],{},[18,33373,22790],{},[14,33375,22793,33376,20053,33378,22799,33380,273],{},[18,33377,22796],{},[18,33379,22786],{},[41,33381,22804],{"href":22802,"rel":33382},[831],[23,33384,22808],{"id":22807},[14,33386,22811,33387,302,33389,22817,33391,302,33393,20053,33395,22827,33399,22832,33403,22836],{},[18,33388,22814],{},[18,33390,7977],{},[18,33392,22814],{},[18,33394,7977],{},[41,33396,22826],{"href":33397,"rel":33398},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fhelm\u002Ftree\u002Fmain\u002Fnode-red-container",[831],[41,33400,26289],{"href":33401,"rel":33402},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fhelm",[831],[18,33404,22835],{},[14,33406,22839,33407,22845],{},[41,33408,22844],{"href":22842,"rel":33409},[831],[14,33411,22848],{},[50,33413,33414],{"className":22851,"code":22852,"language":66,"meta":55,"style":55},[18,33415,33416,33420,33424,33428,33432,33436,33440],{"__ignoreMap":55},[59,33417,33418],{"class":61,"line":62},[59,33419,22859],{},[59,33421,33422],{"class":61,"line":77},[59,33423,188],{"emptyLinePlaceholder":187},[59,33425,33426],{"class":61,"line":88},[59,33427,22868],{},[59,33429,33430],{"class":61,"line":99},[59,33431,22873],{},[59,33433,33434],{"class":61,"line":156},[59,33435,188],{"emptyLinePlaceholder":187},[59,33437,33438],{"class":61,"line":216},[59,33439,22882],{},[59,33441,33442],{"class":61,"line":224},[59,33443,5847],{},[14,33445,22889],{},[50,33447,33448],{"className":22851,"code":22892,"language":66,"meta":55,"style":55},[18,33449,33450,33454,33458,33462,33466,33470],{"__ignoreMap":55},[59,33451,33452],{"class":61,"line":62},[59,33453,22899],{},[59,33455,33456],{"class":61,"line":77},[59,33457,188],{"emptyLinePlaceholder":187},[59,33459,33460],{"class":61,"line":88},[59,33461,22908],{},[59,33463,33464],{"class":61,"line":99},[59,33465,22913],{},[59,33467,33468],{"class":61,"line":156},[59,33469,188],{"emptyLinePlaceholder":187},[59,33471,33472],{"class":61,"line":216},[59,33473,22922],{},[14,33475,22925],{},[50,33477,33479],{"className":22928,"code":33478,"language":22930,"meta":55,"style":55},"docker build node-red-container\u002FDockerfile-dashboard -t [your.container.registry]\u002Fflowfuse\u002Fnode-red-dashboard:3.0.2\ndocker push [your.container.registry]\u002Fflowfuse\u002Fnode-red-dashboard:3.0.2\n",[18,33480,33481,33494],{"__ignoreMap":55},[59,33482,33483,33485,33487,33489,33491],{"class":61,"line":62},[59,33484,66],{"class":65},[59,33486,22939],{"class":69},[59,33488,22942],{"class":69},[59,33490,22945],{"class":73},[59,33492,33493],{"class":178}," [your.container.registry]\u002Fflowfuse\u002Fnode-red-dashboard:3.0.2\n",[59,33495,33496,33498,33501],{"class":61,"line":77},[59,33497,66],{"class":65},[59,33499,33500],{"class":69}," push",[59,33502,33493],{"class":178},[14,33504,22951,33505,20053,33508,22958],{},[18,33506,33507],{},"[your.container.registry]\u002Fflowforge\u002Fnode-red-dashboard:3.0.2",[18,33509,22957],{},[14,33511,33512,33513,273],{},"Stacks can be changed on a per Node-RED instance basis, see also the\n",[41,33514,22965],{"href":22964},[316,33516,33517],{},"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 .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":55,"searchDepth":77,"depth":77,"links":33519},[33520],{"id":22807,"depth":77,"text":22808},{},"Kubernetes Project Stacks","install\u002Fkubernetes\u002Fstacks.md","\u002Fdocs\u002Finstall\u002Fkubernetes\u002Fstacks",{"title":1140,"description":22763},"docs\u002Finstall\u002Fkubernetes\u002Fstacks","_JhnXZXTpZdYu9WKOJGVo-3cbHCQ4QaTLC9vO7pNy88",{"id":33529,"title":55,"body":33530,"description":55,"extension":329,"layout":532,"meta":33534,"navGroup":3950,"navOrder":88,"navTitle":33535,"navigation":187,"originalPath":33536,"path":33537,"redirect":33538,"seo":33540,"stem":33541,"updated":337,"version":338,"__hash__":33542},"docs\u002Fdocs\u002Fmigration\u002Findex.md",{"type":7,"value":33531,"toc":33532},[],{"title":55,"searchDepth":77,"depth":77,"links":33533},[],{},"Migrating a Node-RED project to FlowFuse","migration\u002FREADME.md","\u002Fdocs\u002Fmigration",{"to":33539},"\u002Fdocs\u002Fmigration\u002Fintroduction",{"description":55},"docs\u002Fmigration\u002Findex","ut18ix6uMkaOwVw1ngxyXEi7nblKjoaitY5jDOYwKtk",{"id":33544,"title":33545,"body":33546,"description":33553,"extension":329,"layout":330,"meta":33625,"navGroup":330,"navOrder":62,"navTitle":4659,"navigation":187,"originalPath":33626,"path":33539,"redirect":330,"seo":33627,"stem":33628,"updated":337,"version":338,"__hash__":33629},"docs\u002Fdocs\u002Fmigration\u002Fintroduction.md","Migrating a Node-RED application to FlowFuse",{"type":7,"value":33547,"toc":33617},[33548,33551,33554,33557,33560,33563,33567,33575,33579,33588,33592,33595,33599,33603],[10,33549,33545],{"id":33550},"migrating-a-node-red-application-to-flowfuse",[14,33552,33553],{},"This guide will help you to move existing Node-RED instances into\nFlowFuse.",[14,33555,33556],{},"When migrating your Node-RED instances into FlowFuse, you'll export\nthe flows, credentials, and environment variables.",[14,33558,33559],{},"Before you start ensure you can log in to FlowFuse Cloud or your own FlowFuse\nserver, and that you have created your Node-RED instance that you wish to move into FlowFuse.",[14,33561,33562],{},"If you have not yet created a Node-RED instance that you want to manage within FlowFuse, you can create the new instance within FlowFuse directly, and the following instructions will not apply in your case.",[23,33564,33566],{"id":33565},"migrating-the-flows-and-credentials","Migrating the flows and credentials",[14,33568,33569,33570,33574],{},"Install the Node-RED tools plugin as explained\n",[41,33571,33573],{"href":33572},"\u002Fdocs\u002Fmigration\u002Fnode-red-tools.md","in the documentation",". After you created a snapshot for\nthe Node-RED instance you wish to move, you'll have copied over the flows and credentials.",[23,33576,33578],{"id":33577},"migrating-environment-variables","Migrating Environment Variables",[14,33580,33581,33582,33587],{},"You can use ",[41,33583,33586],{"href":33584,"rel":33585},"https:\u002F\u002Fflows.nodered.org\u002Fflow\u002F8ebfe9ae218aa5105e7da13db14ac272",[831],"this flow","\nto dump a list of your environment variables into the debug window. For each\nvariable needed for the flow should be added on FlowFuse under\nInstance > Settings > Environment tabs.",[23,33589,33591],{"id":33590},"starting-the-snapshot","Starting the snapshot",[14,33593,33594],{},"Under the snapshots tab, click 'Restore Snapshot' in the kebab menu. The migrated flows\nwill now be started with the modules installed.",[23,33596,33598],{"id":33597},"limitations","Limitations",[104,33600,33602],{"id":33601},"static-files","Static Files",[14,33604,33605,33606,33608,33609,33611,33612,33616],{},"Check your ",[18,33607,6369],{}," file to see if ",[18,33610,16803],{}," has been set, if so then\ncheck for any files in this path. The files in this path need to be manually\nmigrated to ",[41,33613,33615],{"href":33614},"\u002Fdocs\u002Fuser\u002Fstatic-asset-service\u002F","FlowFuse's Static Assets"," service.",{"title":55,"searchDepth":77,"depth":77,"links":33618},[33619,33620,33621,33622],{"id":33565,"depth":77,"text":33566},{"id":33577,"depth":77,"text":33578},{"id":33590,"depth":77,"text":33591},{"id":33597,"depth":77,"text":33598,"children":33623},[33624],{"id":33601,"depth":88,"text":33602},{},"migration\u002Fintroduction.md",{"title":33545,"description":33553},"docs\u002Fmigration\u002Fintroduction","7M3TMuxLmFbVUS1dv2PXc9zaA9PxveegN2i6mWiL7SU",{"id":33631,"title":33632,"body":33633,"description":33640,"extension":329,"layout":330,"meta":33795,"navGroup":330,"navOrder":330,"navTitle":33796,"navigation":187,"originalPath":33797,"path":33798,"redirect":330,"seo":33799,"stem":33800,"updated":337,"version":338,"__hash__":33801},"docs\u002Fdocs\u002Fmigration\u002Fnode-red-tools.md","FlowFuse Node-RED Tools plugin",{"type":7,"value":33634,"toc":33787},[33635,33638,33641,33644,33647,33651,33658,33679,33685,33689,33692,33696,33699,33721,33725,33728,33731,33734,33738,33755,33758,33761,33765,33772,33776,33779,33785],[10,33636,33632],{"id":33637},"flowfuse-node-red-tools-plugin",[14,33639,33640],{},"The Node-RED Tools Plugin is a module you can install into any Node-RED instance\nrunning outside of FlowFuse, that gives you the ability to work on your flows\nlocally.",[14,33642,33643],{},"The current version of the plugin allows you to create a new Instance Snapshot\nusing the flows you have locally and push them into an instance on FlowFuse.",[14,33645,33646],{},"This can make it easier to develop hardware-specific flows locally, that can then\nbe pushed out to your devices through FlowFuse.",[23,33648,33650],{"id":33649},"install","Install",[14,33652,33653,33654,33657],{},"This plugin can be installed through the Manage Palette option by searching for\n",[18,33655,33656],{},"@flowfuse\u002Fnr-tools-plugin"," in the Node-RED editor, or on the command-line:",[50,33659,33661],{"className":52,"code":33660,"language":54,"meta":55,"style":55},"cd ~\u002F.node-red\nnpm install @flowfuse\u002Fnr-tools-plugin\n",[18,33662,33663,33670],{"__ignoreMap":55},[59,33664,33665,33667],{"class":61,"line":62},[59,33666,7534],{"class":73},[59,33668,33669],{"class":69}," ~\u002F.node-red\n",[59,33671,33672,33674,33676],{"class":61,"line":77},[59,33673,7542],{"class":65},[59,33675,7956],{"class":69},[59,33677,33678],{"class":69}," @flowfuse\u002Fnr-tools-plugin\n",[14,33680,33681,33682,273],{},"This assumes the default location of the Node-RED user directory. If you are not\nsure where that is, check the log output when Node-RED starts as it will log the\nfull path to the ",[18,33683,33684],{},"User directory",[23,33686,33688],{"id":33687},"usage","Usage",[14,33690,33691],{},"This initial version of the plugin allows you to create a snapshot of your locally\ndeveloped flows and push them into one of your instances running inside FlowFuse.",[104,33693,33695],{"id":33694},"connecting-to-flowfuse","Connecting to FlowFuse",[14,33697,33698],{},"Before you can do anything, you need to connect the plugin to a FlowFuse platform.",[398,33700,33701,33704,33715,33718],{},[31,33702,33703],{},"In the Node-RED editor, open the FlowFuse Tools sidebar and click on the cog\nicon to open the settings panel.",[31,33705,33706,33707,33711,33712,273],{},"Enter the url of your FlowFuse platform. For example, if you have signed-up\nto ",[41,33708,4067],{"href":33709,"rel":33710},"https:\u002F\u002Fapp.flowfuse.com\u002F",[831]," then use the URL ",[18,33713,33714],{},"https:\u002F\u002Fapp.flowfuse.com",[31,33716,33717],{},"Click connect. This will open another window where you can log in to FlowFuse\nand give permission for the plugin to connect to your account.",[31,33719,33720],{},"Once connected, close the settings panel.",[104,33722,33724],{"id":33723},"working-with-snapshots","Working with Snapshots",[14,33726,33727],{},"The FlowFuse Tools sidebar allows you to browse the teams you are a member of\nand their instances.",[14,33729,33730],{},"When you select an instance, the sidebar lists its snapshots.",[14,33732,33733],{},"You can then create a new snapshot using the flows you have running locally.",[104,33735,33737],{"id":33736},"create-a-snapshot","Create a Snapshot",[398,33739,33740,33746,33749,33752],{},[31,33741,15930,33742,33745],{},[18,33743,33744],{},"+ snapshot"," button to open the Create Snapshot dialog.",[31,33747,33748],{},"In the dialog, enter a name for the snapshot and an optional description.",[31,33750,33751],{},"The dialog lists the modules your flows are using, along with the version number.\nThis information is included in the snapshot when sent back to the FlowFuse instance.\nCheck the notes below on how this is handled within FlowFuse.",[31,33753,33754],{},"Click 'Create Snapshot'",[14,33756,33757],{},"At this point, a new snapshot will be created in FlowFuse. You can then switch\nto the FlowFuse platform and from the Snapshots menu either select the 'Restore Snapshot'\noption to deploy that snapshot, or set it as the Device Target to deploy it to your\ndevices.",[14,33759,33760],{},"We'll be working on improving this workflow in future releases of the plugin - to\nallow you to manage more from within the Node-RED plugin.",[768,33762,33764],{"id":33763},"adding-modules-to-a-snapshot","Adding modules to a snapshot",[14,33766,33767,33768,33771],{},"The snapshot created by the sidebar includes a list of the modules used by the flows.\nIf there are any modules included that have not already been added to your instance\nyou will need to manually add them via the ",[18,33769,33770],{},"Instance Settings -> Palette -> Installed Modules","\nview in the platform.",[768,33773,33775],{"id":33774},"defining-environment-variables","Defining Environment Variables",[14,33777,33778],{},"We do not currently support defining environment variables for the flows from\nwithin the Node-RED Plugin. This means that when you create a snapshot from the\nplugin, the platform will automatically merge in the currently defined environment\nvariables for that instance.",[14,33780,33781,33782,33771],{},"To manage your instance's environment variables, use the ",[18,33783,33784],{},"Instance Settings -> Environment",[316,33786,10183],{},{"title":55,"searchDepth":77,"depth":77,"links":33788},[33789,33790],{"id":33649,"depth":77,"text":33650},{"id":33687,"depth":77,"text":33688,"children":33791},[33792,33793,33794],{"id":33694,"depth":88,"text":33695},{"id":33723,"depth":88,"text":33724},{"id":33736,"depth":88,"text":33737},{},"Node-RED Tools plugin","migration\u002Fnode-red-tools.md","\u002Fdocs\u002Fmigration\u002Fnode-red-tools",{"title":33632,"description":33640},"docs\u002Fmigration\u002Fnode-red-tools","-VBy3X_pJL3uJTTyrL4w8xBpAJ91-Su9teWT2Z8v9Uk",{"id":33803,"title":33804,"body":33805,"description":33819,"extension":329,"layout":330,"meta":33820,"navGroup":4117,"navOrder":88,"navTitle":33821,"navigation":187,"originalPath":33822,"path":33823,"redirect":330,"seo":33824,"stem":33825,"updated":337,"version":338,"__hash__":33826},"docs\u002Fdocs\u002Fpremium-support.md","Customer Support",{"type":7,"value":33806,"toc":33817},[33807,33810],[10,33808,33804],{"id":33809},"customer-support",[14,33811,4120,33812,4125,33814,273],{},[41,33813,4124],{"href":4123},[41,33815,4130],{"href":4128,"rel":33816},[831],{"title":55,"searchDepth":77,"depth":77,"links":33818},[],"Premium customers can get support by filing a ticket. We offer\nsupport for the FlowFuse application and your account, any issues relating to\nNode-RED such as your flows or a 3rd party node should be raised in the\ncommunity forum.",{},"Premium Support","premium-support.md","\u002Fdocs\u002Fpremium-support",{"title":33804,"description":33819},"docs\u002Fpremium-support","arxOlA9i7OyMx-r6fRK-RkXISbBFmVyWIGd6CBmUlcE",{"id":33828,"title":15506,"body":33829,"description":34107,"extension":329,"layout":330,"meta":34108,"navGroup":534,"navOrder":62,"navTitle":15861,"navigation":187,"originalPath":34109,"path":21365,"redirect":330,"seo":34110,"stem":34111,"updated":337,"version":338,"__hash__":34112},"docs\u002Fdocs\u002Fquick-start\u002Findex.md",{"type":7,"value":33830,"toc":34097},[33831,33834,33845,33848,33867,33873,33875,33888,33892,33898,33920,33927,33931,33959,33963,33971,33979,33994,33998,34001,34016,34019,34023,34036,34039,34042,34058,34060,34063,34088,34094],[10,33832,15506],{"id":33833},"quick-start-guide",[14,33835,33836,33837,302,33841,273],{},"This guide provides a streamlined process for setting up and running the FlowFuse platform using ",[41,33838,1209],{"href":33839,"rel":33840},"https:\u002F\u002Fdocs.docker.com\u002Fget-started\u002F",[831],[41,33842,162],{"href":33843,"rel":33844},"https:\u002F\u002Fdocs.docker.com\u002Fcompose\u002F",[831],[14,33846,33847],{},"The Docker Compose file deploys the following services:",[28,33849,33850,33856,33861],{},[31,33851,33852,33855],{},[364,33853,33854],{},"FlowFuse Platform",": Includes the core application, MQTT broker, and file server for storage",[31,33857,33858,33860],{},[364,33859,21459],{}," A pre-configured database for storing platform data",[31,33862,33863,33866],{},[364,33864,33865],{},"Proxy Server:"," A pre-configured proxy server for managing HTTP traffic",[14,33868,33869,33870,273],{},"For a full installation guide, including how to setup FlowFuse in a production environment, please refer to the dedicated page for ",[41,33871,33872],{"href":22750},"running FlowFuse on Docker",[23,33874,26],{"id":25},[14,33876,33877,33878,302,33882,21445,33885,33887],{},"Before you begin, ensure you have ",[41,33879,1209],{"href":33880,"rel":33881},"https:\u002F\u002Fdocs.docker.com\u002Fengine\u002Finstall\u002F",[831],[41,33883,162],{"href":21443,"rel":33884},[831],[18,33886,21448],{}," version or higher) installed on your system (either as a standalone binary or as a Docker plugin).",[23,33889,33891],{"id":33890},"step-1-prepare-your-domain","Step 1: Prepare your domain",[14,33893,33894,33895,33897],{},"FlowFuse requires a domain name to work properly — it uses subdomains to run each Node-RED instance separately, so ",[18,33896,18615],{}," won't work here.",[14,33899,33900,33901,1706,33903,33906,33907,33910,33911,33913,33914,1706,33916,33919],{},"If you own a domain (e.g., ",[18,33902,1312],{},[18,33904,33905],{},"flowfuse.example.com","), create an ",[364,33908,33909],{},"A record"," pointing to your server's IP address, and another ",[364,33912,33909],{}," for the wildcard subdomain (e.g., ",[18,33915,21530],{},[18,33917,33918],{},"*.flowfuse.example.com",") pointing to the same address. That's all the setup needed.",[14,33921,33922,33923,273],{},"If you don't have a domain yet and just want to try FlowFuse locally, see ",[41,33924,33926],{"href":33925},"\u002Fdocs\u002Finstall\u002Fdns-setup#no-local-dns-server","setting up an alternative to DNS",[23,33928,33930],{"id":33929},"step-2-download-files","Step 2: Download files",[50,33932,33933],{"className":52,"code":21564,"language":54,"meta":55,"style":55},[18,33934,33935,33947],{"__ignoreMap":55},[59,33936,33937,33939,33941,33943,33945],{"class":61,"line":62},[59,33938,1381],{"class":65},[59,33940,15015],{"class":73},[59,33942,15021],{"class":73},[59,33944,21577],{"class":69},[59,33946,21580],{"class":69},[59,33948,33949,33951,33953,33955,33957],{"class":61,"line":77},[59,33950,1381],{"class":65},[59,33952,15015],{"class":73},[59,33954,15021],{"class":73},[59,33956,21591],{"class":69},[59,33958,21594],{"class":69},[23,33960,33962],{"id":33961},"step-3-provide-domain-name","Step 3: Provide domain name",[14,33964,33965,33966,21610,33968,33970],{},"Edit the downloaded ",[18,33967,21560],{},[18,33969,21613],{}," variable with your domain.",[14,33972,33581,33973,21621,33975,21624,33977,21627],{},[18,33974,21620],{},[18,33976,21613],{},[18,33978,21560],{},[50,33980,33982],{"className":52,"code":33981,"language":54,"meta":55,"style":55},"sed -i.bak 's\u002F^DOMAIN=.*\u002FDOMAIN=example.com\u002F' .env\n",[18,33983,33984],{"__ignoreMap":55},[59,33985,33986,33988,33990,33992],{"class":61,"line":62},[59,33987,21620],{"class":65},[59,33989,22321],{"class":73},[59,33991,21642],{"class":69},[59,33993,21645],{"class":69},[23,33995,33997],{"id":33996},"step-4-start-the-application","Step 4: Start the Application",[14,33999,34000],{},"Run the following command to deploy FlowFuse:",[50,34002,34004],{"className":52,"code":34003,"language":54,"meta":55,"style":55},"docker compose up -d\n",[18,34005,34006],{"__ignoreMap":55},[59,34007,34008,34010,34012,34014],{"class":61,"line":62},[59,34009,66],{"class":65},[59,34011,21903],{"class":69},[59,34013,21917],{"class":69},[59,34015,21920],{"class":73},[14,34017,34018],{},"This downloads the necessary Docker images, runs initial setup, and starts all services in detached mode.",[23,34020,34022],{"id":34021},"step-5-complete-the-application-setup","Step 5: Complete the application Setup",[14,34024,34025,34026,34029,34030,34033,34034,273],{},"Open your web browser and navigate to ",[18,34027,34028],{},"http:\u002F\u002Fforge.\u003Cyour-domain>\u002Fsetup"," (e.g., ",[18,34031,34032],{},"http:\u002F\u002Fforge.example.com\u002Fsetup","). You will be redirected to the setup page where you can create your admin account and set up your instance.\nFor detailed information about first setup and configuration, follow ",[41,34035,2587],{"href":21164},[23,34037,34038],{"id":32220},"Cleanup",[14,34040,34041],{},"To stop and remove the FlowFuse application, run the following command:",[50,34043,34045],{"className":52,"code":34044,"language":54,"meta":55,"style":55},"docker compose down -v\n",[18,34046,34047],{"__ignoreMap":55},[59,34048,34049,34051,34053,34056],{"class":61,"line":62},[59,34050,66],{"class":65},[59,34052,21903],{"class":69},[59,34054,34055],{"class":69}," down",[59,34057,15122],{"class":73},[23,34059,12621],{"id":12620},[14,34061,34062],{},"If you encounter any issues, please check the following:",[398,34064,34065,34068,34071],{},[31,34066,34067],{},"Ensure all prerequisites are correctly installed",[31,34069,34070],{},"Verify your DNS settings are correct and have propagated",[31,34072,34073,34074],{},"Check the Docker logs for any error messages:\n",[50,34075,34077],{"className":52,"code":34076,"language":54,"meta":55,"style":55},"docker compose logs\n",[18,34078,34079],{"__ignoreMap":55},[59,34080,34081,34083,34085],{"class":61,"line":62},[59,34082,66],{"class":65},[59,34084,21903],{"class":69},[59,34086,34087],{"class":69}," logs\n",[14,34089,34090,34091,273],{},"For more detailed information or advanced configuration options for running FlowFuse on Docker, please refer to our ",[41,34092,34093],{"href":22750},"full documentation",[316,34095,34096],{},"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);}",{"title":55,"searchDepth":77,"depth":77,"links":34098},[34099,34100,34101,34102,34103,34104,34105,34106],{"id":25,"depth":77,"text":26},{"id":33890,"depth":77,"text":33891},{"id":33929,"depth":77,"text":33930},{"id":33961,"depth":77,"text":33962},{"id":33996,"depth":77,"text":33997},{"id":34021,"depth":77,"text":34022},{"id":32220,"depth":77,"text":34038},{"id":12620,"depth":77,"text":12621},"This guide provides a streamlined process for setting up and running the FlowFuse platform using Docker and Docker Compose.",{},"quick-start\u002FREADME.md",{"title":15506,"description":34107},"docs\u002Fquick-start\u002Findex","t8nIFnXlSSBoGRNeaZNHG_V-sAnytbcU07VgLhqQTsw",{"id":34114,"title":25044,"body":34115,"description":34121,"extension":329,"layout":330,"meta":34548,"navGroup":534,"navOrder":88,"navTitle":25044,"navigation":187,"originalPath":34549,"path":18010,"redirect":330,"seo":34550,"stem":34551,"updated":337,"version":338,"__hash__":34552},"docs\u002Fdocs\u002Fupgrade\u002Findex.md",{"type":7,"value":34116,"toc":34536},[34117,34119,34122,34125,34128,34132,34135,34151,34155,34159,34162,34165,34174,34180,34188,34191,34194,34197,34214,34222,34226,34234,34253,34257,34269,34281,34285,34298,34301,34305,34311,34315,34322,34325,34328,34341,34345,34349,34352,34354,34364,34368,34372,34375,34381,34385,34388,34391,34468,34474,34477,34480,34483,34486,34490,34497,34500,34534],[10,34118,25044],{"id":25043},[14,34120,34121],{},"If you are upgrading an existing FlowFuse installation, this page will list any\nparticular requirements needed to upgrade to a given level.",[14,34123,34124],{},"If you are upgrading across multiple versions, make sure you check the requirements\nfor each version you are upgrading across.",[14,34126,34127],{},"Note that we do not support downgrading FlowFuse to previous levels once an upgrade\nhas been performed.",[23,34129,34131],{"id":34130},"general-guideline","General guideline",[14,34133,34134],{},"Details of how to upgrade can be found for each deployment model:",[28,34136,34137,34142,34146],{},[31,34138,34139],{},[41,34140,24198],{"href":34141},"\u002Fdocs\u002Fcontribute\u002Flocal#upgrade",[31,34143,34144],{},[41,34145,1209],{"href":21172},[31,34147,34148],{},[41,34149,6291],{"href":34150},"\u002Fdocs\u002Finstall\u002Fkubernetes#upgrade",[104,34152,34154],{"id":34153},"upgrading-to-260","Upgrading to 2.6.0",[768,34156,34158],{"id":34157},"required-aws-eks-configuration-change","Required AWS EKS configuration change",[14,34160,34161],{},"This release introduces the new Embedded Editor which integrates the Node-RED editor with the FlowFuse dashboard when using Node-RED 4.0. This has required some changes to be made on how certain HTTP headers are passed between the NGINX Ingress controller and AWS NLB.",[14,34163,34164],{},"The following configuration change must be applied otherwise users will not be able to login to Node-RED 4.0 instances.",[14,34166,34167,34168,34173],{},"The following configuration needs to be added in the values passed to the ingress-nginx helm chart. See ",[41,34169,34172],{"href":34170,"rel":34171},"https:\u002F\u002Fflowfuse.com\u002Fdocs\u002Finstall\u002Fkubernetes\u002Faws\u002F#nginx-ingress",[831],"full configuration"," for the reference.",[50,34175,34178],{"className":34176,"code":34177,"language":3920},[3918],"controller:\n   config:\n      use-proxy-protocol: true\n   service:\n      annotations:\n         service.beta.kubernetes.io\u002Faws-load-balancer-target-group-attributes: proxy_protocol_v2.enabled=true\n   externalTrafficPolicy: Cluster\n",[18,34179,34177],{"__ignoreMap":55},[14,34181,34182,34183,273],{},"The Proxy Protocol feature will be enabled only on newly created Target Groups.\nTo enable the Proxy Protocol on an existing Target Group, manual intervention is required. For detailed instructions, please refer to the ",[41,34184,34187],{"href":34185,"rel":34186},"https:\u002F\u002Fdocs.aws.amazon.com\u002Felasticloadbalancing\u002Flatest\u002Fnetwork\u002Fload-balancer-target-groups.html#enable-proxy-protocol",[831],"official AWS documentation",[768,34189,23785],{"id":34190},"persistent-storage",[14,34192,34193],{},"As part of this release there is a new option for Persistent File Storage for Kubernetes based deployments.\nThis change removes the need to use the customised File Nodes and the FlowFuse File Server by mounting a\nPersistent Volume into the Pods running the instances.",[14,34195,34196],{},"To enable this feature the following needs to be created",[28,34198,34199,34205],{},[31,34200,34201,34202],{},"A Kubernetes StorageClass that points to storage provider that can\ndynamically provision new Persistent Volumes. e.g. the ",[41,34203,23926],{"href":23924,"rel":34204},[831],[31,34206,34207,34208,34213],{},"Pass the following values to the FlowFuse Helm Chart\n",[50,34209,34211],{"className":34210,"code":23932,"language":3920},[3918],[18,34212,23932],{"__ignoreMap":55},"\nWhere size is the default size for the volume.",[14,34215,34216,34217,273],{},"Details for how to setup a AWS EFS backed StorageClass can be found on the aws-efs-csi-driver ",[41,34218,34221],{"href":34219,"rel":34220},"https:\u002F\u002Fgithub.com\u002Fkubernetes-sigs\u002Faws-efs-csi-driver\u002Fblob\u002Fmaster\u002Fdocs\u002Fefs-create-filesystem.md",[831],"site",[104,34223,34225],{"id":34224},"upgrading-to-200","Upgrading to 2.0.0",[1110,34227,34228],{},[14,34229,34230,34233],{},[364,34231,34232],{},"⚠️","  Breaking changes introduced!",[14,34235,34236,34237,34240,34241,34246,34247,34249,34250,24043],{},"Together with new application features, this ",[364,34238,34239],{},"release 2.0.0 introduces breaking changes"," in Flowfuse Helm chart.\nIf you are managing your local Flowfuse instance using our ",[41,34242,34245],{"href":34243,"rel":34244},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fhelm\u002Ftree\u002Fmain\u002Fhelm\u002Fflowfuse",[831],"Helm Chart",", please refer to the ",[41,34248,9855],{"href":34150}," section of the Kubernetes installation guide or the Helm Chart ",[41,34251,18566],{"href":30208,"rel":34252},[831],[104,34254,34256],{"id":34255},"upgrading-to-110","Upgrading to 1.10",[14,34258,34259,34260,34262,34263,34265,34266,273],{},"Endpoint Rate Limiting is now available to FlowFuse. This is disabled by default, but can be enabled by setting the ",[18,34261,19570],{}," config setting to ",[18,34264,3558],{},".\nThe documentation for this is available ",[41,34267,785],{"href":34268},"\u002Fdocs\u002Finstall\u002Fconfiguration#rate-limiting-configuration",[14,34270,372,34271,34275,34276,34280],{},[41,34272,34274],{"href":34273},"\u002Fdocs\u002Fuser\u002Fconcepts#team-type","TeamType concept"," was expanded in this release.\nIt is used to control what Instance Types are available for different teams, as\nwell as any additional limits that should be applied. When creating new Instance\nTypes, they must now be ",[41,34277,34279],{"href":34278},"\u002Fdocs\u002Fadmin\u002Fintroduction#managing-instance-types","manually enabled","\nfor the Team Types on the platform.",[104,34282,34284],{"id":34283},"upgrading-to-15","Upgrading to 1.5",[14,34286,34287,34288,34292,34293,34297],{},"The main change in this release was a change in our terminology around the individual\nNode-RED instances. We have introduced the ",[41,34289,34291],{"href":34290},"\u002Fdocs\u002Fuser\u002Fconcepts#application","Application concept","\nas a way to group individual ",[41,34294,34296],{"href":34295},"\u002Fdocs\u002Fuser\u002Fconcepts#instance","Node-RED instances"," (what we previously called Projects).",[14,34299,34300],{},"The term 'Project' is being phased out. You may still see it crop up, such as\nin some of the external APIs, but we're working our way through removing it.",[104,34302,34304],{"id":34303},"upgrading-to-13","Upgrading to 1.3",[14,34306,34307,34308,273],{},"To enable the Team Library and FlowFuse-based Authentication of HTTP routes each\nNode-RED instance will need to be updated to the ",[41,34309,34310],{"href":22964},"latest Stack",[768,34312,34314],{"id":34313},"persistent-context-added","Persistent Context added",[14,34316,34317,34318,273],{},"The new Persistent Context feature is available to projects when running with a\n",[41,34319,34321],{"href":34320},"\u002Fdocs\u002Fupgrade\u002Fopen-source-to-premium","premium license",[14,34323,34324],{},"This feature requires additional configuration to be added to the File Server component\nthat was introduced in FlowFuse 1.1.",[14,34326,34327],{},"Details of how to configure this can be found at the following links:",[28,34329,34330,34335],{},[31,34331,34332],{},[41,34333,24198],{"href":34334},"\u002Fdocs\u002Finstall\u002Ffile-storage#localfs",[31,34336,34337],{},[41,34338,34340],{"href":34339},"\u002Fdocs\u002Finstall\u002Ffile-storage#configuring","Docker and Kubernetes",[104,34342,34344],{"id":34343},"upgrading-to-11","Upgrading to 1.1",[768,34346,34348],{"id":34347},"file-server-added","File Server added",[14,34350,34351],{},"This release introduces a system for supporting persistent file storage when running on\nDocker or Kubernetes (it will also work with LocalFS, but is not required as projects\nhave access to the hosts filesystem).",[14,34353,34327],{},[28,34355,34356,34360],{},[31,34357,34358],{},[41,34359,24198],{"href":34334},[31,34361,34362],{},[41,34363,34340],{"href":34339},[104,34365,34367],{"id":34366},"upgrading-to-08","Upgrading to 0.8",[768,34369,34371],{"id":34370},"mqtt-broker-added","MQTT Broker added",[14,34373,34374],{},"This release introduces an MQTT Broker into the FlowFuse platform used to communicate\nbetween devices and the core platform.",[14,34376,34377,34378],{},"For LocalFS users, they will need to manually setup the broker and ensure it is\nproperly configured. The documentation for this is available ",[41,34379,785],{"href":34380},"\u002Fdocs\u002Fcontribute\u002Flocal#setting-up-mosquitto-(optional)",[768,34382,34384],{"id":34383},"localfs-users","LocalFS Users",[14,34386,34387],{},"With the 0.8 release we have updated the version of the SQLite3 module used by the localfs\ncontainer driver. We are moving from v5.0.2 to v5.0.8.",[14,34389,34390],{},"There appears to be a clash with the bcrypt module when doing an in place upgrade of the\nSQLite3 module that gives an error similar to the following:",[50,34392,34394],{"className":52,"code":34393,"language":54,"meta":55,"style":55},"npm ERR! path \u002Fopt\u002Fshare\u002Fprojects\u002Fflowforge\u002Fsqlite-test\u002Fnode_modules\u002Fsqlite3\nnpm ERR! command failed\nnpm ERR! command sh -c node-pre-gyp install --fallback-to-build\nnpm ERR! sh: line 1: node-pre-gyp: command not found\n",[18,34395,34396,34409,34421,34442],{"__ignoreMap":55},[59,34397,34398,34400,34403,34406],{"class":61,"line":62},[59,34399,7542],{"class":65},[59,34401,34402],{"class":69}," ERR!",[59,34404,34405],{"class":69}," path",[59,34407,34408],{"class":69}," \u002Fopt\u002Fshare\u002Fprojects\u002Fflowforge\u002Fsqlite-test\u002Fnode_modules\u002Fsqlite3\n",[59,34410,34411,34413,34415,34418],{"class":61,"line":77},[59,34412,7542],{"class":65},[59,34414,34402],{"class":69},[59,34416,34417],{"class":69}," command",[59,34419,34420],{"class":69}," failed\n",[59,34422,34423,34425,34427,34429,34432,34434,34437,34439],{"class":61,"line":88},[59,34424,7542],{"class":65},[59,34426,34402],{"class":69},[59,34428,34417],{"class":69},[59,34430,34431],{"class":69}," sh",[59,34433,9670],{"class":73},[59,34435,34436],{"class":69}," node-pre-gyp",[59,34438,7956],{"class":69},[59,34440,34441],{"class":73}," --fallback-to-build\n",[59,34443,34444,34446,34448,34451,34454,34457,34460,34462,34465],{"class":61,"line":99},[59,34445,7542],{"class":65},[59,34447,34402],{"class":69},[59,34449,34450],{"class":69}," sh:",[59,34452,34453],{"class":69}," line",[59,34455,34456],{"class":69}," 1:",[59,34458,34459],{"class":69}," node-pre-gyp:",[59,34461,34417],{"class":69},[59,34463,34464],{"class":69}," not",[59,34466,34467],{"class":69}," found\n",[14,34469,34470,34471,34473],{},"If you see this then the simplest fix is to remove the ",[18,34472,17491],{}," directory and reinstall\nthe modules.",[768,34475,790],{"id":34476},"project-nodes",[14,34478,34479],{},"This release adds support for the new Project Link nodes that can be used to send\nmessages between projects seamlessly.",[14,34481,34482],{},"These nodes require the MQTT Broker to be properly configured.",[14,34484,34485],{},"To deploy flows using these nodes to a Device will require the Device to be running\nthe latest 0.2.0 release. They will also need to have their credentials regenerated\nonce the MQTT Broker has been added.",[104,34487,34489],{"id":34488},"upgrading-to-07","Upgrading to 0.7",[14,34491,34492,34493,273],{},"The 0.7 release introduces the ",[41,34494,34496],{"href":34495},"\u002Fdocs\u002Fuser\u002Fconcepts#instance-type","ProjectType concept",[14,34498,34499],{},"After upgrading to 0.7, an administrator must perform the following tasks before\nusers will be able to create new projects:",[398,34501,34502,34516],{},[31,34503,34504,34505],{},"Create a Project Type.\n",[398,34506,34507,34510,34513],{},[31,34508,34509],{},"On the Administrator Settings -> Project Types page, click 'Create project type'.",[31,34511,34512],{},"Provide a name and description. If you have billing enabled, copy in the default\nStripe Product\u002FPrice IDs from your runtime settings file.",[31,34514,34515],{},"Click 'create'",[31,34517,34518,34519],{},"Assign your existing stacks to that type\n",[398,34520,34521,34524,34527],{},[31,34522,34523],{},"On the Administrator Settings -> Stacks page, edit each existing stack via\nthe drop-down menu in the table.",[31,34525,34526],{},"As a one-time action, set its Project Type to the one just created.",[31,34528,34529,34530,34533],{},"Click 'save'. This will update the stack ",[1160,34531,34532],{},"and"," all existing projects to\nbe associated with the new Project Type",[316,34535,17916],{},{"title":55,"searchDepth":77,"depth":77,"links":34537},[34538],{"id":34130,"depth":77,"text":34131,"children":34539},[34540,34541,34542,34543,34544,34545,34546,34547],{"id":34153,"depth":88,"text":34154},{"id":34224,"depth":88,"text":34225},{"id":34255,"depth":88,"text":34256},{"id":34283,"depth":88,"text":34284},{"id":34303,"depth":88,"text":34304},{"id":34343,"depth":88,"text":34344},{"id":34366,"depth":88,"text":34367},{"id":34488,"depth":88,"text":34489},{},"upgrade\u002FREADME.md",{"title":25044,"description":34121},"docs\u002Fupgrade\u002Findex","MLMxIbx4NOePessMe0RBGsDPLynrshYiZrTfxu_1CV0",{"id":34554,"title":34555,"body":34556,"description":55,"extension":329,"layout":330,"meta":34586,"navGroup":330,"navOrder":330,"navTitle":34587,"navigation":187,"originalPath":34588,"path":34320,"redirect":330,"seo":34589,"stem":34590,"updated":337,"version":338,"__hash__":34591},"docs\u002Fdocs\u002Fupgrade\u002Fopen-source-to-premium.md","Open Source To Premium",{"type":7,"value":34557,"toc":34581},[34558,34562,34568,34571,34575,34578],[23,34559,34561],{"id":34560},"upgrading-to-flowfuse-enterprise","Upgrading to FlowFuse Enterprise",[14,34563,34564,34565,34567],{},"For self-managed FlowFuse installations without a license you can unlock more\nfeatures with a enterprise license. As an admin a license can be uploaded to\nFlowFuse in the admin panel, under the settings tab. When a license is uploaded\na restart of the ",[18,34566,22305],{}," app is required.",[14,34569,34570],{},"After the forge application has restarted, the Node-RED runtimes need to be\nupdated to leverage these features. As restarting Node-RED might need to be\ncoordinated, FlowFuse will not automatically restart all instances.",[104,34572,34574],{"id":34573},"reusing-flowfuse-licenses","Reusing FlowFuse licenses",[14,34576,34577],{},"A single license may only be applied to one FlowFuse platform at any time. Running multiple FlowFuse platforms with the same license key is against the terms of the subscription.",[14,34579,34580],{},"A license may be reused on another FlowFuse platform if the original platform is no longer running. For example, if the FlowFuse platform is reinstalled onto new hardware, the license from the original install can be reused.",{"title":55,"searchDepth":77,"depth":77,"links":34582},[34583],{"id":34560,"depth":77,"text":34561,"children":34584},[34585],{"id":34573,"depth":88,"text":34574},{},"Installing a license","upgrade\u002Fopen-source-to-premium.md",{"description":55},"docs\u002Fupgrade\u002Fopen-source-to-premium","O2sRzD-zYOQ7uXpULsuhRhWxN9VU0cRO9jQw_qHrXM0",{"id":34593,"title":34594,"body":34595,"description":34602,"extension":329,"layout":330,"meta":34679,"navGroup":330,"navOrder":77,"navTitle":34594,"navigation":187,"originalPath":34680,"path":992,"redirect":330,"seo":34681,"stem":34682,"updated":337,"version":338,"__hash__":34683},"docs\u002Fdocs\u002Fuser\u002Fbill-of-materials.md","Bill of Materials",{"type":7,"value":34596,"toc":34671},[34597,34600,34603,34607,34614,34620,34624,34627,34641,34643,34645,34659,34663],[10,34598,34594],{"id":34599},"bill-of-materials",[14,34601,34602],{},"The Application Bill of Materials (BoM) provides a comprehensive overview of all instance dependencies for each application. This feature gives you visibility and control to monitor, manage, and analyze the components your applications rely on.",[23,34604,34606],{"id":34605},"accessing-the-bill-of-materials","Accessing the Bill of Materials",[14,34608,34609,34610,34613],{},"The BoM is located in the ",[364,34611,34612],{},"Dependencies"," tab of each application.",[14,34615,34616],{},[638,34617],{"alt":34618,"dataZoomable":55,"src":34619},"bom.png","\u002Fdocs\u002Fuser\u002Fimages\u002Fbom.png",[23,34621,34623],{"id":34622},"search-functionality","Search Functionality",[14,34625,34626],{},"Use the integrated search to efficiently find devices and instances assigned to your application. Search by:",[28,34628,34629,34632,34635,34638],{},[31,34630,34631],{},"Instance or device name",[31,34633,34634],{},"Package name",[31,34636,34637],{},"Specific dependency",[31,34639,34640],{},"Dependency version",[23,34642,26],{"id":25},[104,34644,4067],{"id":4081},[28,34646,34647,34653],{},[31,34648,34649,34650],{},"Instance Stack with launcher version ",[364,34651,34652],{},"2.9.0 or higher",[31,34654,34655,34658],{},[364,34656,34657],{},"Enterprise Team Type"," required",[104,34660,34662],{"id":34661},"self-hosted","Self-Hosted",[28,34664,34665],{},[31,34666,34667,34668],{},"Available only with ",[364,34669,34670],{},"Enterprise license",{"title":55,"searchDepth":77,"depth":77,"links":34672},[34673,34674,34675],{"id":34605,"depth":77,"text":34606},{"id":34622,"depth":77,"text":34623},{"id":25,"depth":77,"text":26,"children":34676},[34677,34678],{"id":4081,"depth":88,"text":4067},{"id":34661,"depth":88,"text":34662},{},"user\u002Fbill-of-materials.md",{"title":34594,"description":34602},"docs\u002Fuser\u002Fbill-of-materials","a2PhI4XJo4cj-ZjFvZKUpzkTQrmaIsuekbNWjioCCDM",{"id":34685,"title":34686,"body":34687,"description":34743,"extension":329,"layout":330,"meta":34744,"navGroup":330,"navOrder":330,"navTitle":34686,"navigation":187,"originalPath":34745,"path":22964,"redirect":330,"seo":34746,"stem":34747,"updated":337,"version":338,"__hash__":34748},"docs\u002Fdocs\u002Fuser\u002Fchangestack.md","Changing the Stack",{"type":7,"value":34688,"toc":34741},[34689,34692,34699,34702,34707,34710,34713,34733,34736],[10,34690,34686],{"id":34691},"changing-the-stack",[14,34693,34694,34698],{},[41,34695,34697],{"href":34696},"\u002Fdocs\u002Fuser\u002Fconcepts#stack","Stacks"," define various aspects of how Node-RED instances run - including the version of Node-RED being used.",[14,34700,34701],{},"FlowFuse allows you to change the stack an instance is using - providing a way\nto upgrade Node-RED.",[14,34703,34704,34706],{},[364,34705,1798],{}," Stacks are created by Administrators and made available to the teams\nand users of the platform.",[14,34708,34709],{},"When an Administrator creates a new version of a Stack your instance is using,\nthe platform will notify you that there is a new version available.",[14,34711,34712],{},"To change an instance's stack:",[398,34714,34715,34720,34725,34728],{},[31,34716,34717,34718,16058],{},"Go to the instance's page and select the ",[364,34719,17558],{},[31,34721,15930,34722,15933],{},[364,34723,34724],{},"Change Instance Stack",[31,34726,34727],{},"You will be prompted to select the new stack.",[31,34729,15596,34730],{},[364,34731,34732],{},"Change Stack",[14,34734,34735],{},"Your instance will now be restarted on the new stack.",[14,34737,34738,34740],{},[364,34739,1798],{}," Changing the stack causes Node-RED to be stopped and restarted. This\nwill require a short downtime of the flows.",{"title":55,"searchDepth":77,"depth":77,"links":34742},[],"Stacks define various aspects of how Node-RED instances run - including the version of Node-RED being used.",{},"user\u002Fchangestack.md",{"title":34686,"description":34743},"docs\u002Fuser\u002Fchangestack","8ueaHzRBxdZuAlCWiyv8ZTan6IduT-XezZ6xP0uBQ_U",{"id":34750,"title":591,"body":34751,"description":34758,"extension":329,"layout":330,"meta":35331,"navGroup":330,"navOrder":77,"navTitle":591,"navigation":187,"originalPath":35332,"path":35333,"redirect":330,"seo":35334,"stem":35335,"updated":337,"version":338,"__hash__":35336},"docs\u002Fdocs\u002Fuser\u002Fconcepts.md",{"type":7,"value":34752,"toc":35305},[34753,34756,34759,34763,34927,34929,34932,34940,34943,34949,34952,34957,34960,34963,34966,34969,34974,34979,34982,34987,34990,35049,35052,35057,35060,35063,35070,35073,35076,35079,35082,35085,35088,35090,35093,35096,35103,35106,35109,35111,35114,35120,35123,35126,35129,35135,35142,35145,35148,35151,35159,35166,35169,35172,35202,35205,35208,35219,35222,35225,35228,35231,35237,35255,35257,35262,35265,35271,35276,35279,35282,35285,35290,35296,35299],[10,34754,591],{"id":34755},"flowfuse-concepts",[14,34757,34758],{},"FlowFuse makes it easy to create, manage, and scale Node-RED instances. The platform introduces a few core concepts to help you organize and work with it effectively. Throughout the platform, you'll also see ⓘ icons that you can click on to get pop-up explanations for different features and terms.",[23,34760,34762],{"id":34761},"table-of-contents","Table of Contents",[28,34764,34765],{},[31,34766,34767,34770],{},[41,34768,591],{"href":34769},"#flowfuse-concepts",[28,34771,34772,34777,34789,34802,34897,34922],{},[31,34773,34774],{},[41,34775,34762],{"href":34776},"#table-of-contents",[31,34778,34779,34782],{},[41,34780,4154],{"href":34781},"#team",[28,34783,34784],{},[31,34785,34786],{},[41,34787,4324],{"href":34788},"#team-type",[31,34790,34791,34794],{},[41,34792,15613],{"href":34793},"#application",[28,34795,34796],{},[31,34797,34798],{},[41,34799,34801],{"href":34800},"#devops-pipeline","DevOps Pipeline",[31,34803,34804,34807],{},[41,34805,16075],{"href":34806},"#instance",[28,34808,34809,34815,34821,34827,34833,34839,34845,34851,34871],{},[31,34810,34811],{},[41,34812,34814],{"href":34813},"#hosted-instance","Hosted Instance",[31,34816,34817],{},[41,34818,34820],{"href":34819},"#remote-instance","Remote Instance",[31,34822,34823],{},[41,34824,34826],{"href":34825},"#instance-configuration","Instance Configuration",[31,34828,34829],{},[41,34830,34832],{"href":34831},"#instance-type","Instance Type",[31,34834,34835],{},[41,34836,34838],{"href":34837},"#stack","Stack",[31,34840,34841],{},[41,34842,34844],{"href":34843},"#blueprint","Blueprint",[31,34846,34847],{},[41,34848,34850],{"href":34849},"#template","Template",[31,34852,34853,34857],{},[41,34854,34856],{"href":34855},"#snapshot","Snapshot",[28,34858,34859,34865],{},[31,34860,34861],{},[41,34862,34864],{"href":34863},"#hosted-instance-snapshot","Hosted Instance Snapshot",[31,34866,34867],{},[41,34868,34870],{"href":34869},"#remote-instance-snapshot","Remote Instance Snapshot",[31,34872,34873,34877],{},[41,34874,34876],{"href":34875},"#assigning-remote-instances-to-hosted-instances","Assigning Remote Instances to Hosted Instances",[28,34878,34879,34885,34891],{},[31,34880,34881],{},[41,34882,34884],{"href":34883},"#how-it-works","How It Works",[31,34886,34887],{},[41,34888,34890],{"href":34889},"#assignment-rules","Assignment Rules",[31,34892,34893],{},[41,34894,34896],{"href":34895},"#when-to-use-instance-assignment-vs-devops-pipelines","When to Use Instance Assignment vs DevOps Pipelines",[31,34898,34899,34903],{},[41,34900,34902],{"href":34901},"#device","Device",[28,34904,34905,34910,34916],{},[31,34906,34907],{},[41,34908,12179],{"href":34909},"#device-agent",[31,34911,34912],{},[41,34913,34915],{"href":34914},"#fleet-mode-vs-developer-mode","Fleet Mode vs Developer Mode",[31,34917,34918],{},[41,34919,34921],{"href":34920},"#provisioning-tokens","Provisioning Tokens",[31,34923,34924],{},[41,34925,932],{"href":34926},"#device-groups",[23,34928,4154],{"id":4153},[14,34930,34931],{},"Teams are how FlowFuse organizes users on the platform. Each team can have multiple members and each user can be a member of multiple teams.",[14,34933,34934,34935,34939],{},"The users in a team can have different roles that determine what they are ",[41,34936,34938],{"href":34937},"\u002Fdocs\u002Fuser\u002Fteam#role-based-access-control","able to do",". In FlowFuse Cloud, each team has its own billing plan, managed via Stripe.",[104,34941,4324],{"id":34942},"team-type",[14,34944,34945,34946,34948],{},"The platform can be configured to provide different types of teams. These can be used to apply limits on what teams of a given type can do. For example, a particular team type may be restricted to certain types of ",[41,34947,34296],{"href":34806},", or how many members the team can have.",[23,34950,15613],{"id":34951},"application",[14,34953,34954],{},[364,34955,34956],{},"Introduced in FlowFuse 1.5",[14,34958,34959],{},"To organize your Node-RED instances, they are grouped within Applications. With the 1.5 release, each Application has a single Node-RED instance. With the 1.6 release, an application can have multiple Node-RED instances.",[14,34961,34962],{},"Applications provide logical organization of related instances, support for DevOps pipeline workflows, simplified device group management, application-level audit logging, and clear organizational boundaries for managing multiple instances and devices.",[14,34964,34965],{},"With FlowFuse's Granular RBAC, Applications can now also act as an authorization boundary. This means user roles and permissions can be managed at the application level, providing finer control over access to instances, snapshots, and devices within a given application.",[104,34967,34801],{"id":34968},"devops-pipeline",[14,34970,34971],{},[364,34972,34973],{},"Introduced in FlowFuse 1.8",[14,34975,34976,34977,273],{},"DevOps Pipelines allow you to manage staged development environments, pushing from your Development instances to Production once you have stable and well-tested flows. You can find out how to implement DevOps Pipelines ",[41,34978,785],{"href":15817},[23,34980,16075],{"id":34981},"instance",[14,34983,34984],{},[364,34985,34986],{},"This was called a Project before FlowFuse 1.5",[14,34988,34989],{},"Within your Application, you can have one or more instances of Node-RED. FlowFuse supports two types of instances based on where they run.",[2289,34991,34992,35004],{},[2292,34993,34994],{},[2295,34995,34996,34999,35001],{},[2298,34997,34998],{},"Feature",[2298,35000,34814],{},[2298,35002,35003],{},"Remote Instance (Device)",[2305,35005,35006,35017,35027,35038],{},[2295,35007,35008,35011,35014],{},[2310,35009,35010],{},"Where it runs",[2310,35012,35013],{},"On FlowFuse-managed infrastructure (cloud or self-hosted)",[2310,35015,35016],{},"On user-provided hardware (Edge, VM, Pi, PLC)",[2295,35018,35019,35021,35024],{},[2310,35020,16057],{},[2310,35022,35023],{},"Automatic via the FlowFuse UI",[2310,35025,35026],{},"Manual installation of the FlowFuse Device Agent",[2295,35028,35029,35032,35035],{},[2310,35030,35031],{},"Lifecycle",[2310,35033,35034],{},"Fully managed by FlowFuse",[2310,35036,35037],{},"Managed by FlowFuse via the Device Agent",[2295,35039,35040,35043,35046],{},[2310,35041,35042],{},"Common Use",[2310,35044,35045],{},"Core logic, APIs, dashboards, development, testing",[2310,35047,35048],{},"Local data processing, IO, edge control",[104,35050,34814],{"id":35051},"hosted-instance",[14,35053,36,35054,35056],{},[364,35055,34814],{}," is a Node-RED environment that runs within the FlowFuse infrastructure, whether in the cloud or on a self-hosted FlowFuse platform. These instances run on FlowFuse-managed infrastructure and benefit from automatic scaling and high availability. They are accessed via HTTPS with TLS\u002FSSL encryption, making them ideal for centralized data processing and transformation, dashboard hosting, cloud service integration, and development or testing environments.",[14,35058,35059],{},"When running in Docker or Kubernetes (such as FlowFuse Cloud), the instance names are used as the hostname to access the instance. This means that the names must be DNS safe (made up of a-z, 0-9 and -) and not start with a number. Currently, it is not possible to change an instance name after it has been created.",[104,35061,34820],{"id":35062},"remote-instance",[14,35064,36,35065,35067,35068,22239],{},[364,35066,34820],{}," refers to a Node-RED environment that is managed by FlowFuse but runs on external infrastructure—be it a PLC, Gateway, local PC, or even a server in a different network. Remote instances used to be referred to as Devices within the FlowFuse platform, and in some parts of the platform this terminology is still used. See the ",[41,35069,34902],{"href":34901},[14,35071,35072],{},"These instances run on user-provided infrastructure such as edge devices, on-premises servers, or industrial equipment while being managed centrally through the FlowFuse platform. They are ideal for edge computing, local data processing, and environments with limited connectivity. Remote instances require FlowFuse Device Agent installation to connect to the platform.",[104,35074,34826],{"id":35075},"instance-configuration",[14,35077,35078],{},"Both hosted and remote instances are customized versions of Node-RED that include various FlowFuse plugins to integrate with the platform. A number of the standard Node-RED settings are exposed for customization, and they can be preset by applying a Template when creating the instance.",[14,35080,35081],{},"When an instance is being created, the user can select a blueprint to use.",[104,35083,34832],{"id":35084},"instance-type",[14,35086,35087],{},"When you create a Node-RED instance, you can pick its type from the list the platform Administrator has made available. For example, each type could provide a different amount of memory or CPU allocation. If the platform has billing enabled, each type may have a different monthly price associated with it.",[104,35089,34838],{"id":4232},[14,35091,35092],{},"A Stack describes the properties of the Node-RED runtime. This can include the version of Node-RED, memory, and CPU allocation. The specific details will depend on how and where FlowFuse is running.",[14,35094,35095],{},"Stacks are created and owned by the platform Administrator. When a User comes to create a new Node-RED instance, they choose from the available stacks associated with the chosen Instance Type. The stack determines the Node-RED version, memory, and CPU usage for your instance, ensuring compatibility and optimal performance for your use case.",[14,35097,35098,35099,35102],{},"For details on how to administer and manage Stacks, please see the ",[41,35100,535],{"href":35101},"\u002Fdocs\u002Fadmin\u002Fintroduction#managing-stacks"," docs.",[104,35104,34844],{"id":35105},"blueprint",[14,35107,35108],{},"Blueprints are pre-built Node-RED instances designed for industrial applications. They include ready-to-use flows along with all node configurations and the required nodes, as well as environment settings, allowing the instance to be deployed with minimal modification.",[104,35110,34850],{"id":20066},[14,35112,35113],{},"A Template describes the properties of Node-RED itself. It is how many of the settings a user familiar with Node-RED would be used to modifying in their settings file. But it can also be used to customize the palette of nodes that are pre-installed, provide a set of default flows and change the look and feel of the editor.",[14,35115,35116,35117,35119],{},"A template can also specify ",[41,35118,11824],{"href":15823}," which can then have their values customized for each Node-RED instance, or have their values locked to prevent any changes. In the current release of FlowFuse, Templates are created by the Administrator. As well as defining the values, they also get to choose whether instances can override any of the settings for themselves.",[104,35121,34856],{"id":35122},"snapshot",[14,35124,35125],{},"Snapshots are point-in-time backups that work differently depending on the instance type.",[768,35127,34864],{"id":35128},"hosted-instance-snapshot",[14,35130,35131,35132,35134],{},"A snapshot is a point-in-time backup of a hosted Node-RED instance. It captures the flows, credentials, and runtime settings. Snapshots can be created and deleted on the FlowFuse Platform, or using the ",[41,35133,33632],{"href":33572},". The platform also allows you to roll an instance back to a previous snapshot.",[14,35136,35137,35138,35141],{},"A user can create an hosted instance snapshot and then mark it as the ",[1160,35139,35140],{},"target"," snapshot for remote instance. The platform will then deploy that snapshot to all of the devices assigned to the instance. Snapshots can be set as targets for DevOps pipeline stages. Auto snapshots are available for automatic backup scheduling. If an auto snapshot is set as a target or assigned to a pipeline stage, it will not be automatically cleaned up, so you may have more than 10 auto snapshots in that case.",[768,35143,34870],{"id":35144},"remote-instance-snapshot",[14,35146,35147],{},"Similar to instance snapshots, a device snapshot is a point-in-time backup of a Node-RED instance running on a remote device. It captures the flows, credentials, and runtime settings. The difference is that local changes made on a device during developer mode are pulled into the FlowFuse platform and stored as a snapshot. The dashboard also allows you to see and manage these snapshots. With devices assigned to an application, a user can create a device snapshot from the remote device.",[104,35149,34876],{"id":35150},"assigning-remote-instances-to-hosted-instances",[14,35152,35153,35155,35156,35158],{},[364,35154,1798],{}," This is a legacy feature that predates DevOps Pipelines. For new deployments, consider using ",[41,35157,15454],{"href":34800}," instead, which provide more flexible and powerful deployment workflows.",[14,35160,35161,35162,273],{},"Instance assignment establishes a parent-child deployment relationship between a hosted instance and remote instances. When assigned, the hosted instance becomes the source of truth and automatically pushes snapshots to the remote instance. You can only push nodes and flows that are supported by both Hosted and Remote Instances. For more information on how this works, see ",[41,35163,35165],{"href":35164},"\u002Fdocs\u002Fuser\u002Fconcepts.md#how-it-works","How Instance Assignment Works",[768,35167,34884],{"id":35168},"how-it-works",[14,35170,35171],{},"Instance assignment creates a parent-child deployment relationship with the following behavior:",[28,35173,35174,35180,35186,35196],{},[31,35175,35176,35179],{},[364,35177,35178],{},"Hosted Instance (Parent)",": Acts as the deployment controller and source of truth for snapshots",[31,35181,35182,35185],{},[364,35183,35184],{},"Remote Instance (Child)",": Receives and applies snapshot updates from the parent",[31,35187,35188,35191,35192,35195],{},[364,35189,35190],{},"Deployment Flow",": When you set a ",[41,35193,16352],{"href":35194},"\u002Fdocs\u002Fuser\u002Fsnapshots\u002F#instance-owned-devices"," on the hosted instance, all assigned remote instances automatically restart on that snapshot",[31,35197,35198,35201],{},[364,35199,35200],{},"One-Way Relationship",": Changes flow exclusively from the hosted instance to remote instances, ensuring consistent deployments across your fleet",[768,35203,34890],{"id":35204},"assignment-rules",[14,35206,35207],{},"FlowFuse enforces specific constraints on instance assignment:",[28,35209,35210,35213,35216],{},[31,35211,35212],{},"Remote instances can be assigned to hosted instances",[31,35214,35215],{},"Remote instances cannot be assigned to other remote instances",[31,35217,35218],{},"Hosted instances cannot be assigned to any other instances",[768,35220,34896],{"id":35221},"when-to-use-instance-assignment-vs-devops-pipelines",[14,35223,35224],{},"Instance assignment suits straightforward scenarios where you need to push snapshots from a single hosted instance directly to one or more remote instances. It works well for maintaining existing deployments or when you simply need one hosted instance deploying to multiple remote instances without staging environments.",[14,35226,35227],{},"DevOps Pipelines are recommended for most deployment workflows. They support staged environments for testing changes in development before promoting to production, enable deployment to device groups for easier fleet management, and offer more flexibility aligned with modern development practices. If you're setting up a new deployment process, use DevOps Pipelines.",[23,35229,34902],{"id":35230},"device",[14,35232,35233,35234,35236],{},"The FlowFuse platform can be used to manage Node-RED applications running on remote devices. A Device is essentially a ",[364,35235,34820],{}," that runs a software agent to connect back to the platform and receive updates.",[14,35238,35239,35240,35243,35244,35247,35248,35250,35251,35254],{},"Users must ",[41,35241,35242],{"href":11871},"install the agent"," on the devices. Devices are registered to a Team and then assigned to an individual Node-RED instance or a FlowFuse application within that team. A user can create an ",[41,35245,35246],{"href":34863},"instance snapshot"," and then mark it as the ",[1160,35249,35140],{}," snapshot for devices. The platform will then deploy that snapshot to all of the devices assigned to the instance. With devices assigned to an application, a user can create a ",[41,35252,35253],{"href":34869},"device snapshot"," from the remote device.",[104,35256,12179],{"id":12583},[14,35258,35259,35260,273],{},"The FlowFuse Device Agent is the software that connects your hardware to FlowFuse and manages how Node-RED runs on it. It receives snapshots from FlowFuse, installs the required Node-RED version and nodes, and ensures the device always runs the assigned snapshot. It maintains a secure connection so FlowFuse can monitor the device and send updates remotely. Read more about the ",[41,35261,12179],{"href":12183},[104,35263,34915],{"id":35264},"fleet-mode-vs-developer-mode",[14,35266,35267,35270],{},[364,35268,35269],{},"Fleet Mode","\nWhen in this mode, the device runs only the assigned target snapshot. It automatically updates whenever the target snapshot changes, and the Node-RED editor is disabled to prevent any local modifications. This keeps all devices consistent across the fleet.",[14,35272,35273,35275],{},[364,35274,15697],{},"\nWhen in this mode, the device ignores the target snapshot and stops receiving updates. A secure tunnel opens so you can access the Node-RED editor and make changes directly on the hardware. When you exit Developer Mode, all local edits are discarded and the device reloads the current target snapshot.",[104,35277,34921],{"id":35278},"provisioning-tokens",[14,35280,35281],{},"Provisioning tokens can be created to allow Remote Instances to automatically join a team and to be auto assigned to an application or an instance if required.",[23,35283,932],{"id":35284},"device-groups",[14,35286,35287],{},[364,35288,35289],{},"Introduced in FlowFuse 1.15",[14,35291,35292,35293,35295],{},"Device groups allow you to organize your devices into logical groups. These groups can be the target of ",[41,35294,15454],{"href":34800},", greatly simplifying the deployments to one or hundreds of devices.",[14,35297,35298],{},"Device groups provide logical organization of devices by location, function, environment, or other criteria. They enable simplified mass deployments through DevOps pipelines, allow you to target specific device groups for staged rollouts, and let you manage subsets of devices independently.",[14,35300,35301,35302,273],{},"Read more ",[41,35303,35304],{"href":938},"about Device Groups",{"title":55,"searchDepth":77,"depth":77,"links":35306},[35307,35308,35311,35314,35325,35330],{"id":34761,"depth":77,"text":34762},{"id":4153,"depth":77,"text":4154,"children":35309},[35310],{"id":34942,"depth":88,"text":4324},{"id":34951,"depth":77,"text":15613,"children":35312},[35313],{"id":34968,"depth":88,"text":34801},{"id":34981,"depth":77,"text":16075,"children":35315},[35316,35317,35318,35319,35320,35321,35322,35323,35324],{"id":35051,"depth":88,"text":34814},{"id":35062,"depth":88,"text":34820},{"id":35075,"depth":88,"text":34826},{"id":35084,"depth":88,"text":34832},{"id":4232,"depth":88,"text":34838},{"id":35105,"depth":88,"text":34844},{"id":20066,"depth":88,"text":34850},{"id":35122,"depth":88,"text":34856},{"id":35150,"depth":88,"text":34876},{"id":35230,"depth":77,"text":34902,"children":35326},[35327,35328,35329],{"id":12583,"depth":88,"text":12179},{"id":35264,"depth":88,"text":34915},{"id":35278,"depth":88,"text":34921},{"id":35284,"depth":77,"text":932},{},"user\u002Fconcepts.md","\u002Fdocs\u002Fuser\u002Fconcepts",{"title":591,"description":34758},"docs\u002Fuser\u002Fconcepts","rZykc1g5v4O9dYV6rSVQTXL7miRN7jGftPOViIUX0EY",{"id":35338,"title":964,"body":35339,"description":35420,"extension":329,"layout":330,"meta":35421,"navGroup":330,"navOrder":330,"navTitle":964,"navigation":187,"originalPath":35422,"path":970,"redirect":330,"seo":35423,"stem":35424,"updated":337,"version":338,"__hash__":35425},"docs\u002Fdocs\u002Fuser\u002Fcustom-hostnames.md",{"type":7,"value":35340,"toc":35417},[35341,35343,35348,35351,35359,35363,35366,35372,35386,35395,35405],[10,35342,964],{"id":4598},[14,35344,35345,35346,273],{},"FlowFuse allows you to point a custom subdomain at your instances, such as ",[18,35347,4607],{},[14,35349,35350],{},"This feature is available to:",[28,35352,35353,35356],{},[31,35354,35355],{},"FlowFuse Cloud Enterprise teams",[31,35357,35358],{},"Self-hosted FlowFuse Enterprise, running version 2.5 or later with the kubernetes driver",[23,35360,35362],{"id":35361},"configuring-a-custom-hostname","Configuring a custom hostname",[14,35364,35365],{},"The Custom Hostname option is available under the General Settings tab of your Instance.",[14,35367,35368,35369,35371],{},"Currently, FlowFuse only supports configuring a subdomain such as ",[18,35370,4607],{},". Top level domains cannot be used.",[398,35373,35374,35377,35380],{},[31,35375,35376],{},"Enter your custom subdomain and click save.",[31,35378,35379],{},"You will be shown a dialog to confirm the change, as this will require restarting your Instance to apply.\nIt also provides information on how to configure your DNS provider.",[31,35381,35382,35383,35385],{},"Using your DNS provider, create a ",[18,35384,20340],{}," record for your chosen subdomain that points at the endpoint provided\nin the dialog.",[14,35387,35388,35390,35391,35394],{},[364,35389,1760],{},": it is important to configure the Instance's Custom Hostname ",[1160,35392,35393],{},"before"," you make the DNS changes. Making\nthe DNS changes without configuring your Instance may allow someone else to configure their instance to use\nyour subdomain.",[14,35396,35397,35398,35400,35401,35404],{},"For FlowFuse Cloud, the ",[18,35399,20340],{}," should point to ",[18,35402,35403],{},"custom-loadbalancer.flowfuse.com",". For self-hosted users,\nuse the information provided in the dialog.",[14,35406,35407,35408,35412,35413,35416],{},"If you also use CAA DNS entries to control which Certificate Authorities can issue certificates for your domains,\nyou will need to add a record allowing LetsEncrypt to issue certificate. Please see details ",[41,35409,785],{"href":35410,"rel":35411},"https:\u002F\u002Fletsencrypt.org\u002Fdocs\u002Fcaa\u002F",[831],".\nThe platform will issue certificates using the ",[18,35414,35415],{},"http-01"," validation method.",{"title":55,"searchDepth":77,"depth":77,"links":35418},[35419],{"id":35361,"depth":77,"text":35362},"FlowFuse allows you to point a custom subdomain at your instances, such as dashboard.example.com.",{},"user\u002Fcustom-hostnames.md",{"title":964,"description":35420},"docs\u002Fuser\u002Fcustom-hostnames","0ko78sV1Skj6mGGrU5QxMA-xR3qi__01U_qotbPTVrE",{"id":35427,"title":35428,"body":35429,"description":35708,"extension":329,"layout":330,"meta":35709,"navGroup":330,"navOrder":330,"navTitle":35428,"navigation":187,"originalPath":35710,"path":818,"redirect":330,"seo":35711,"stem":35712,"updated":337,"version":338,"__hash__":35713},"docs\u002Fdocs\u002Fuser\u002Fcustom-npm-packages.md","Custom Node Packages",{"type":7,"value":35430,"toc":35698},[35431,35434,35442,35451,35459,35463,35466,35470,35473,35477,35480,35483,35493,35499,35503,35506,35525,35531,35606,35610,35616,35622,35625,35634,35638,35645,35654,35658,35661,35665,35668,35671,35679,35683,35686,35695],[10,35432,35428],{"id":35433},"custom-node-packages",[14,35435,35436,35437,35441],{},"FlowFuse has access to the wide range of Node-RED nodes listed in the\n",[41,35438,35440],{"href":4243,"rel":35439},[831],"public catalogue",". But occasionally there\nwill be the need for a custom node for a situation that is specific\nto your Team.",[14,35443,35444,35445,35450],{},"If you decide to ",[41,35446,35449],{"href":35447,"rel":35448},"https:\u002F\u002Fnodered.org\u002Fdocs\u002Fcreating-nodes\u002F",[831],"develop"," your\nown nodes, you will need somewhere to host both the node and a Node-RED\ncatalogue file. FlowFuse has two solutions for this:",[398,35452,35453,35456],{},[31,35454,35455],{},"FlowFuse Hosted Nodes - Use the private NPM registry hosted by FlowFuse to store and manage your custom npm packages.",[31,35457,35458],{},"Third-Party NPM Registries - If you already have a private npm registry, you can enable access to these in your Instance's settings.",[23,35460,35462],{"id":35461},"flowfuse-hosted-nodes","FlowFuse Hosted Nodes",[14,35464,35465],{},"If you want to create a Node-RED node for private use by Instances in your\nFlowFuse Team then you can publish them to the FlowFuse Custom Node Registry\n(available to Teams and Enterprise level teams on FlowFuse Cloud).",[104,35467,35469],{"id":35468},"publishing-nodes","Publishing Nodes",[14,35471,35472],{},"After developing your node you can publish it to your Teams Custom Nodes\nregistry with the following steps",[768,35474,35476],{"id":35475},"authenticating","Authenticating",[14,35478,35479],{},"Before publishing to the registry you need to authenticate. This step should\nonly need to be done once.",[14,35481,35482],{},"The credentials can be found by navigating to the \"Custom Nodes\" tab under\nthe Team Library and clicking on the \"Publish\" button.",[14,35484,35485,35489,35490],{},[638,35486],{"alt":35487,"dataZoomable":55,"src":35488},"Publish Custom Package","\u002Fdocs\u002Fuser\u002Fimages\u002Fpublish-custom-package.png","{style=\"max-width: 600px;\"}\n",[1160,35491,35492],{},"Screenshot fo the \"Publish Custom Package\" dialog shown in the FlowFuse UI",[50,35494,35497],{"className":35495,"code":35496,"language":3920},[3918],"npm login --registry=https:\u002F\u002Fregistry.flowfuse.cloud\n",[18,35498,35496],{"__ignoreMap":55},[768,35500,35502],{"id":35501},"packaging","Packaging",[14,35504,35505],{},"There are steps required to ensure your node is correctly packaged for the\nFlowFuse Custom Nodes registry",[398,35507,35508,35515],{},[31,35509,35510,35511,35514],{},"Make sure the package name contains the correct scope prefix e.g.\n",[18,35512,35513],{},"@flowfuse-[team id]\u002Fnode-name",". The correct prefix will be shown on the in\nthe FlowFuse application",[31,35516,17262,35517,35520,35521,35524],{},[18,35518,35519],{},"publishConfig"," section with a ",[18,35522,35523],{},"registry"," entry",[14,35526,35527,35528],{},"e.g. for a Team with ID ",[18,35529,35530],{},"6Rag1kQj4k",[50,35532,35534],{"className":2972,"code":35533,"language":2974,"meta":55,"style":55},"{\n    \"name\": \"@flowfuse-6Rag1kQj4k\u002Fbar\",\n    \"version\": \"0.0.1\",\n    \"description\": \"...\",\n    \"publishConfig\": {\n        \"registry\": \"https:\u002F\u002Fregistry.flowfuse.cloud\"\n    },\n    ...\n}\n",[18,35535,35536,35540,35552,35563,35575,35582,35592,35596,35602],{"__ignoreMap":55},[59,35537,35538],{"class":61,"line":62},[59,35539,2981],{"class":178},[59,35541,35542,35545,35547,35550],{"class":61,"line":77},[59,35543,35544],{"class":73},"    \"name\"",[59,35546,179],{"class":178},[59,35548,35549],{"class":69},"\"@flowfuse-6Rag1kQj4k\u002Fbar\"",[59,35551,2665],{"class":178},[59,35553,35554,35556,35558,35561],{"class":61,"line":88},[59,35555,8671],{"class":73},[59,35557,179],{"class":178},[59,35559,35560],{"class":69},"\"0.0.1\"",[59,35562,2665],{"class":178},[59,35564,35565,35568,35570,35573],{"class":61,"line":99},[59,35566,35567],{"class":73},"    \"description\"",[59,35569,179],{"class":178},[59,35571,35572],{"class":69},"\"...\"",[59,35574,2665],{"class":178},[59,35576,35577,35580],{"class":61,"line":156},[59,35578,35579],{"class":73},"    \"publishConfig\"",[59,35581,3068],{"class":178},[59,35583,35584,35587,35589],{"class":61,"line":216},[59,35585,35586],{"class":73},"        \"registry\"",[59,35588,179],{"class":178},[59,35590,35591],{"class":69},"\"https:\u002F\u002Fregistry.flowfuse.cloud\"\n",[59,35593,35594],{"class":61,"line":224},[59,35595,3190],{"class":178},[59,35597,35598],{"class":61,"line":233},[59,35599,35601],{"class":35600},"s7hpK","    ...\n",[59,35603,35604],{"class":61,"line":241},[59,35605,3336],{"class":178},[768,35607,35609],{"id":35608},"publishing","Publishing",[14,35611,35612,35613,35615],{},"In the same directory as the ",[18,35614,7977],{}," file run the following command",[50,35617,35620],{"className":35618,"code":35619,"language":3920},[3918],"npm publish\n",[18,35621,35619],{"__ignoreMap":55},[14,35623,35624],{},"Once published you should see the Node listed in the \"Custom Nodes\" section\nof the Team Library.",[14,35626,35627,35631,35632],{},[638,35628],{"alt":35629,"dataZoomable":55,"src":35630},"Screenshot of the \"Custom Nodes\" view in the Team Library","\u002Fdocs\u002Fuser\u002Fimages\u002Fcustom-node-library.png","{style=\"max-width: 850px;\"}\n",[1160,35633,35629],{},[104,35635,35637],{"id":35636},"installing-nodes","Installing Nodes",[14,35639,35640,35641,35644],{},"Any packages uploaded to the Team Library will be published to your Instances\nunder a custom catalogue with the name \"FlowFuse Team ",[59,35642,35643],{},"team name"," Catalogue\"",[14,35646,35647,35489,35651],{},[638,35648],{"alt":35649,"dataZoomable":55,"src":35650},"Node-RED Custom Catalogue","\u002Fdocs\u002Fuser\u002Fimages\u002Fcustom-catalogue.png",[1160,35652,35653],{},"Screenshot of the contents of a FlowFUse catalogue appearing in the \"install\" tab of the Node-RED Palette Manager",[23,35655,35657],{"id":35656},"_3rd-party-npm-registries-or-private-npmjsorg-packages","3rd Party NPM Registries or Private npmjs.org packages",[14,35659,35660],{},"The following features are available to Team and Enterprise users of FlowFuse\nCloud.",[104,35662,35664],{"id":35663},"npm-registries","NPM Registries",[14,35666,35667],{},"If you have already published packages to an existing NPM Registry then you\ncan enable access to this by adding the required values to a `.npmrc\" file\nin the Instance Settings.",[14,35669,35670],{},"This can include authentication tokens to access private packages.",[14,35672,35673,35489,35676],{},[638,35674],{"alt":12090,"dataZoomable":55,"src":35675},"\u002Fdocs\u002Fuser\u002Fimages\u002Finstance-settings-npmrc.png",[1160,35677,35678],{},"Screenshot from the FlowFuse platform, showing the input for defining an .npmrc file",[104,35680,35682],{"id":35681},"node-red-catalogues","Node-RED Catalogues",[14,35684,35685],{},"In order to be able to install packages in the Node-RED editor they need to\nin a Node-RED Catalogue file that is loaded from a HTTPS URL. You can\nsupply a list of Catalogue URLs in the Instance Settings.",[14,35687,35688,35489,35692],{},[638,35689],{"alt":35690,"dataZoomable":55,"src":35691},"Node Catalogues","\u002Fdocs\u002Fuser\u002Fimages\u002Finstance-settings-catalogues.png",[1160,35693,35694],{},"Screenshot of the listed Node Catalogues configured on an Instance",[316,35696,35697],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}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":55,"searchDepth":77,"depth":77,"links":35699},[35700,35704],{"id":35461,"depth":77,"text":35462,"children":35701},[35702,35703],{"id":35468,"depth":88,"text":35469},{"id":35636,"depth":88,"text":35637},{"id":35656,"depth":77,"text":35657,"children":35705},[35706,35707],{"id":35663,"depth":88,"text":35664},{"id":35681,"depth":88,"text":35682},"FlowFuse has access to the wide range of Node-RED nodes listed in the\npublic catalogue. But occasionally there\nwill be the need for a custom node for a situation that is specific\nto your Team.",{},"user\u002Fcustom-npm-packages.md",{"title":35428,"description":35708},"docs\u002Fuser\u002Fcustom-npm-packages","4dPKr2YCy7pSml7kM5lH2tRyh0Z2MpODp8d_9CqtzIs",{"id":35715,"title":35716,"body":35717,"description":35969,"extension":329,"layout":330,"meta":35970,"navGroup":330,"navOrder":330,"navTitle":35716,"navigation":187,"originalPath":35971,"path":938,"redirect":330,"seo":35972,"stem":35973,"updated":337,"version":338,"__hash__":35974},"docs\u002Fdocs\u002Fuser\u002Fdevice-groups.md","Groups",{"type":7,"value":35718,"toc":35957},[35719,35722,35728,35731,35737,35745,35749,35754,35762,35767,35781,35783,35794,35798,35804,35829,35833,35837,35843,35892,35897,35901,35906,35909,35914,35917,35922,35925,35928,35931],[10,35720,35716],{"id":35721},"groups",[14,35723,35724,35727],{},[364,35725,35726],{},"Navigation",": Team > Application > Groups",[23,35729,15465],{"id":35730},"overview",[14,35732,35733],{},[638,35734],{"alt":35735,"dataZoomable":55,"src":35736},"Groups UI","\u002Fdocs\u002Fuser\u002Fimages\u002Fgroups.png",[14,35738,35739,35740,35742,35743,273],{},"Groups help you organize and manage multiple devices that run the same ",[41,35741,35122],{"href":949}," configuration. By grouping devices logically, you can deploy updates to dozens or even hundreds of devices simultaneously through ",[41,35744,15454],{"href":15817},[104,35746,35748],{"id":35747},"key-features","Key Features",[14,35750,35751],{},[364,35752,35753],{},"Automatic Updates",[28,35755,35756,35759],{},[31,35757,35758],{},"Devices added to an active group automatically receive the current pipeline snapshot",[31,35760,35761],{},"Devices removed from an active group have their snapshot cleared",[14,35763,35764],{},[364,35765,35766],{},"Group-Level Environment Variables",[28,35768,35769,35772,35775,35778],{},[31,35770,35771],{},"Set environment variables at the group level that apply to all member devices",[31,35773,35774],{},"Device-specific variables take precedence over group variables",[31,35776,35777],{},"Variables are merged at runtime without modifying device settings",[31,35779,35780],{},"Updates to group variables trigger automatic device restarts",[104,35782,12208],{"id":12207},[28,35784,35785,35788,35791],{},[31,35786,35787],{},"FlowFuse 1.15+ (Enterprise Tier)",[31,35789,35790],{},"FlowFuse Cloud (Enterprise Tier)",[31,35792,35793],{},"FlowFuse 2.10+ for Group Environment Variables",[23,35795,35797],{"id":35796},"creating-a-group","Creating a Group",[14,35799,35800],{},[638,35801],{"alt":35802,"dataZoomable":55,"src":35803},"Create Group","\u002Fdocs\u002Fuser\u002Fimages\u002Fcreate-device-group.png",[398,35805,35806,35809,35814,35819,35822,35825],{},[31,35807,35808],{},"Navigate to your Application",[31,35810,16421,35811,35813],{},[364,35812,35716],{}," tab",[31,35815,15596,35816],{},[364,35817,35818],{},"Add Group",[31,35820,35821],{},"Enter a descriptive name for your group",[31,35823,35824],{},"(Optional) Add a description to help distinguish between groups",[31,35826,15596,35827],{},[364,35828,16097],{},[23,35830,35832],{"id":35831},"managing-group-membership","Managing Group Membership",[104,35834,35836],{"id":35835},"adding-and-removing-devices","Adding and Removing Devices",[14,35838,35839],{},[638,35840],{"alt":35841,"dataZoomable":55,"src":35842},"Edit Group Members","\u002Fdocs\u002Fuser\u002Fimages\u002Fui-device-group-member-edit.png",[398,35844,35845,35848,35853,35869,35875,35881,35885],{},[31,35846,35847],{},"Click the group you want to modify from the table",[31,35849,15596,35850],{},[364,35851,35852],{},"Edit",[31,35854,35855,35856],{},"Review the two lists:\n",[28,35857,35858,35864],{},[31,35859,35860,35863],{},[364,35861,35862],{},"Available Devices"," (left): Devices assigned to your application that can be added",[31,35865,35866,35868],{},[364,35867,15035],{}," (right): Devices currently in the group",[31,35870,35871,35872],{},"To add devices: Check the boxes next to devices in the Available list, then click ",[364,35873,35874],{},"Add Devices",[31,35876,35877,35878],{},"To remove devices: Check the boxes next to devices in the Group list, then click ",[364,35879,35880],{},"Remove Devices",[31,35882,15596,35883],{},[364,35884,478],{},[31,35886,35887,35888,35891],{},"Review the confirmation prompt and click ",[364,35889,35890],{},"Confirm"," to apply changes",[14,35893,35894,35896],{},[364,35895,1760],{},": If a device doesn't appear in the Available list, it's likely already assigned to another group.",[104,35898,35900],{"id":35899},"how-snapshots-are-affected","How Snapshots Are Affected",[14,35902,35903],{},[364,35904,35905],{},"When Adding a Device",[14,35907,35908],{},"If the group has an active pipeline snapshot, newly added devices will automatically be updated to that snapshot.",[14,35910,35911],{},[364,35912,35913],{},"When Removing a Device",[14,35915,35916],{},"If the group has an active pipeline snapshot and the device is currently running it, removing the device will clear its snapshot, effectively resetting it to a blank state.",[14,35918,35919],{},[364,35920,35921],{},"Clearing Group Snapshots",[14,35923,35924],{},"You can remove the target snapshot from a group in the group settings. This will also clear the snapshot from all devices in the group.",[23,35926,11824],{"id":35927},"environment-variables",[14,35929,35930],{},"Group-level environment variables follow these rules:",[28,35932,35933,35939,35945,35951],{},[31,35934,35935,35938],{},[364,35936,35937],{},"Precedence",": Device variables override group variables",[31,35940,35941,35944],{},[364,35942,35943],{},"Scope",": Variables are merged at runtime only; device settings remain unchanged",[31,35946,35947,35950],{},[364,35948,35949],{},"Removal",": Removing a device from a group removes the group's variables from that device",[31,35952,35953,35956],{},[364,35954,35955],{},"Updates",": Changing group variables triggers a restart of all devices in the group",{"title":55,"searchDepth":77,"depth":77,"links":35958},[35959,35963,35964,35968],{"id":35730,"depth":77,"text":15465,"children":35960},[35961,35962],{"id":35747,"depth":88,"text":35748},{"id":12207,"depth":88,"text":12208},{"id":35796,"depth":77,"text":35797},{"id":35831,"depth":77,"text":35832,"children":35965},[35966,35967],{"id":35835,"depth":88,"text":35836},{"id":35899,"depth":88,"text":35900},{"id":35927,"depth":77,"text":11824},"Navigation: Team > Application > Groups",{},"user\u002Fdevice-groups.md",{"title":35716,"description":35969},"docs\u002Fuser\u002Fdevice-groups","bfy75bUh2OUCSAda8GSX59OGIHguuawPS2yzvUC_uE0",{"id":35976,"title":15454,"body":35977,"description":36281,"extension":329,"layout":330,"meta":36282,"navGroup":330,"navOrder":330,"navTitle":15454,"navigation":187,"originalPath":36283,"path":15817,"redirect":330,"seo":36284,"stem":36285,"updated":337,"version":338,"__hash__":36286},"docs\u002Fdocs\u002Fuser\u002Fdevops-pipelines.md",{"type":7,"value":35978,"toc":36272},[35979,35981,35988,35991,35994,35997,36000,36004,36054,36061,36065,36068,36071,36082,36088,36091,36094,36115,36119,36122,36165,36168,36171,36174,36178,36198,36202,36221,36225,36228,36232,36235,36238,36241,36244,36248,36251,36254,36257,36260,36263,36266,36269],[10,35980,15454],{"id":15777},[14,35982,35983,179,35985],{},[364,35984,35726],{},[18,35986,35987],{},"Team > Application > Pipelines",[638,35989],{"src":35990,"width":473},"\u002Fdocs\u002Fuser\u002Fimages\u002Fui-devops-pipelines.png",[14,35992,35993],{},"In FlowFuse it is possible to configure a DevOps pipeline for your Node-RED instances.\nDevOps Pipelines allow you to easily deploy from one instance to another, most\ncommonly used for having an unstable\u002Fexperimental \"Development\" instance, and a more stable\n\"Production\" instance.",[14,35995,35996],{},"The pipeline then allows you to move your full flow and configuration along from \"Development\"\nto \"Production\" once it's ready.",[14,35998,35999],{},"You can configure this in FlowFuse from the Application screen. Note you will need to have created\nany Instances you wish to include in the Pipeline before being able to add them to a Pipeline.",[23,36001,36003],{"id":36002},"creating-a-pipeline","Creating a Pipeline",[398,36005,36006,36009,36025,36028,36031,36034,36037,36048,36051],{},[31,36007,36008],{},"Select the Application you want to configure a Pipeline for.",[31,36010,36011,36012],{},"Ensure an instance is created for each stage you plan to create, e.g. development, QA, and production.\n",[398,36013,36014,36019],{},[31,36015,36016,36017,35813],{},"For the instance you want to duplicate go to the ",[364,36018,17558],{},[31,36020,15596,36021,36024],{},[364,36022,36023],{},"Duplicate Instance"," and provide the necessary details",[31,36026,36027],{},"Select the \"DevOps Pipelines\" tab",[31,36029,36030],{},"Select \"Add Pipeline\"",[31,36032,36033],{},"Name your pipeline appropriately (this can be changed later)",[31,36035,36036],{},"Select \"Add Stage\"",[31,36038,36039,36040,15610,36044,273],{},"Define your Stage's name, select the ",[41,36041,36043],{"href":36042},"#stage-types","Stage Type",[41,36045,36047],{"href":36046},"#actions","Action",[31,36049,36050],{},"Click \"Add Stage\"",[31,36052,36053],{},"Repeat 5. - 7. for as many stages as you need.",[14,36055,4611,36056,36060],{},[41,36057,36059],{"href":36058},"#pipeline-stage-details","Pipeline Stage details"," below for more info.",[23,36062,36064],{"id":36063},"running-a-pipeline-stage","Running a Pipeline Stage",[638,36066],{"src":36067,"width":473},"\u002Fdocs\u002Fuser\u002Fimages\u002Fui-devops-run.png",[14,36069,36070],{},"Each stage currently is deployed manually. To do so, click the \"play\" icon on the source stage. In the example above,\nit will push from the \"Development\" stage to the \"Production\" stage.",[14,36072,36073,36075,36076,36081],{},[364,36074,11824],{}," - When pushing to a next stage, ",[1160,36077,36078],{},[364,36079,36080],{},"only your environment variable keys\nwill be copied over",". Values must be set on the next Stage's Instance explicitly.",[14,36083,36084,36087],{},[364,36085,36086],{},"Instance Settings"," - None of your Instance Settings will be copied over (e.g. Editor, Palette or Security Settings).",[14,36089,36090],{},"This ensures a split between your staging environments.",[23,36092,36059],{"id":36093},"pipeline-stage-details",[28,36095,36096,36099,36102,36107,36112],{},[31,36097,36098],{},"Stages of a pipeline are executed from left to right.",[31,36100,36101],{},"Actionable stages have a play button that will push from that stage to the next stage.",[31,36103,36104,36105,273],{},"Every stage, except the last one, is effectively a source stage that can be pulled ",[1160,36106,15793],{},[31,36108,36109,36110,273],{},"Every stage, except the first one, is a target stage that can be pushed ",[1160,36111,15796],{},[31,36113,36114],{},"You cannot currently insert a Stage into the middle of a Pipeline, only at the end.",[104,36116,36118],{"id":36117},"stage-types","Stage Types",[14,36120,36121],{},"There are four types of stage to chose from:",[398,36123,36124,36132,36140,36149],{},[31,36125,36126,36131],{},[364,36127,36128],{},[41,36129,16075],{"href":36130},"\u002Fdocs\u002Fuser\u002Fconcepts#hosted-instance"," - a single Node-RED instance.",[31,36133,36134,36139],{},[364,36135,36136],{},[41,36137,34902],{"href":36138},"\u002Fdocs\u002Fuser\u002Fconcepts#remote-instance"," - a single remote instance.",[31,36141,36142,36148],{},[364,36143,36144],{},[41,36145,36147],{"href":36146},"\u002Fdocs\u002Fuser\u002Fconcepts#device-groups","Device Group"," - a group of remote instances.",[31,36150,36151,36154,36155],{},[364,36152,36153],{},"Git Repository"," - a remote GitHub\u002FAzure DevOps repository.\n",[28,36156,36157],{},[31,36158,36159,36160],{},"This stage currently only supports:\n",[28,36161,36162],{},[31,36163,36164],{},"Repositories hosted on GitHub.com or dev.azure.com",[104,36166,16417],{"id":36167},"actions",[638,36169],{"src":36170},"\u002Fdocs\u002Fuser\u002Fimages\u002Fui-devops-select-action.png",[14,36172,36173],{},"The action defines what happens when the stage is deployed.\nThe available actions depend on the stage type selected for the stage.\nThese are listed below.",[768,36175,36177],{"id":36176},"instance-stage-actions","Instance stage actions",[28,36179,36180,36186,36192],{},[31,36181,36182,36185],{},[364,36183,36184],{},"Create new instance snapshot"," - A new snapshot of the instance will be created and pushed to the next stage.",[31,36187,36188,36191],{},[364,36189,36190],{},"Use latest instance snapshot"," - The latest existing snapshot of the instance will be pushed to the next stage.",[31,36193,36194,36197],{},[364,36195,36196],{},"Prompt to select instance snapshot"," - You will be prompted to choose which snapshot to push to the next stage.",[768,36199,36201],{"id":36200},"device-stage-action","Device stage action",[28,36203,36204,36210,36216],{},[31,36205,36206,36209],{},[364,36207,36208],{},"Use active snapshot"," - The active snapshot of the device will be pushed to the next stage.",[31,36211,36212,36215],{},[364,36213,36214],{},"Use latest device snapshot"," - The latest snapshot of the device will be pushed to the next stage.",[31,36217,36218,36197],{},[364,36219,36220],{},"Prompt to select device snapshot",[768,36222,36224],{"id":36223},"device-group-stage","Device Group stage",[14,36226,36227],{},"When a Device Group stage is triggered, it will push the current active snapshot of the group to the next stage.",[768,36229,36231],{"id":36230},"git-repository-stage","Git Repository stage",[14,36233,36234],{},"Git Repository stages can be used to push and pull snapshots from a GitHub or Azure DevOps hosted repository. The stage can be configured with\nthe branch to push\u002Fpull from as well as the filename to use for the snapshot.",[14,36236,36237],{},"If a filename is not configured, it will generate the filename when pushing to the repository based on the name of Instance, Device\nor Device Group that provided the snapshot. The provided filename can include directory structures, allowing the snapshot to be stored in a subdirectory of the repository.",[14,36239,36240],{},"When pulling from a repository, if the stage has not previously been used to push to the repository, the filename is a required property.",[14,36242,36243],{},"It is also possible to configure the stage with different branches for the push and pull actions. This enables a GitHub-based review process as part of the pipeline; using a Pull Request process to review and approve the changes before merging between the two branches.",[768,36245,36247],{"id":36246},"deploy-to-devices","Deploy to Devices",[14,36249,36250],{},"This option is only applicable when the Stage Type is an Instance.",[14,36252,36253],{},"When a pipeline stage with this action is deployed to, the same snapshot will be deployed to all devices connected to the defined instance.",[23,36255,861],{"id":36256},"protected-instances",[14,36258,36259],{},"It is now possible to mark an instance as Protected. This means that all team members (including Owners) only have Read Only access to the Node-RED Editor\nand updates to the flows can only be made by a Team Owner running a DevOps pipeline that targets the instance.",[14,36261,36262],{},"Protected mode is activated under Instance > Settings > Protect Instance",[638,36264],{"src":36265,"width":473},"\u002Fdocs\u002Fuser\u002Fimages\u002Fprotected-instance.png",[14,36267,36268],{},"A Protected Instance will be marked by a status badge next to it's running state. Click on this badge will take you to the Settings page.",[638,36270],{"src":36271,"width":473},"\u002Fdocs\u002Fuser\u002Fimages\u002Fprotected-instance-pill.png",{"title":55,"searchDepth":77,"depth":77,"links":36273},[36274,36275,36276,36280],{"id":36002,"depth":77,"text":36003},{"id":36063,"depth":77,"text":36064},{"id":36093,"depth":77,"text":36059,"children":36277},[36278,36279],{"id":36117,"depth":88,"text":36118},{"id":36167,"depth":88,"text":16417},{"id":36256,"depth":77,"text":861},"Navigation: Team > Application > Pipelines",{},"user\u002Fdevops-pipelines.md",{"title":15454,"description":36281},"docs\u002Fuser\u002Fdevops-pipelines","RrVLjvRMrsDPjo4MAvCjIxvvQDPMDJ4EUZEBheuffCs",{"id":36288,"title":11824,"body":36289,"description":36482,"extension":329,"layout":330,"meta":36483,"navGroup":330,"navOrder":330,"navTitle":11824,"navigation":187,"originalPath":36484,"path":15823,"redirect":330,"seo":36485,"stem":36486,"updated":337,"version":338,"__hash__":36487},"docs\u002Fdocs\u002Fuser\u002Fenvvar.md",{"type":7,"value":36290,"toc":36476},[36291,36293,36301,36304,36308,36318,36321,36325,36331,36334,36338,36344,36353,36356,36359,36401,36404,36408,36411,36432,36435,36467,36470],[10,36292,11824],{"id":35927},[14,36294,36295,36296,273],{},"Environment Variables allow you to manage variables used in your Node-RED flows from the FlowFuse application, you can read more on how to access environment variables inside Node-RED ",[41,36297,36300],{"href":36298,"rel":36299},"https:\u002F\u002Fnodered.org\u002Fdocs\u002Fuser-guide\u002Fenvironment-variables",[831],"in the Node-RED Docs",[14,36302,36303],{},"An Environment Variable consists of a name and a value.",[23,36305,36307],{"id":36306},"editing","Editing",[14,36309,36310,36311,36313,36314,36317],{},"You can edit the environment variables from the ",[18,36312,17558],{}," tab of an instance, select the ",[18,36315,36316],{},"Environment"," option from the side menu.",[14,36319,36320],{},"Changes will only take effect when the Node-RED instance is restarted.",[23,36322,36324],{"id":36323},"template-provided-variables","Template provided variables",[14,36326,372,36327,36330],{},[41,36328,34850],{"href":36329},"\u002Fdocs\u002Fuser\u002Fconcepts#template"," may include some predefined environment\nvariables that are automatically applied. The template may lock some of those\nvariables to prevent an individual instance from changing them.",[14,36332,36333],{},"Variables provided by the template cannot be deleted, however if they are editable,\ntheir value can be set to blank.",[23,36335,36337],{"id":36336},"node-red-instance-variables","Node-RED instance variables",[14,36339,36340,36341,15933],{},"You can create additional variables for an individual Node-RED instance clicking the ",[18,36342,36343],{},"Add variable",[14,36345,36346,36347,36349,36350,15933],{},"You can import variables from a ",[18,36348,21560],{}," file using the ",[18,36351,36352],{},"Import .env",[14,36354,36355],{},"You can delete a variable using the trash can icon.",[14,36357,36358],{},"The image below shows an instance with the following environment variables:",[28,36360,36361,36367,36373,36379,36384,36390,36395],{},[31,36362,36363,36366],{},[18,36364,36365],{},"policy_item_locked"," - added by the template, locked",[31,36368,36369,36372],{},[18,36370,36371],{},"policy_item_editable"," - added by the template, editable",[31,36374,36375,36378],{},[18,36376,36377],{},"FF_INSTANCE_ID"," - provided by the platform, locked",[31,36380,36381,36378],{},[18,36382,36383],{},"FF_INSTANCE_NAME",[31,36385,36386,36389],{},[18,36387,36388],{},"FF_PROJECT_ID"," - provided by the platform, locked, depreciated",[31,36391,36392,36389],{},[18,36393,36394],{},"FF_PROJECT_NAME",[31,36396,36397,36400],{},[18,36398,36399],{},"INSTANCE_VAR"," - added to the instance, editable",[638,36402],{"src":36403,"width":15952},"\u002Fdocs\u002Fuser\u002Fimages\u002Fproject-envvar.png",[23,36405,36407],{"id":36406},"standard-environment-variables","Standard environment variables",[14,36409,36410],{},"Standard environment variables are set for all Node-RED instances running\nwithin the platform:",[28,36412,36413,36418,36423],{},[31,36414,36415,36417],{},[18,36416,36377],{}," - The unique identifier of the instance",[31,36419,36420,36422],{},[18,36421,36383],{}," - The name of the instance as set in FlowFuse",[31,36424,36425,36428,36429,660],{},[18,36426,36427],{},"FF_INSTANCE_URL"," - The full URL of the instance (e.g. ",[18,36430,36431],{},"https:\u002F\u002Fmy-instance.flowfuse.cloud",[14,36433,36434],{},"In addition, the following variables are set when running on a device:",[28,36436,36437,36443,36449,36455,36461],{},[31,36438,36439,36442],{},[18,36440,36441],{},"FF_DEVICE_ID"," - The unique identifier of the device",[31,36444,36445,36448],{},[18,36446,36447],{},"FF_DEVICE_NAME"," - The name of the device as set in FlowFuse",[31,36450,36451,36454],{},[18,36452,36453],{},"FF_DEVICE_TYPE"," - The device type label assigned in FlowFuse",[31,36456,36457,36460],{},[18,36458,36459],{},"FF_SNAPSHOT_ID"," - The unique identifier of the snapshot currently deployed to the device",[31,36462,36463,36466],{},[18,36464,36465],{},"FF_SNAPSHOT_NAME"," - The name of the snapshot currently deployed to the device",[14,36468,36469],{},"When deploying the same set of flows out to multiple devices, these variables can\nbe used by the flows to identify the specific device being run on.",[14,36471,36472,36473,36475],{},"NOTE: ",[18,36474,36465],{}," will not be immediately updated when the current snapshot is edited.\nIt will only be updated when the snapshot is changed or a setting that causes the device to\nbe restarted is changed.",{"title":55,"searchDepth":77,"depth":77,"links":36477},[36478,36479,36480,36481],{"id":36306,"depth":77,"text":36307},{"id":36323,"depth":77,"text":36324},{"id":36336,"depth":77,"text":36337},{"id":36406,"depth":77,"text":36407},"Environment Variables allow you to manage variables used in your Node-RED flows from the FlowFuse application, you can read more on how to access environment variables inside Node-RED in the Node-RED Docs.",{},"user\u002Fenvvar.md",{"title":11824,"description":36482},"docs\u002Fuser\u002Fenvvar","u3sUJPTlvhwO2IL52RNhjKchioJ-Ukees7YNk-YFFQw",{"id":36489,"title":36490,"body":36491,"description":36498,"extension":329,"layout":330,"meta":37117,"navGroup":330,"navOrder":330,"navTitle":36490,"navigation":187,"originalPath":37118,"path":37119,"redirect":330,"seo":37120,"stem":37121,"updated":337,"version":338,"__hash__":37122},"docs\u002Fdocs\u002Fuser\u002Fexpert\u002Fchat.md","Chat Interface",{"type":7,"value":36492,"toc":37103},[36493,36496,36499,36504,36508,36515,36522,36529,36535,36539,36546,36586,36589,36595,36598,36619,36623,36626,36629,36635,36638,36660,36663,36673,36677,36680,36733,36736,36746,36749,36760,36763,36769,36772,36775,36778,36780,36791,36794,36800,36803,36806,36818,36821,36824,36826,36837,36840,36846,36850,36857,36860,36870,36883,36886,36919,36926,36931,36948,36952,36955,36958,36962,36965,37002,37006,37009,37037,37041,37044,37072,37076,37079,37087,37091,37094,37100],[10,36494,36490],{"id":36495},"chat-interface",[14,36497,36498],{},"The FlowFuse Expert chat interface is where you actively engage the AI: asking questions, building flows directly on your canvas, and querying live operational data. While other FlowFuse Expert features (like inline code completions and next-node prediction) work passively in the Node-RED editor, the chat interface is where you direct Expert and see it act on your behalf.",[1110,36500,36501],{},[14,36502,36503],{},"Team owners can disable all AI features, including the chat interface, from the team settings page. If AI has been disabled for your team, the Expert panel will not be available.",[23,36505,36507],{"id":36506},"opening-the-chat-interface","Opening the Chat Interface",[14,36509,36510,36511,36514],{},"To open the chat, first open your Node-RED instance using the ",[364,36512,36513],{},"Open Editor"," button. This launches Immersive Mode, where the Expert panel is available alongside your canvas.",[14,36516,36517,36518,36521],{},"While in Immersive Mode you also have access to all ",[41,36519,36520],{"href":18102},"instance settings"," from a drawer that sits beside the canvas. You can manage environment variables, snapshots, the palette, and other settings without leaving the editor. Use the eye icon at the top of the drawer to move it left or right, pin or unpin it, or toggle fullscreen mode.",[1110,36523,36524],{},[14,36525,36526,36528],{},[364,36527,1798],{}," Note: As of v2.29, the FlowFuse Expert panel opens automatically whenever you enter the FlowFuse platform or an instance. If you close the panel, your preference is saved — Expert will remain closed on your next visit so it doesn’t get in your way. To open or close the panel while within the editor, click the FlowFuse drawer button in the top-left corner of the editor.",[14,36530,36531],{},[638,36532],{"alt":36533,"dataZoomable":55,"src":36534},"\"Chat interface of FlowFuse Expert alongside the Node-RED editor\"","\u002Fdocs\u002Fuser\u002Fimages\u002Fassistant\u002Fflowfuse-expert-drawer.png",[23,36536,36538],{"id":36537},"chat-modes","Chat Modes",[14,36540,36541,36542,36545],{},"The chat interface operates in two distinct modes. You can switch between them using the ",[364,36543,36544],{},"Mode Selector"," at the top of the Expert panel.",[17958,36547,21376,36549,21376,36569],{"style":36548},"display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin: 20px 0;",[41,36550,21380,36554,21380,36565,21376],{"className":36551,"href":36553},[36552],"assistant-feature","#support-mode",[18108,36555,17970,36559,17970,36562,21380],{"width":36556,"height":36556,"viewBox":18148,"className":36557,"xmlns":18110},"48",[36558],"icon-stroke",[16850,36560],{"d":36561,"strokeWidth":3158},"M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z",[16850,36563],{"d":36564,"strokeWidth":18170,"strokeLineCap":18152},"M8 9h8M8 13h6",[17972,36566,36568],{"style":36567},"margin: 10px 0; font-size: 16px; color: #333;","Support Mode",[41,36570,21380,36573,21380,36583,21376],{"className":36571,"href":36572},[36552],"#insights-mode",[18108,36574,17970,36576,17970,36580,21380],{"width":36556,"height":36556,"viewBox":18148,"className":36575,"xmlns":18110},[36558],[36577,36578],"circle",{"cx":36579,"cy":36579,"r":9921,"strokeWidth":3158},"12",[16850,36581],{"d":36582,"strokeWidth":3158,"strokeLineCap":18152},"M12 2v3M12 19v3M4.22 4.22l2.12 2.12M17.66 17.66l2.12 2.12M2 12h3M19 12h3M4.22 19.78l2.12-2.12M17.66 6.34l2.12-2.12",[17972,36584,36585],{"style":36567},"Insights Mode",[104,36587,36568],{"id":36588},"support-mode",[14,36590,36591,36594],{},[364,36592,36593],{},"Support mode"," is for flow-building assistance. Use it when you need help understanding, building, or debugging your Node-RED flows. Expert draws on its knowledge of Node-RED, your installed palette, and the context of your current flows to answer questions, explain and debug flows, and, when agentic flow building is enabled, build flows directly on your canvas.",[14,36596,36597],{},"Typical use cases in Support mode:",[28,36599,36600,36603,36606,36609,36616],{},[31,36601,36602],{},"\"How do I convert data to CSV for writing to file & do you have any flow examples?\"",[31,36604,36605],{},"\"Explain what this flow is doing\"",[31,36607,36608],{},"\"Why is this node outputting a number instead of a string?\"",[31,36610,36611,36612,36615],{},"\"Is ",[18,36613,36614],{},"node-red-contrib-string"," node installed on this instance?\"",[31,36617,36618],{},"\"Build a flow that reads from Modbus and publishes each tag value to MQTT\"",[768,36620,36622],{"id":36621},"building-flows-on-the-canvas","Building Flows on the Canvas",[14,36624,36625],{},"Expert can act on your requests directly by adding tabs, placing and wiring nodes, and configuring node properties on the canvas, rather than only describing what you should do yourself.",[14,36627,36628],{},"To use it, describe what you want to build in the chat input, the same way you would ask a question. Expert will start working immediately, and you can follow along via real-time status updates in the chat panel as each step completes. When it finishes, the result is live on your canvas. You can continue refining it through chat by asking Expert to adjust a configuration, add a node, or change a topic path, or you can edit the canvas directly as normal.",[14,36630,36631],{},[638,36632],{"alt":36633,"dataZoomable":55,"src":36634},"FlowFuse Expert building and configuring a flow directly on the Node-RED canvas","\u002Fdocs\u002Fuser\u002Fimages\u002Fassistant\u002Fflowfuse-expert-building-flow.gif",[14,36636,36637],{},"Some prompts that work well:",[28,36639,36640,36651,36654,36657],{},[31,36641,36642,36643,36646,36647,36650],{},"\"Create a flow that reads Modbus registers from ",[18,36644,36645],{},"192.168.1.10"," every 5 seconds and publishes the values to MQTT on ",[18,36648,36649],{},"factory\u002Fline3\u002Ftemperature","\"",[31,36652,36653],{},"\"Build an OEE dashboard tab with downtime reason buttons and a shift target gauge\"",[31,36655,36656],{},"\"Add a shift handover screen that shows the last 5 alarms and a notes input field\"",[31,36658,36659],{},"\"Set up a flow that polls an HTTP endpoint every minute and sends an alert if the response status is not 200\"",[14,36661,36662],{},"The more specific your prompt, the closer the result will be to what you need. Include node names, topic paths, endpoints, or field names where you have them. Expert will use them directly rather than substituting placeholders.",[1110,36664,36665],{},[14,36666,36667,36668,36672],{},"Availability: This feature is in open beta on FlowFuse Cloud (Starter, Team, and Enterprise tiers), no request needed. It is also available on Self-Hosted Enterprise: ",[41,36669,4253],{"href":36670,"rel":36671},"https:\u002F\u002Fflowfuse.com\u002Fcontact-us\u002F?subject=FlowFuse%20Expert%20Application%20Building",[831]," to get it set up as Self-hosted requires a provisioning token and the bridge to be configured on the instance. Agentic flow building works on both Hosted and Remote Instances, and requires the nr-assistant plugin at v0.16.0 or newer. FlowFuse Expert will let you know when an update is available in your instance's immersive editor.",[768,36674,36676],{"id":36675},"context-what-the-expert-can-see","Context: What the Expert Can See",[14,36678,36679],{},"Support mode becomes significantly more helpful when the Expert has context about your environment. Context is not automatic, you choose what to share with the Expert depending on what you need help with.",[17958,36681,21376,36682,21376,36702,21376,36715],{"style":36548},[41,36683,21380,36686,21380,36699,21376],{"className":36684,"href":36685},[36552],"#palette-context",[18108,36687,17970,36689,17970,36692,17970,36695,17970,36697,21380],{"width":36556,"height":36556,"viewBox":18148,"className":36688,"xmlns":18110},[36558],[36690,36691],"rect",{"x":9921,"y":9921,"width":3145,"height":3145,"strokeWidth":3158,"rx":8359},[36690,36693],{"x":36694,"y":9921,"width":3145,"height":3145,"strokeWidth":3158,"rx":8359},"14",[36690,36696],{"x":9921,"y":36694,"width":3145,"height":3145,"strokeWidth":3158,"rx":8359},[36690,36698],{"x":36694,"y":36694,"width":3145,"height":3145,"strokeWidth":3158,"rx":8359},[17972,36700,36701],{"style":36567},"Palette Context",[41,36703,21380,36706,21380,36712,21376],{"className":36704,"href":36705},[36552],"#flow-context",[18108,36707,17970,36709,21380],{"width":36556,"height":36556,"viewBox":18148,"className":36708,"xmlns":18110},[36558],[16850,36710],{"d":36711,"strokeWidth":3158,"strokeLineCap":18152,"strokeLineJoin":18152},"M5 12h14M12 5l7 7-7 7",[17972,36713,36714],{"style":36567},"Flow Context",[41,36716,21380,36719,21380,36730,21376],{"className":36717,"href":36718},[36552],"#debug-context",[18108,36720,17970,36722,17970,36725,17970,36727,21380],{"width":36556,"height":36556,"viewBox":18148,"className":36721,"xmlns":18110},[36558],[16850,36723],{"d":36724,"strokeWidth":3158,"strokeLineCap":18152},"M12 8v4l3 3",[36577,36726],{"cx":36579,"cy":36579,"r":1774,"strokeWidth":3158},[16850,36728],{"d":36729,"strokeWidth":18170,"strokeLineCap":18152},"M8 2l1.5 2M16 2l-1.5 2",[17972,36731,36732],{"style":36567},"Debug Context",[1146,36734,36701],{"id":36735},"palette-context",[14,36737,36738,36739,36742,36743,36745],{},"To add Palette Context, click the ",[364,36740,36741],{},"Resource Selector"," button (paperclip icon) in the chat interface and select ",[364,36744,4799],{},". Once added, the Expert has access to information about the nodes installed in your Node-RED instance - including installed packages and their versions.",[14,36747,36748],{},"This allows you to ask questions like:",[28,36750,36751,36754,36757],{},[31,36752,36753],{},"\"Is my palette up to date?\"",[31,36755,36756],{},"\"What version of node-red-dashboard is installed?\"",[31,36758,36759],{},"\"Do I have a node available for reading from a PostgreSQL database?\"",[14,36761,36762],{},"The Expert can use palette context to tailor its suggestions - for example, recommending nodes you actually have installed rather than suggesting ones that are not available.",[14,36764,36765],{},[638,36766],{"alt":36767,"dataZoomable":55,"src":36768},"Palette Context discussion with the FlowFuse Expert.","\u002Fdocs\u002Fuser\u002Fimages\u002Fassistant\u002Fpalette-context.gif",[1146,36770,36714],{"id":36771},"flow-context",[14,36773,36774],{},"To add Flow Context, select the flow you want the Expert to reference from the flow tabs in the Node-RED editor. The selected flow is then added as context for the Expert to read and reason about.",[14,36776,36777],{},"This makes it possible to ask questions directly about your flows without having to copy and paste JSON or describe your configuration manually.",[14,36779,36748],{},[28,36781,36782,36785,36788],{},[31,36783,36784],{},"\"What does this flow do?\"",[31,36786,36787],{},"\"Why does this flow output a number instead of a string?\"",[31,36789,36790],{},"\"Is there something wrong with this flow? I don't seem to get an output from the debug node!\"",[14,36792,36793],{},"Flow Context is what makes the Expert genuinely useful as a debugging and code review tool - it can see the same flow you're looking at and reason about it directly.",[14,36795,36796],{},[638,36797],{"alt":36798,"dataZoomable":55,"src":36799},"FlowFuse Context discussion with the FlowFuse Expert.","\u002Fdocs\u002Fuser\u002Fimages\u002Fassistant\u002Fflow-context.gif",[1146,36801,36732],{"id":36802},"debug-context",[14,36804,36805],{},"To add Debug Context, you have 2 options:",[398,36807,36808,36811],{},[31,36809,36810],{},"Add individual log entries by clicking the ➕ button that appears over your debug message",[31,36812,15930,36813,36742,36815,273],{},[364,36814,36741],{},[364,36816,36817],{},"Add Debug Logs",[14,36819,36820],{},"Once added, the Expert has access to the messages and output currently captured in your Node-RED debug panel.",[14,36822,36823],{},"This allows the Expert to reason directly about the data or errors you are getting from your nodes at runtime, not just the structure of the flow itself.",[14,36825,36748],{},[28,36827,36828,36831,36834],{},[31,36829,36830],{},"\"Why is this debug output a number instead of a string?\"",[31,36832,36833],{},"\"What does this error message in the debug panel mean?\"",[31,36835,36836],{},"\"Is the payload structure here what my downstream node expects?\"",[14,36838,36839],{},"Debug Context is especially useful in combination with Flow Context - together they give the Expert both the structure of your flow and the actual data it is producing, making debugging significantly more effective.",[14,36841,36842],{},[638,36843],{"alt":36844,"dataZoomable":55,"src":36845},"Debug Context discussion with the FlowFuse Expert.","\u002Fdocs\u002Fuser\u002Fimages\u002Fassistant\u002Fexpert-debug-context.gif",[768,36847,36849],{"id":36848},"resetting-context","Resetting Context",[14,36851,36852,36853,36856],{},"If the Expert starts giving unexpected or inconsistent answers, it may be due to accumulated context from earlier in the conversation influencing its responses. Use the ",[364,36854,36855],{},"Start Over"," button to clear the conversation and start fresh with a clean context.",[104,36858,36585],{"id":36859},"insights-mode",[14,36861,36862,36865,36866,36869],{},[364,36863,36864],{},"Insights mode"," connects the Expert to your live data via ",[364,36867,36868],{},"Model Context Protocol (MCP)",". Use it when you want to query, analyze, or interact with real-world data - not just your Node-RED flows.",[14,36871,36872,36873,36877,36878,273],{},"In Insights mode, you first select an MCP Server that you've built using ",[41,36874,36876],{"href":18373,"rel":36875},[831],"FlowFuse MCP Server Nodes",". The Expert can then use the tools and resources exposed by that server to answer questions against your live operational data. If you haven't built an MCP Server yet, see the guide on ",[41,36879,36882],{"href":36880,"rel":36881},"https:\u002F\u002Fflowfuse.com\u002Fblog\u002F2025\u002F10\u002Fbuilding-mcp-server-using-flowfuse\u002F",[831],"building an MCP Server using FlowFuse",[14,36884,36885],{},"Typical use cases in Insights mode:",[28,36887,36888,36900,36907],{},[31,36889,36890,36891,36894,36895],{},"You have an MCP Resource named ",[18,36892,36893],{},"production_lines_facilities_list"," that returns a list of your production lines, their facility names and the facility types (stamping, assembly, packaging etc)\n",[28,36896,36897],{},[31,36898,36899],{},"You can ask: \"List all stamping facilities on our production lines\"",[31,36901,36902,36903,36906],{},"You have added an MCP Tool named ",[18,36904,36905],{},"get_production_live_state","\n-You can ask: \"Tell me which of any of my assembly facilities are running and at what speed\"",[31,36908,36909,36910,36913,36914],{},"You have added an MCP tool named ",[18,36911,36912],{},"get_production_oee"," that\n",[28,36915,36916],{},[31,36917,36918],{},"You can ask: \"Show me the worst 3 OEE results for all production line facilities\"",[1110,36920,36921],{},[14,36922,36923,36925],{},[364,36924,1798],{}," Insights mode is currently in Beta. Capabilities are actively being expanded.",[14,36927,36928],{},[364,36929,36930],{},"To switch to Insights mode:",[398,36932,36933,36936,36942,36945],{},[31,36934,36935],{},"Open the FlowFuse Expert panel",[31,36937,36938,36939,36941],{},"Use the ",[364,36940,36544],{}," to switch from \"Chat\" to \"Insights\"",[31,36943,36944],{},"Select the MCP Server you want to query",[31,36946,36947],{},"Ask your question",[23,36949,36951],{"id":36950},"writing-better-queries","Writing Better Queries",[14,36953,36954],{},"The quality of Expert's responses depends heavily on how your prompt is phrased, whether you are asking a question or requesting Expert to build something on the canvas. The more specific and contextual your prompt, the more accurate and actionable the result.",[14,36956,36957],{},"Here are some common patterns to improve your prompts:",[104,36959,36961],{"id":36960},"be-specific-about-what-youre-referring-to","Be specific about what you're referring to",[14,36963,36964],{},"Vague references like \"it\", \"this\", or \"that\" require the Expert to guess what you mean. Name the thing explicitly.",[2289,36966,36967,36977],{},[2292,36968,36969],{},[2295,36970,36971,36974],{},[2298,36972,36973],{},"Less effective",[2298,36975,36976],{},"More effective",[2305,36978,36979,36986,36994],{},[2295,36980,36981,36984],{},[2310,36982,36983],{},"\"Is it up to date?\"",[2310,36985,36753],{},[2295,36987,36988,36991],{},[2310,36989,36990],{},"\"What does this mean?\"",[2310,36992,36993],{},"\"What does this log entry mean?\"",[2295,36995,36996,36999],{},[2310,36997,36998],{},"\"Why did this happen?\"",[2310,37000,37001],{},"\"Why did this error log occur?\"",[104,37003,37005],{"id":37004},"describe-the-actual-problem-not-just-a-symptom","Describe the actual problem, not just a symptom",[14,37007,37008],{},"If something isn't behaving as expected, describe what you expected versus what you got.",[2289,37010,37011,37019],{},[2292,37012,37013],{},[2295,37014,37015,37017],{},[2298,37016,36973],{},[2298,37018,36976],{},[2305,37020,37021,37029],{},[2295,37022,37023,37026],{},[2310,37024,37025],{},"\"This doesn't work\"",[2310,37027,37028],{},"\"My flow should output a string but it is outputting a number\"",[2295,37030,37031,37034],{},[2310,37032,37033],{},"\"The node is broken\"",[2310,37035,37036],{},"\"The HTTP Request node is returning a 401 status code\"",[104,37038,37040],{"id":37039},"include-relevant-details-upfront","Include relevant details upfront",[14,37042,37043],{},"The Expert works best when it doesn't have to ask clarifying questions. Include relevant context - the node type, the message property, the protocol, or the error - in your initial query.",[2289,37045,37046,37054],{},[2292,37047,37048],{},[2295,37049,37050,37052],{},[2298,37051,36973],{},[2298,37053,36976],{},[2305,37055,37056,37064],{},[2295,37057,37058,37061],{},[2310,37059,37060],{},"\"How do I connect to a database?\"",[2310,37062,37063],{},"\"How do I connect to a PostgreSQL database using the node-red-contrib-postgresql node?\"",[2295,37065,37066,37069],{},[2310,37067,37068],{},"\"How do I format this?\"",[2310,37070,37071],{},"\"How do I format a Unix timestamp as an ISO 8601 string in a Function node?\"",[104,37073,37075],{"id":37074},"ask-one-question-at-a-time-for-complex-topics","Ask one question at a time for complex topics",[14,37077,37078],{},"If you have multiple questions, consider asking them separately so the Expert can give a focused answer to each rather than a broad response that covers everything superficially.",[14,37080,37081],{},[1160,37082,4625,37083,37086],{},[41,37084,37085],{"href":1070},"Node-RED Embedded AI"," for AI features built directly into the Node-RED editor.",[23,37088,37090],{"id":37089},"keeping-expert-up-to-date","Keeping Expert Up to Date",[14,37092,37093],{},"When a newer version of FlowFuse Expert is available, a banner appears in the chat area to let you know. You can update with a single click directly from the notification, without leaving your current workflow.",[14,37095,37096],{},[638,37097],{"alt":37098,"dataZoomable":55,"src":37099},"FlowFuse Expert Update Banner","\u002Fdocs\u002Fuser\u002Fimages\u002Fassistant\u002Fff-expert-update-banner.gif",[316,37101,37102],{},"\n  .assistant-feature {\n    border: 1px solid #ddd;\n    border-radius: 6px;\n    padding: 18px;\n    text-align: center;\n    background: #f9f9f9;\n    display: flex;\n    align-items: center;\n    justify-content: flex-start;\n    gap: 12px;\n    text-decoration: none;\n  }\n  .assistant-feature svg.icon-stroke path,\n  .assistant-feature svg.icon-stroke line,\n  .assistant-feature svg.icon-stroke circle,\n  .assistant-feature svg.icon-stroke polyline,\n  .assistant-feature svg.icon-stroke rect {\n    stroke: #2563eb;\n    fill: none;\n  }\n  .assistant-feature:hover {\n    border-color: #2563eb;\n    color: #2563eb;\n    cursor: pointer;\n    text-decoration: none;\n  }\n  .assistant-feature label {\n    font-size: 1.15rem;\n    pointer-events: none;\n  }\n",{"title":55,"searchDepth":77,"depth":77,"links":37104},[37105,37106,37110,37116],{"id":36506,"depth":77,"text":36507},{"id":36537,"depth":77,"text":36538,"children":37107},[37108,37109],{"id":36588,"depth":88,"text":36568},{"id":36859,"depth":88,"text":36585},{"id":36950,"depth":77,"text":36951,"children":37111},[37112,37113,37114,37115],{"id":36960,"depth":88,"text":36961},{"id":37004,"depth":88,"text":37005},{"id":37039,"depth":88,"text":37040},{"id":37074,"depth":88,"text":37075},{"id":37089,"depth":77,"text":37090},{},"user\u002Fexpert\u002Fchat.md","\u002Fdocs\u002Fuser\u002Fexpert\u002Fchat",{"title":36490,"description":36498},"docs\u002Fuser\u002Fexpert\u002Fchat","QdlOOFau7aCg8kb4Mn3CqUHvNGBXuL_T1C9uOJ6YBds",{"id":37124,"title":37125,"body":37126,"description":37133,"extension":329,"layout":330,"meta":37262,"navGroup":330,"navOrder":330,"navTitle":37125,"navigation":187,"originalPath":37263,"path":37264,"redirect":330,"seo":37265,"stem":37266,"updated":337,"version":338,"__hash__":37267},"docs\u002Fdocs\u002Fuser\u002Fexpert\u002Findex.md","FlowFuse Expert",{"type":7,"value":37127,"toc":37254},[37128,37131,37134,37145,37149,37155,37161,37179,37183,37186,37188,37191,37194,37207,37212,37216,37219,37222,37233,37238,37242,37245],[10,37129,37125],{"id":37130},"flowfuse-expert",[14,37132,37133],{},"FlowFuse Expert is the AI built into FlowFuse and the Node-RED editor. It is not a generic AI assistant bolted onto the side of your workflow, it understands your flows, your installed nodes, your live data, and your environment in real time.",[14,37135,37136,37139,37140,37144],{},[364,37137,37138],{},"FlowFuse Expert is automatically installed and available in all hosted and remote instances running within or connected to FlowFuse",", no manual installation or configuration required. For self-hosted Enterprise customers, FlowFuse Expert can be enabled on request. ",[41,37141,37143],{"href":4251,"rel":37142},[831],"Contact us"," to get it set up on your infrastructure.",[23,37146,37148],{"id":37147},"managing-ai-features","Managing AI Features",[14,37150,37151,37154],{},[364,37152,37153],{},"Team owners"," can enable or disable all AI features for their team from the team settings page. When disabled, the Expert chat panel with all AI feature will get removed for that team. Running instances need to be restarted for the change to take full effect.",[14,37156,37157,37160],{},[364,37158,37159],{},"Self-Hosted Enterprise admins"," have two additional controls:",[28,37162,37163,37173],{},[31,37164,37165,37166,3497,37168,37172],{},"Disable AI across the entire platform via the ",[18,37167,19833],{},[41,37169,37171],{"href":37170},"\u002Fdocs\u002Finstall\u002Fconfiguration\u002F#ai-configuration","configuration option",". This overrides all team-level settings.",[31,37174,37175,37176,17083],{},"Configure which AI features are available on a per-team-type basis from the Admin Panel. See ",[41,37177,760],{"href":37178},"\u002Fdocs\u002Fadmin\u002Fintroduction\u002F#managing-team-types",[23,37180,37182],{"id":37181},"what-flowfuse-expert-can-do","What FlowFuse Expert Can Do",[14,37184,37185],{},"FlowFuse Expert works in two distinct ways inside your environment.",[104,37187,36490],{"id":36495},[14,37189,37190],{},"The Chat Interface is a conversational AI panel built into the FlowFuse Platform and accessible directly within the Node-RED editor. With agentic flow building enabled, you can describe what you want to build and Expert will build it on your canvas for you. It can also answer questions, debug flows, and query live operational data via MCP.",[14,37192,37193],{},"The Chat Interface supports two modes:",[28,37195,37196,37201],{},[31,37197,37198,37200],{},[364,37199,4117],{},": flow-building assistance, including asking questions, debugging, and building flows on the canvas",[31,37202,37203,37206],{},[364,37204,37205],{},"Insights",": query live operational data via MCP tools and resources exposed by your own MCP servers",[14,37208,37209],{},[41,37210,37211],{"href":1059},"Learn more about the Chat Interface",[104,37213,37215],{"id":37214},"ai-in-node-red","AI in Node-RED",[14,37217,37218],{},"FlowFuse Expert brings AI assistance directly into the Node-RED editor itself. It works where you already are - inside node editors, on the canvas, without requiring you to open a separate panel.",[14,37220,37221],{},"AI features within the Node-RED editor include inline code completions, flow autocomplete, function builder, flow explainer, JSON generation, and CSS and HTML generation for FlowFuse Dashboard.",[1110,37223,37224],{},[14,37225,37226,37228,37229,37232],{},[364,37227,1798],{}," FlowFuse Expert's in-editor AI features can also be installed as a plugin into Node-RED instances running outside of FlowFuse, using the ",[18,37230,37231],{},"@flowfuse\u002Fnr-assistant"," package from the Node-RED Palette Manager. This requires a FlowFuse Cloud account but does not require a paid subscription for the current release. The Chat Interface is exclusive to FlowFuse and cannot be installed externally.",[14,37234,37235],{},[41,37236,37237],{"href":1070},"Learn more about AI in Node-RED",[23,37239,37241],{"id":37240},"data-privacy","Data Privacy",[14,37243,37244],{},"No data from FlowFuse is used by third-party AI service providers for training models.",[14,37246,37247,37248,37253],{},"Some features utilize the OpenAI API, and as such some data is sent to OpenAI to process requests. In accordance with the ",[41,37249,37252],{"href":37250,"rel":37251},"https:\u002F\u002Fhelp.openai.com\u002Fen\u002Farticles\u002F5722486-how-your-data-is-used-to-improve-model-performance",[831],"OpenAI Terms of Service"," no data is used for training of future models. OpenAI will retain data sent via its APIs for 30 days for abuse monitoring, after which it is permanently deleted.",{"title":55,"searchDepth":77,"depth":77,"links":37255},[37256,37257,37261],{"id":37147,"depth":77,"text":37148},{"id":37181,"depth":77,"text":37182,"children":37258},[37259,37260],{"id":36495,"depth":88,"text":36490},{"id":37214,"depth":88,"text":37215},{"id":37240,"depth":77,"text":37241},{},"user\u002Fexpert\u002Findex.md","\u002Fdocs\u002Fuser\u002Fexpert",{"title":37125,"description":37133},"docs\u002Fuser\u002Fexpert\u002Findex","rrYsxgs95raG7NVU9xOeqcpUaufpbNFDD4AUKUXJlpI",{"id":37269,"title":37215,"body":37270,"description":37668,"extension":329,"layout":330,"meta":37669,"navGroup":330,"navOrder":330,"navTitle":37215,"navigation":187,"originalPath":37670,"path":37671,"redirect":330,"seo":37672,"stem":37673,"updated":337,"version":338,"__hash__":37674},"docs\u002Fdocs\u002Fuser\u002Fexpert\u002Fnode-red-embedded-ai.md",{"type":7,"value":37271,"toc":37654},[37272,37274,37280,37290,37299,37305,37308,37428,37478,37481,37490,37493,37507,37510,37516,37526,37529,37532,37535,37538,37542,37551,37554,37557,37560,37563,37569,37572,37575,37578,37587,37590,37598,37602,37608,37612,37619,37622,37641,37645,37651],[10,37273,37215],{"id":37214},[14,37275,37276,37277,37279],{},"FlowFuse Expert brings AI assistance directly into the Node-RED editor itself. Unlike the ",[41,37278,36490],{"href":1059},", which is a conversational panel you open separately, FlowFuse Expert's in-editor AI works where you already are - inside node editors, on the canvas, and in the palette.",[1110,37281,37282],{},[14,37283,37284,37286,37287,37289],{},[364,37285,1798],{}," FlowFuse Expert can also be installed as a plugin into Node-RED instances running outside of FlowFuse, using the ",[18,37288,37231],{}," package from the Node-RED Palette Manager. This requires a FlowFuse Cloud account but does not require a paid subscription for the current release.",[14,37291,37292,37293],{},"To enable the latest features, ensure your instance is running the latest Stack. ",[364,37294,37295,37296,37298],{},"For FlowFuse Cloud instances, FlowFuse Expert (",[18,37297,37231],{},") is automatically updated to the latest version.",[14,37300,37301,37302,37304],{},"Team owners can disable all AI features, including the in-editor AI, from the team settings page. When AI is disabled, the ",[18,37303,37231],{}," plugin will not be loaded on new deployments. Running instances will need to be restarted for the change to take full effect.",[23,37306,37307],{"id":7199},"Features",[17958,37309,21376,37311,21376,37327,21376,37343,21376,37357,21376,37372,21376,37386,37406,37407],{"style":37310},"display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin: 20px 0;",[41,37312,21380,37315,21380,37324,21376],{"className":37313,"href":37314},[36552],"#flow-autocomplete",[18108,37316,17970,37318,17970,37321,21380],{"width":36556,"height":36556,"viewBox":18148,"className":37317,"xmlns":18110},[36558],[16850,37319],{"d":37320,"strokeWidth":3158,"strokeLineCap":18152},"M3 12c0-2 1-4 3-4s3 2 3 4",[16850,37322],{"d":37323,"strokeWidth":3158,"strokeLineCap":18152},"M9 12c0 2 1 4 3 4s3-2 3-4",[17972,37325,37326],{"style":36567},"Flow Autocomplete",[41,37328,21380,37331,21380,37340,21376],{"className":37329,"href":37330},[36552],"#inline-code-completions",[18108,37332,17970,37334,17970,37337,21380],{"width":36556,"height":36556,"viewBox":18148,"className":37333,"xmlns":18110},[36558],[16850,37335],{"d":37336,"strokeWidth":18170,"strokeLineCap":18152},"M6 7h6M6 11h6M6 15h12",[16850,37338],{"d":37339,"strokeWidth":3158,"strokeLineCap":18152,"strokeLineJoin":18152},"M16 7l2 2-2 2",[17972,37341,37342],{"style":36567},"Inline Code Completions",[41,37344,21380,37347,21380,37354,21376],{"className":37345,"href":37346},[36552],"#flow-explainer",[18108,37348,17970,37350,17970,37352,21380],{"width":36556,"height":36556,"viewBox":18148,"className":37349,"xmlns":18110},[36558],[16850,37351],{"d":36561,"strokeWidth":3158},[16850,37353],{"d":36564,"strokeWidth":18170,"strokeLineCap":18152},[17972,37355,37356],{"style":36567},"Flow Explainer",[41,37358,21380,37361,21380,37369,21376],{"className":37359,"href":37360},[36552],"#function-node-creation",[18108,37362,17970,37366,21380],{"width":36556,"height":36556,"viewBox":37363,"className":37364},"0 0 54 54",[37365],"icon-fill",[16850,37367],{"xmlns":18110,"d":37368},"M30.999 31.005v-3h-6.762s.812-12.397 1.162-14 .597-3.35 2.628-3.103 1.971 3.103 1.971 3.103l4.862-.016s-.783-3.984-2.783-5.984-7.946-1.7-9.633.03c-1.687 1.73-2.302 5.065-2.597 6.422-.588 4.5-.854 9.027-1.248 13.547h-8.6v3H18.1s-.812 12.398-1.162 14-.597 3.35-2.628 3.103-1.972-3.102-1.972-3.102l-4.862.015s.783 3.985 2.783 5.985c2 2 7.946 1.699 9.634-.031 1.687-1.73 2.302-5.065 2.597-6.422.587-4.5.854-9.027 1.248-13.547z",[17972,37370,37371],{"style":36567},"Function Builder",[41,37373,21380,37376,21380,37383,21376],{"className":37374,"href":37375},[36552],"#function-code-generation",[18108,37377,17970,37379,21380],{"width":36556,"height":36556,"viewBox":18148,"className":37378,"xmlns":18110},[37365],[16850,37380],{"d":37381,"fill":37382},"M13 2L3 14h9l-1 8 10-12h-9l1-8z","#2563eb",[17972,37384,37385],{"style":36567},"Function Code Generation",[41,37387,21380,37390,21380,37403,21376],{"className":37388,"href":37389},[36552],"#json-generation",[18108,37391,17970,37393,17970,37396,17970,37400,21380],{"width":36556,"height":36556,"viewBox":18148,"className":37392,"xmlns":18110},[36558],[16850,37394],{"d":37395,"strokeWidth":3158},"M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z",[37397,37398],"polyline",{"points":37399,"stroke":37382,"strokeWidth":3158},"14,2 14,8 20,8",[16850,37401],{"d":37402,"stroke":37382,"strokeWidth":3158,"strokeLineCap":18152},"M8 13h8M8 17h4",[17972,37404,37405],{"style":36567},"JSON Generation","  \n  ",[41,37408,21380,37411,21380,37425,21376],{"className":37409,"href":37410},[36552],"#css-and-html-generation-for-flowfuse-dashboard",[18108,37412,17970,37413,17970,37416,17970,37419,17970,37422,21380],{"width":36556,"height":36556,"viewBox":18148,"fill":18147,"xmlns":18110},[16850,37414],{"d":37415,"stroke":37382,"strokeWidth":3158},"M21 16V8a2 2 0 00-1-1.73l-7-4a2 2 0 00-2 0l-7 4A2 2 0 003 8v8a2 2 0 001 1.73l7 4a2 2 0 002 0l7-4A2 2 0 0021 16z",[37397,37417],{"points":37418,"stroke":37382,"strokeWidth":3158},"3.29,7 12,12 20.71,7",[61,37420],{"x1":36579,"y1":37421,"x2":36579,"y2":36579,"stroke":37382,"strokeWidth":3158},"22",[16850,37423],{"d":37424,"stroke":37382,"strokeWidth":8359},"M8 10l4-2 4 2",[17972,37426,37427],{"style":36567},"CSS & HTML Generation",[398,37429,37430,37436,37445,37451,37457,37463,37469],{},[31,37431,37432,37435],{},[364,37433,37434],{},"Flow Autocomplete:"," Automated, intelligent suggestions for which node should be added next in your flow",[31,37437,37438,37441,37442,30076],{},[364,37439,37440],{},"Inline Code Completions:"," Inline code completions for Function node, Tables Query node and FlowFuse Dashboard ",[18,37443,37444],{},"ui-template",[31,37446,37447,37450],{},[364,37448,37449],{},"Flow Explainer:"," Get detailed explanations of the selected nodes in your flow",[31,37452,37453,37456],{},[364,37454,37455],{},"Function Node Creation:"," Create a new function node directly, driven by natural language",[31,37458,37459,37462],{},[364,37460,37461],{},"Function Code Generation:"," Within the scope of an existing function node, ask the assistant to write code for you",[31,37464,37465,37468],{},[364,37466,37467],{},"JSON Generation:"," In-editor JSON generation within the JSON editor for all typed inputs and JSON editors",[31,37470,37471,37474,37475,37477],{},[364,37472,37473],{},"CSS and HTML Generation:"," In-editor CSS and HTML generation for FlowFuse Dashboard ",[18,37476,37444],{}," nodes",[104,37479,37326],{"id":37480},"flow-autocomplete",[14,37482,37483,37488],{},[638,37484],{"alt":37485,"src":37486,"dataZoomable":55,"width":37487},"Recording of the flow autocomplete in-action, with up\u002Fdown keys used to toggle suggestions and tab to move to the next suggestion","\u002Fdocs\u002Fuser\u002Fimages\u002Fassistant\u002Fnode-autocomplete.gif","700px",[1160,37489,37485],{},[14,37491,37492],{},"FlowFuse Expert runs a trained, in-browser machine learning model that provides intelligent suggestions for which node should be added next in your flow.",[14,37494,37495,37496,37499,37500,302,37503,37506],{},"You can accept the suggestion by clicking it or by pressing the ",[18,37497,37498],{},"Tab"," key. You can also toggle through suggestions by pressing the ",[18,37501,37502],{},"Up",[18,37504,37505],{},"Down"," keys.",[104,37508,37342],{"id":37509},"inline-code-completions",[14,37511,37512,37513,37515],{},"Mimicking the familiar code assistant in your IDE, FlowFuse Expert provides inline code completions for Function nodes, Tables Query nodes, and FlowFuse Dashboard ",[18,37514,37444],{}," nodes.",[14,37517,37518,37523],{},[638,37519],{"alt":37520,"src":37521,"dataZoomable":55,"width":37522},"inline code completions","\u002Fdocs\u002Fuser\u002Fimages\u002Fassistant\u002Finline-completion.png","450px",[1160,37524,37525],{},"A simple example of inline code completions for a Function node",[14,37527,37528],{},"This feature accelerates the writing of custom code and queries by providing intelligent suggestions without having to leave the editor, lowering the barrier to entry for non-technical users.",[14,37530,37531],{},"Using comments is optional - the assistant will do its best to understand the context of the code and provide suggestions based on the surrounding code. However, writing comments does help to frame the request in a way that is more likely to produce accurate results.",[104,37533,37356],{"id":37534},"flow-explainer",[14,37536,37537],{},"FlowFuse Expert adds a button to the Assistant menu that will explain what the selected nodes do. To use this feature, select one or more nodes on the canvas and click the \"Explain Flows\" button in the Assistant menu.",[104,37539,37541],{"id":37540},"function-node-creation","Function Node Creation",[14,37543,37544,37548],{},[638,37545],{"alt":37546,"src":37547},"FlowFuse Expert dialog","\u002Fdocs\u002Fuser\u002Fimages\u002Fassistant\u002Fdialog-function-node-builder.png",[1160,37549,37550],{},"Screenshot showing the FlowFuse Expert dialog for creating a function node",[14,37552,37553],{},"Use natural language to request a new function node be added to your Node-RED flow. This is useful when you want to quickly add a function node without having to drag it from the palette and write the code yourself.",[14,37555,37556],{},"If your instance supports external modules, you can ask for a function node that uses them and they will be added to the function node setup automatically. If your function node requires multiple outputs, FlowFuse Expert will set the correct number of outputs accordingly.",[104,37558,37385],{"id":37559},"function-code-generation",[14,37561,37562],{},"FlowFuse Expert adds a code lens to the function node editor that allows you to generate code directly within the editor.",[14,37564,37565],{},[638,37566],{"alt":37567,"src":37568,"dataZoomable":55},"inline code lens","\u002Fdocs\u002Fuser\u002Fimages\u002Fassistant\u002Ffunction-node-inline-code-lens.png",[14,37570,37571],{},"This is useful when you want to quickly add or rewrite code within an existing function node without generating a full new function node from scratch.",[104,37573,37405],{"id":37574},"json-generation",[14,37576,37577],{},"FlowFuse Expert adds a code lens to the JSON editor that allows you to generate JSON directly within the Monaco editor.",[14,37579,37580,37584],{},[638,37581],{"alt":37582,"src":37583,"dataZoomable":55,"width":37487},"json generation","\u002Fdocs\u002Fuser\u002Fimages\u002Fassistant\u002Fjson-prompt.png",[1160,37585,37586],{},"Screenshot showing a FlowFuse Expert prompt for JSON generation",[14,37588,37589],{},"This is useful when you want to quickly generate JSON for a prototype or to test a piece of functionality in your flows.",[14,37591,37592,37595],{},[638,37593],{"alt":37582,"src":37594,"dataZoomable":55,"width":37487},"\u002Fdocs\u002Fuser\u002Fimages\u002Fassistant\u002Fjson-results.png",[1160,37596,37597],{},"Screenshot showing the result of the above FlowFuse Expert prompt",[104,37599,37601],{"id":37600},"css-and-html-generation-for-flowfuse-dashboard","CSS and HTML Generation for FlowFuse Dashboard",[14,37603,37604,37605,37607],{},"FlowFuse Expert adds a code lens to the FlowFuse Dashboard ",[18,37606,37444],{}," node that allows you to generate CSS and HTML directly within the code editor. It is aware of the context of the node and will generate suitable CSS and HTML components for Vuetify and the FlowFuse Dashboard.",[23,37609,37611],{"id":37610},"installing-in-external-node-red-instances","Installing in External Node-RED Instances",[1110,37613,37614],{},[14,37615,37616,37618],{},[364,37617,1798],{}," FlowFuse Cloud instances have FlowFuse Expert automatically installed. Manual installation is only needed for Node-RED instances running outside of FlowFuse. Only the in-editor AI features can be installed externally - the Chat Interface is exclusive to FlowFuse.",[14,37620,37621],{},"To install the plugin in your own Node-RED instance:",[398,37623,37624,37629,37632,37635,37638],{},[31,37625,37626,37627],{},"Use the Node-RED Palette Manager to install the package ",[18,37628,37231],{},[31,37630,37631],{},"Restart Node-RED",[31,37633,37634],{},"Click the FlowFuse Expert icon in the header",[31,37636,37637],{},"Follow the prompts to connect to your FlowFuse Cloud account",[31,37639,37640],{},"Once connected, you will be able to use all FlowFuse Expert features",[104,37642,37644],{"id":37643},"flowfuse-expert-for-self-hosted-customers","FlowFuse Expert for self-hosted customers",[14,37646,37647,37648,37650],{},"If you are self-hosting FlowFuse with an Enterprise license, get in touch with ",[41,37649,4116],{"href":4123}," who will be able to help get you set up to use FlowFuse Expert locally.",[316,37652,37653],{},"\n  .assistant-feature {\n    border: 1px solid #ddd;\n    border-radius: 6px;\n    padding: 18px;\n    text-align: center;\n    background: #f9f9f9;\n    display: flex;\n    align-items: center;\n    justify-content: flex-start;\n    gap: 12px;\n    text-decoration: none;\n  }\n  .assistant-feature svg.icon-fill path,\n  .assistant-feature svg.icon-fill line,\n  .assistant-feature svg.icon-fill circle,\n  .assistant-feature svg.icon-fill polyline {\n    fill: #2563eb;\n  }\n\n  .assistant-feature svg.icon-stroke path,\n  .assistant-feature svg.icon-stroke line,\n  .assistant-feature svg.icon-stroke circle,\n  .assistant-feature svg.icon-stroke polyline {\n    stroke: #2563eb;\n    fill: none;\n  }\n\n  .assistant-feature:hover {\n    border-color: #2563eb;\n    color: #2563eb;\n    cursor: pointer;\n    text-decoration: none;\n  }\n\n  .assistant-feature label {\n    font-size: 1.15rem;\n    pointer-events: none;\n  }\n",{"title":55,"searchDepth":77,"depth":77,"links":37655},[37656,37665],{"id":7199,"depth":77,"text":37307,"children":37657},[37658,37659,37660,37661,37662,37663,37664],{"id":37480,"depth":88,"text":37326},{"id":37509,"depth":88,"text":37342},{"id":37534,"depth":88,"text":37356},{"id":37540,"depth":88,"text":37541},{"id":37559,"depth":88,"text":37385},{"id":37574,"depth":88,"text":37405},{"id":37600,"depth":88,"text":37601},{"id":37610,"depth":77,"text":37611,"children":37666},[37667],{"id":37643,"depth":88,"text":37644},"FlowFuse Expert brings AI assistance directly into the Node-RED editor itself. Unlike the Chat Interface, which is a conversational panel you open separately, FlowFuse Expert's in-editor AI works where you already are - inside node editors, on the canvas, and in the palette.",{},"user\u002Fexpert\u002Fnode-red-embedded-ai.md","\u002Fdocs\u002Fuser\u002Fexpert\u002Fnode-red-embedded-ai",{"title":37215,"description":37668},"docs\u002Fuser\u002Fexpert\u002Fnode-red-embedded-ai","TKJKj-2eETEgYffuU58NC9tzKkWyHAzNYcu0rUR-Ktc",{"id":37676,"title":37677,"body":37678,"description":55,"extension":329,"layout":330,"meta":37772,"navGroup":330,"navOrder":330,"navTitle":37677,"navigation":187,"originalPath":37773,"path":1025,"redirect":330,"seo":37774,"stem":37775,"updated":337,"version":338,"__hash__":37776},"docs\u002Fdocs\u002Fuser\u002Fff-tables.md","FlowFuse Tables",{"type":7,"value":37679,"toc":37768},[37680,37683,37702,37705,37708,37716,37719,37727,37730,37738,37742,37745,37753,37757,37760],[10,37681,37677],{"id":37682},"flowfuse-tables",[17958,37684,37687,37691],{"className":37685},[21721,37686],"ff-callout--warning",[14,37688,37690],{"className":37689},[21726],"Warning",[17958,37692,37694],{"className":37693},[21730],[14,37695,37696,37697,273],{},"This feature is currently in ",[41,37698,37701],{"href":37699,"rel":37700},"https:\u002F\u002Fflowfuse.com\u002Fhandbook\u002Fengineering\u002Freleases\u002F#beta-release",[831],"the beta state",[14,37703,37704],{},"From FlowFuse v2.20.0 Teams (Enterprise teams only) can create a relational database to use to store data.",[14,37706,37707],{},"You can create a database by selecting the Tables entry in the left hand menu",[14,37709,37710,37714],{},[638,37711],{"alt":37712,"dataZoomable":55,"src":37713},"Screenshot of FF Tables create database menu","\u002Fdocs\u002Fuser\u002Fimages\u002Ftables\u002Fcreate-database.png",[1160,37715,37712],{},[14,37717,37718],{},"Once you have created a database you can use the wizard to create some tables. The wizard offers a subset of the most used column types and has the option to set default values and generate sequences for ids.",[14,37720,37721,37725],{},[638,37722],{"alt":37723,"dataZoomable":55,"src":37724},"Screenshot of create tables wizard","\u002Fdocs\u002Fuser\u002Fimages\u002Ftables\u002Fcreate-table-wizard.png",[1160,37726,37723],{},[14,37728,37729],{},"Once the tables have data in them then the first 10 rows will be displayed in the Explorer tab.",[14,37731,37732,37736],{},[638,37733],{"alt":37734,"dataZoomable":55,"src":37735},"Screenshot of Tables Explorer","\u002Fdocs\u002Fuser\u002Fimages\u002Ftables\u002Ftables-ui-screenshot.png",[1160,37737,37734],{},[23,37739,37741],{"id":37740},"query-nodes","Query Nodes",[14,37743,37744],{},"FlowFuse Node-RED instances come with a Query Node that will be enabled if running in a Team with a FlowFuse Tables database enabled. This node will automatically import the required credentials to connect to the database.",[14,37746,37747,37751],{},[638,37748],{"alt":37749,"dataZoomable":55,"src":37750},"Screenshot of Node-RED query node","\u002Fdocs\u002Fuser\u002Fimages\u002Ftables\u002Ftables-query-node.png",[1160,37752,37749],{},[23,37754,37756],{"id":37755},"postgres-clients","Postgres Clients",[14,37758,37759],{},"You can connect any Postgres Client to the FlowFuse Tables database using the information on the Credentials tab",[14,37761,37762,37766],{},[638,37763],{"alt":37764,"dataZoomable":55,"src":37765},"Screenshot of credentials","\u002Fdocs\u002Fuser\u002Fimages\u002Ftables\u002Fcredentials.png",[1160,37767,37764],{},{"title":55,"searchDepth":77,"depth":77,"links":37769},[37770,37771],{"id":37740,"depth":77,"text":37741},{"id":37755,"depth":77,"text":37756},{},"user\u002Fff-tables.md",{"title":37677,"description":55},"docs\u002Fuser\u002Fff-tables","NtwaSDacqsHOUBx-Dr6OE_AzBcNzc8bPWqmkV_LlSPw",{"id":37778,"title":37779,"body":37780,"description":37787,"extension":329,"layout":330,"meta":37878,"navGroup":330,"navOrder":330,"navTitle":37779,"navigation":187,"originalPath":37879,"path":37880,"redirect":330,"seo":37881,"stem":37882,"updated":337,"version":338,"__hash__":37883},"docs\u002Fdocs\u002Fuser\u002Ffilenodes.md","FlowFuse File Nodes",{"type":7,"value":37781,"toc":37872},[37782,37785,37788,37791,37805,37809,37812,37815,37821,37834,37837,37843,37845,37848,37853,37858,37865,37869],[10,37783,37779],{"id":37784},"flowfuse-file-nodes",[14,37786,37787],{},"Node-RED instances running within FlowFuse Cloud with Launcher version before 2.7.0\ninclude a modified set of nodes that make it possible to store files safely regardless of\nthe environment.\nCloud based instances can read and write to persistent storage using these nodes.\nEdge devices will store files on its local filesystem.",[14,37789,37790],{},"There are two nodes in the File Node collection:",[28,37792,37793,37799],{},[31,37794,37795,37798],{},[18,37796,37797],{},"file"," - A file node for writing to persistent storage",[31,37800,37801,37804],{},[18,37802,37803],{},"file in"," - A file node for reading from persistent storage",[23,37806,37808],{"id":37807},"flowfuse-270-and-later","FlowFuse 2.7.0 and later",[14,37810,37811],{},"With the release of FlowFuse v2.7.0 a new Persistent Storage feature was enabled.",[14,37813,37814],{},"This allows the default Node-RED File Nodes to work safely by mounting a volume to\nensure files are persisted across all restarts of the Instance.",[14,37816,37817,37818,37820],{},"On Docker, Kubernetes self hosted instances and FlowFuse Cloud the volume is mounted\non ",[18,37819,23789],{},". The Current Working Directory for the Node-RED process is set to\nthis directory, this means that if you do not specify a path in a node it will created\nor read from this directory.",[14,37822,37823,37824,37827,37828,37830,37831],{},"For LocalFS builds a ",[18,37825,37826],{},"storage"," directory is created in the Instance User Directory,\nthis means that if FlowFuse is installed in ",[18,37829,9960],{}," the directory will be at\n",[18,37832,37833],{},"\u002Fopt\u002Fflowfuse\u002Fvar\u002Fprojects\u002F\u003Cproject-id>\u002Fstorage",[23,37835,37836],{"id":1242},"Templates",[14,37838,37839,37840,37842],{},"On FlowFuse Cloud before v2.7.0 the Default Template had an explicit exclusion for\n",[18,37841,20052],{}," to ensure that the replacement FlowFuse File Nodes were loaded. This\nTemplate was renamed to \"Default v1\" to differentiate it. If your instance is using\nthis version of the template then it will need modifying to allow access to the\ndefault Node-RED File Nodes to make use of the new Persistent Storage. Please\ncontact FlowFuse support to arrange this, they will also be able to migrate any\nfiles stored in the old service.",[23,37844,33688],{"id":33687},[14,37846,37847],{},"Simply drop the file nodes into your flows as you would with the regular file nodes in Node-RED.",[14,37849,37850,37852],{},[364,37851,31733],{}," Write string to a file, then read from the file",[15568,37854],{"width":473,"height":473,"src":37855,"allow":37856,"style":37857},"https:\u002F\u002Fflows.nodered.org\u002Fflow\u002F7f93fbbf67f9dc4e81bfbeb2b921881e\u002Fshare","clipboard-read; clipboard-write","border: none;",[14,37859,37860,37861,37864],{},"There are more helpful built-in examples on the ",[364,37862,37863],{},"Import Examples"," dialog in Node-RED.",[23,37866,37868],{"id":37867},"deployment-considerations","Deployment Considerations",[14,37870,37871],{},"When a snapshot is deployed to a device, the original Node-RED file nodes are used and\nany files will be stored on the device's local filesystem.",{"title":55,"searchDepth":77,"depth":77,"links":37873},[37874,37875,37876,37877],{"id":37807,"depth":77,"text":37808},{"id":1242,"depth":77,"text":37836},{"id":33687,"depth":77,"text":33688},{"id":37867,"depth":77,"text":37868},{},"user\u002Ffilenodes.md","\u002Fdocs\u002Fuser\u002Ffilenodes",{"title":37779,"description":37787},"docs\u002Fuser\u002Ffilenodes","aZeqvTW_i-QIlFDzEymvReA-YZEyUXyvOynl7I6fQcA",{"id":37885,"title":37886,"body":37887,"description":37894,"extension":329,"layout":330,"meta":38065,"navGroup":330,"navOrder":330,"navTitle":37886,"navigation":187,"originalPath":38066,"path":981,"redirect":330,"seo":38067,"stem":38068,"updated":337,"version":338,"__hash__":38069},"docs\u002Fdocs\u002Fuser\u002Fhigh-availability.md","High Availability mode",{"type":7,"value":37888,"toc":38053},[37889,37892,37895,37898,37905,37908,37912,37938,37941,37945,37948,37951,37954,37962,37965,37969,37972,37975,37979,37982,37984,37990,37993,37997,38000,38003,38006,38010,38013,38016,38023,38026,38030,38033,38036,38045,38050],[10,37890,37886],{"id":37891},"high-availability-mode",[14,37893,37894],{},"High Availability mode allows you to run multiple copies of your Node-RED instance,\nwith incoming work distributed between them.",[14,37896,37897],{},"The following requirements apply:",[28,37899,37900,37903],{},[31,37901,37902],{},"FlowFuse 1.8+ running with an EE license and the kubernetes driver",[31,37904,4067],{},[14,37906,37907],{},"Within FlowFuse Cloud it is currently free to use for all teams, but will\nbecome a chargeable feature in a future release.",[23,37909,37911],{"id":37910},"restrictions","Restrictions",[28,37913,37914,37917,37920,37926,37929,37935],{},[31,37915,37916],{},"Enabling or disabling HA mode requires a restart of the Instance.",[31,37918,37919],{},"When in HA mode, two copies of the flows are run.",[31,37921,37922,37923,37925],{},"Flows cannot be directly modified in an HA Instance; the editor is disabled.\nA ",[41,37924,34801],{"href":15817}," should be created to deploy new flows to the instance.",[31,37927,37928],{},"Any internal state of the flows is not shared between the HA copies.",[31,37930,372,37931,37934],{},[41,37932,23859],{"href":37933},"\u002Fdocs\u002Fuser\u002Fpersistent-context"," is not synchronised\nbetween the HA copies",[31,37936,37937],{},"The logs view show combined logs of all copies. An identifier indicates which replica the log message originates from. You have the possibility to filter the messages.",[14,37939,37940],{},"More details of these restrictions are available below.",[23,37942,37944],{"id":37943},"setting-up-high-availability-mode","Setting up High Availability mode",[14,37946,37947],{},"To make full use of the HA mode you will need two separate Node-RED instances.",[14,37949,37950],{},"The first will be your development instance. This is where you can build and test\nyour flows.",[14,37952,37953],{},"The second will be your HA 'production' instance. This instance will run multiple\ncopies of the flows and get updated from the development instance using a DevOps\nPipeline between the two instances.",[14,37955,37956,37957,37961],{},"To enable HA mode on your production instance, open the ",[41,37958,37960],{"href":37959},"\u002Fdocs\u002Fuser\u002Finstance-settings","settings page"," for your\ninstance and go to the High Availability section. Click the 'Enable HA mode' button\nand confirm the choice in the dialog.",[14,37963,37964],{},"Once enabled the instance will be restarted to apply the settings. Once enabled,\nthe editor will no longer be available.",[23,37966,37968],{"id":37967},"editing-an-ha-instance","Editing an HA instance",[14,37970,37971],{},"As the editor is disabled for an HA instance the flows cannot be directly modified.",[14,37973,37974],{},"There are two options for updating the flows on an HA instance.",[104,37976,37978],{"id":37977},"disabling-ha-mode","Disabling HA mode",[14,37980,37981],{},"If you disable HA mode via the setting page, after the instance has been restarted\nthe editor will be available and changes can be made. However, this will mean a\nperiod of downtime whilst the instance is brought in and out of HA mode.",[104,37983,34801],{"id":34968},[14,37985,25513,37986,37989],{},[41,37987,37988],{"href":15817},"DevOps pipeline"," from a 'development' Instance to push updates to your HA\ninstance.",[14,37991,37992],{},"This allows you to free develop your flows in one instance and when you are happy\nwith the results, use the pipeline to push the changes over to the HA instance.",[23,37994,37996],{"id":37995},"building-ha-ready-flows","Building HA-ready flows",[14,37998,37999],{},"Whilst FlowFuse can help run multiple copies of your flows, and provide the\nnecessary load balancing between those copies, it still requires the flows to\nhave been created with HA in mind.",[14,38001,38002],{},"This guide provides some things to consider when building HA-ready flows. We'll\ncontinue to expand on this as we gather more feedback from our users.",[14,38004,38005],{},"There are two things to consider - how state is managed and how work is distributed\nbetween the copies.",[104,38007,38009],{"id":38008},"managing-state","Managing state",[14,38011,38012],{},"The most well-suited flows for HA are those that do not depend on local state being\nmaintain between individual messages coming into the flow. This is because, in an HA\ninstance, the messages are distributed between the copies - none of them get to see\nevery message.",[14,38014,38015],{},"When state is required, it needs to be stored somewhere that all instances can\naccess it - for example an external database service.",[14,38017,38018,38019,38022],{},"FlowFuse provides a ",[41,38020,38021],{"href":37933},"persistent context"," option - however\nthis includes a local caching layer that means it doesn't fully synchronize between\nthe instances in real time. This is something we'll be working on for a future iteration\nof the feature, as it will require some changes in Node-RED to unlock its full potential.",[14,38024,38025],{},"There is also the state that is implicitly maintained within the nodes of a flow.\nFor example, the Smooth node can be used to calculate a running average value of\nmessages passing through it. The node does that by keeping in memory the recent\nvalues so it can recalculate the average with each update. In an HA instance,\nthe node will only be calculating the average for the messages it sees.",[104,38027,38029],{"id":38028},"routing-work","Routing work",[14,38031,38032],{},"When HA is enabled, all HTTP traffic directed at the instance will be load-balanced\nbetween the copies automatically.",[14,38034,38035],{},"It gets a little more complicated where flows connect out to external systems to\npull in work - for example, when using MQTT.",[14,38037,38038,38039,38044],{},"MQTT includes a feature called ",[41,38040,38043],{"href":38041,"rel":38042},"https:\u002F\u002Fwww.hivemq.com\u002Fblog\u002Fmqtt5-essentials-part7-shared-subscriptions\u002F",[831],"Shared Subscriptions","\nthat allows a broker to distribute messages between a group of subscribers. This\nprovides the load balancing needed for an HA instance - but the flow must be configured\nto use the appropriate shared subscription topics, as well as to not set a ClientID on the connection to avoid conflicts between the connections.",[14,38046,372,38047,38049],{},[41,38048,790],{"href":796}," have been updated to support shared subscriptions\nautomatically when running in HA mode.",[14,38051,38052],{},"There are lots of other nodes that can be used to trigger flows, whether by\nlistening for events on an API, connecting to locally attached hardware and many\nthings in between. Typically, those that are more cloud-aligned, such as messaging\nsystems like Kafka and AMQP will have very well established ways of doing load\nbalancing.",{"title":55,"searchDepth":77,"depth":77,"links":38054},[38055,38056,38057,38061],{"id":37910,"depth":77,"text":37911},{"id":37943,"depth":77,"text":37944},{"id":37967,"depth":77,"text":37968,"children":38058},[38059,38060],{"id":37977,"depth":88,"text":37978},{"id":34968,"depth":88,"text":34801},{"id":37995,"depth":77,"text":37996,"children":38062},[38063,38064],{"id":38008,"depth":88,"text":38009},{"id":38028,"depth":88,"text":38029},{},"user\u002Fhigh-availability.md",{"title":37886,"description":37894},"docs\u002Fuser\u002Fhigh-availability","14LL-yNZHysQFKogTosqqSZ51C8wOvnNi_J14gUTPFU",{"id":38071,"title":38072,"body":38073,"description":38170,"extension":329,"layout":330,"meta":38171,"navGroup":330,"navOrder":330,"navTitle":38072,"navigation":187,"originalPath":38172,"path":927,"redirect":330,"seo":38173,"stem":38174,"updated":337,"version":338,"__hash__":38175},"docs\u002Fdocs\u002Fuser\u002Fhttp-access-tokens.md","HTTP Access Tokens",{"type":7,"value":38074,"toc":38166},[38075,38078,38086,38089,38092,38096,38099,38102,38107,38111,38117,38131,38152,38159,38164],[10,38076,38072],{"id":38077},"http-access-tokens",[14,38079,38080,38081,38085],{},"When a FlowFuse instance is secured with ",[41,38082,38084],{"href":38083},"\u002Fdocs\u002Fuser\u002Finstance-settings#flowfuse-user-authentication","FlowFuse User Authentication",",\nonly users who are in the same FlowFuse team can access the dashboard and HTTP endpoints\ncreated within Node-RED.",[14,38087,38088],{},"In some cases, it is also necessary to provide secure access to the endpoints for other applications to use.\nThis is where the HTTP Access Tokens can be used.",[14,38090,38091],{},"With the FlowFuse User Authentication option enabled on the Instance's Security settings tab, the table of access\ntokens is shown.",[104,38093,38095],{"id":38094},"creating-an-access-token","Creating an access token",[14,38097,38098],{},"To create a token, click the Add Token button. Given your token a name and an optional expiry date, then click create.",[14,38100,38101],{},"You will be shown the token value - this is the only time it will be shown so make a note of it before closing the dialog.",[14,38103,38104],{},[638,38105],{"alt":55,"src":38106},"\u002Fdocs\u002Fuser\u002Fimages\u002Fbearer-token-dialog.png",[104,38108,38110],{"id":38109},"using-an-access-token","Using an access token",[14,38112,38113,38114,38116],{},"The token can be used when making an HTTP request to the Node-RED instance by providing it in the ",[18,38115,3906],{}," http header as\na bearer token.",[14,38118,38119,38120,38123,38124,38127,38128,38130],{},"For example, if a Node-RED instance has a flow deployed that includes an ",[18,38121,38122],{},"HTTP In"," node listening on ",[18,38125,38126],{},"\u002Ftoken-demo",", the following\n",[18,38129,1381],{}," command can be used to access it:",[50,38132,38134],{"className":22928,"code":38133,"language":22930,"meta":55,"style":55},"curl -H \"Authorization: Bearer ffhttp_FKc_S4qlTBV1H411hmhneHcSJ6F_FGNQLPYbnoD3-B0\" \\\n   https:\u002F\u002Fexample.flowfuse.cloud\u002Ftoken-demo\n",[18,38135,38136,38147],{"__ignoreMap":55},[59,38137,38138,38140,38142,38145],{"class":61,"line":62},[59,38139,1381],{"class":65},[59,38141,1384],{"class":73},[59,38143,38144],{"class":69}," \"Authorization: Bearer ffhttp_FKc_S4qlTBV1H411hmhneHcSJ6F_FGNQLPYbnoD3-B0\"",[59,38146,74],{"class":73},[59,38148,38149],{"class":61,"line":77},[59,38150,38151],{"class":69},"   https:\u002F\u002Fexample.flowfuse.cloud\u002Ftoken-demo\n",[14,38153,38154,38155,38158],{},"To access it from another Node-RED instance, you can use the ",[18,38156,38157],{},"HTTP Request"," node by enabling the 'Use authentication'\noption, selecting 'bearer authentication' and providing the token:",[14,38160,38161],{},[638,38162],{"alt":55,"src":38163},"\u002Fdocs\u002Fuser\u002Fimages\u002Fbearer-token-nr-request.png",[316,38165,34096],{},{"title":55,"searchDepth":77,"depth":77,"links":38167},[38168,38169],{"id":38094,"depth":88,"text":38095},{"id":38109,"depth":88,"text":38110},"When a FlowFuse instance is secured with FlowFuse User Authentication,\nonly users who are in the same FlowFuse team can access the dashboard and HTTP endpoints\ncreated within Node-RED.",{},"user\u002Fhttp-access-tokens.md",{"title":38072,"description":38170},"docs\u002Fuser\u002Fhttp-access-tokens","uDKdwAcakuGBd55OSiNbKFWEpP-DDZQQrrTfvmTZWWY",{"id":38177,"title":55,"body":38178,"description":55,"extension":329,"layout":532,"meta":38182,"navGroup":3950,"navOrder":62,"navTitle":18081,"navigation":187,"originalPath":38183,"path":38184,"redirect":38185,"seo":38187,"stem":38188,"updated":337,"version":338,"__hash__":38189},"docs\u002Fdocs\u002Fuser\u002Findex.md",{"type":7,"value":38179,"toc":38180},[],{"title":55,"searchDepth":77,"depth":77,"links":38181},[],{},"user\u002FREADME.md","\u002Fdocs\u002Fuser",{"to":38186},"\u002Fdocs\u002Fuser\u002Fintroduction",{"description":55},"docs\u002Fuser\u002Findex","iQkmoz20VtCuq7XyS0Ok_vttk64ZNsORw4bvy8MACM8",{"id":38191,"title":36086,"body":38192,"description":38199,"extension":329,"layout":330,"meta":38499,"navGroup":330,"navOrder":330,"navTitle":36086,"navigation":187,"originalPath":38500,"path":37959,"redirect":330,"seo":38501,"stem":38502,"updated":337,"version":338,"__hash__":38503},"docs\u002Fdocs\u002Fuser\u002Finstance-settings.md",{"type":7,"value":38193,"toc":38480},[38194,38197,38200,38206,38209,38212,38218,38221,38318,38321,38324,38327,38330,38333,38336,38339,38342,38345,38348,38351,38354,38357,38362,38365,38370,38373,38376,38399,38402,38405,38407,38410,38413,38416,38419,38422,38425,38430,38433,38436,38449,38452,38455,38458,38466,38469],[10,38195,36086],{"id":38196},"instance-settings",[14,38198,38199],{},"The Instance Settings allow you to customize many aspects of your Node-RED runtime.",[14,38201,38202,38203,38205],{},"To access them, click on an instance from the FlowFuse platform and select the ",[364,38204,17558],{}," tab. The instance view also includes other tabs such as Overview, Devices, Version History, Assets, and Node-RED Logs.",[14,38207,38208],{},"You can also access all the same settings from within the immersive Node-RED editor. The settings drawer sits alongside the canvas without overlapping it, so you can adjust environment variables, manage snapshots, update the palette, and make other changes without leaving your flow.",[14,38210,38211],{},"You can customize the drawer layout using the eye icon at the top of the drawer, which lets you move it from right to left, pin or unpin it, and toggle fullscreen mode.",[14,38213,38214],{},[638,38215],{"alt":38216,"src":38217},"Instance settings drawer","\u002Fdocs\u002Fuser\u002Fimages\u002Finstance-settings.png",[14,38219,38220],{},"Instance Settings are split into a number of sections:",[28,38222,38223],{},[31,38224,38225,38228],{},[41,38226,36086],{"href":38227},"#instance-settings",[28,38229,38230,38267,38272,38277,38282,38307,38312],{},[31,38231,38232,38236],{},[41,38233,38235],{"href":38234},"#general","General",[28,38237,38238,38243,38249,38255,38261],{},[31,38239,38240],{},[41,38241,34732],{"href":38242},"#change-stack",[31,38244,38245],{},[41,38246,38248],{"href":38247},"#copy-instance","Copy Instance",[31,38250,38251],{},[41,38252,38254],{"href":38253},"#import-instance","Import Instance",[31,38256,38257],{},[41,38258,38260],{"href":38259},"#suspend-instance","Suspend Instance",[31,38262,38263],{},[41,38264,38266],{"href":38265},"#delete-instance","Delete Instance",[31,38268,38269],{},[41,38270,36316],{"href":38271},"#environment",[31,38273,38274],{},[41,38275,975],{"href":38276},"#high-availability",[31,38278,38279],{},[41,38280,4796],{"href":38281},"#editor",[31,38283,38284,38288],{},[41,38285,38287],{"href":38286},"#security","Security",[28,38289,38290,38296,38302],{},[31,38291,38292],{},[41,38293,38295],{"href":38294},"#none","None",[31,38297,38298],{},[41,38299,38301],{"href":38300},"#basic-authentication","Basic Authentication",[31,38303,38304],{},[41,38305,38084],{"href":38306},"#flowfuse-user-authentication",[31,38308,38309],{},[41,38310,4799],{"href":38311},"#palette",[31,38313,38314],{},[41,38315,38317],{"href":38316},"#alerts","Alerts",[23,38319,38235],{"id":38320},"general",[14,38322,38323],{},"This section includes several actions you can take on the instance:",[104,38325,34732],{"id":38326},"change-stack",[14,38328,38329],{},"The Stack determines the version of Node-RED being used. If a new stack\nis available, you can use this option to update your stack.",[104,38331,38248],{"id":38332},"copy-instance",[14,38334,38335],{},"This allows you to create a copy of the instance in your team.",[104,38337,38254],{"id":38338},"import-instance",[14,38340,38341],{},"This allows you to take existing Node-RED flow and credential files and import them\ninto your instance.",[104,38343,38260],{"id":38344},"suspend-instance",[14,38346,38347],{},"This stops the instance entirely.",[104,38349,38266],{"id":38350},"delete-instance",[14,38352,38353],{},"If you're sure you don't want the instance anymore, this allows you to delete\nit. You cannot undo deleting an instance. Devices assigned to the instance will be\nunassigned from the instance and available to be reassigned to a new instance.",[23,38355,36316],{"id":38356},"environment",[14,38358,38359,38360,273],{},"This allows you to manage the environment variables. More information\non working with environment variables is available ",[41,38361,785],{"href":15823},[23,38363,975],{"id":38364},"high-availability",[14,38366,38367,38368,273],{},"This allows you to manage the HA settings of the instance. High Availability\nis a Preview Feature. More information is available ",[41,38369,785],{"href":981},[23,38371,4796],{"id":38372},"editor",[14,38374,38375],{},"This covers many options to customize the Node-RED editor. This includes:",[28,38377,38378,38381,38384,38387,38390,38393,38396],{},[31,38379,38380],{},"Disabling the editor entirely",[31,38382,38383],{},"Modifying the paths the editor and dashboard are served on",[31,38385,38386],{},"Choosing which code editor to use in your Node-RED nodes",[31,38388,38389],{},"Setting a custom title for the editor",[31,38391,38392],{},"Choosing a light or dark theme for the editor",[31,38394,38395],{},"Controlling the runtime timezone",[31,38397,38398],{},"Controlling the use of node modules in function nodes",[23,38400,38287],{"id":38401},"security",[14,38403,38404],{},"This allows you to modify the security settings of the runtime. In particular\nthis covers the security applied to any HTTP routes served by the runtime.",[104,38406,38295],{"id":18147},[14,38408,38409],{},"The default option is not to apply any security - so any HTTP In nodes, or Node-RED\nDashboard can be accessed by anyone.",[104,38411,38301],{"id":38412},"basic-authentication",[14,38414,38415],{},"You can optionally enable Basic Authentication, with a single hardcoded username\nand password.",[104,38417,38084],{"id":38418},"flowfuse-user-authentication",[14,38420,38421],{},"Alternatively, with a licensed instance of FlowFuse, you can require anyone accessing\nthose routes to be logged into FlowFuse. The hosted pages or API endpoints are only\navailable for FlowFuse users who have access to the team on FlowFuse and the cloud\ninstance.",[14,38423,38424],{},"If using FlowFuse user Authentication you can also generate HTTP Bearer tokens that\ncan be used to access APIs hosted in Instances with HTTP-in\u002FHTTP-response nodes.",[14,38426,38427,38428,273],{},"More information on using HTTP Access Tokens can be found ",[41,38429,785],{"href":927},[23,38431,4799],{"id":38432},"palette",[14,38434,38435],{},"This allows you to manage what extra nodes are installed inside Node-RED, as well\nas any restrictions you want to apply to the Palette Manager within Node-RED.",[14,38437,38438,38439,38441,38442,38444,38445],{},"It includes the option to add extra Node-RED Catalogue URLs and a ",[18,38440,12096],{}," file\nthat will be deployed to the instance. Details of the ",[18,38443,12096],{}," format can be found\n",[41,38446,785],{"href":38447,"rel":38448},"https:\u002F\u002Fdocs.npmjs.com\u002Fcli\u002Fv9\u002Fconfiguring-npm\u002Fnpmrc",[831],[23,38450,38317],{"id":38451},"alerts",[14,38453,38454],{},"Alerts are a feature designed to provide email notifications based on specific Auditlog events. This functionality ensures prompt awareness and response to critical events.",[14,38456,38457],{},"Users can configure alerts for the following Auditlog events:",[28,38459,38460,38463],{},[31,38461,38462],{},"Node-RED has crashed",[31,38464,38465],{},"Node-RED has been placed in Safe Mode",[14,38467,38468],{},"When configuring alerts, you can choose the recipients of these notifications:",[28,38470,38471,38474,38477],{},[31,38472,38473],{},"Team Owners",[31,38475,38476],{},"Team Members",[31,38478,38479],{},"Both Owners and Members",{"title":55,"searchDepth":77,"depth":77,"links":38481},[38482,38489,38490,38491,38492,38497,38498],{"id":38320,"depth":77,"text":38235,"children":38483},[38484,38485,38486,38487,38488],{"id":38326,"depth":88,"text":34732},{"id":38332,"depth":88,"text":38248},{"id":38338,"depth":88,"text":38254},{"id":38344,"depth":88,"text":38260},{"id":38350,"depth":88,"text":38266},{"id":38356,"depth":77,"text":36316},{"id":38364,"depth":77,"text":975},{"id":38372,"depth":77,"text":4796},{"id":38401,"depth":77,"text":38287,"children":38493},[38494,38495,38496],{"id":18147,"depth":88,"text":38295},{"id":38412,"depth":88,"text":38301},{"id":38418,"depth":88,"text":38084},{"id":38432,"depth":77,"text":4799},{"id":38451,"depth":77,"text":38317},{},"user\u002Finstance-settings.md",{"title":36086,"description":38199},"docs\u002Fuser\u002Finstance-settings","Y9Cvdwo_4Mx-CZvAKNbHCr7if7cbHUQHXl75Zf8TMk4",{"id":38505,"title":38506,"body":38507,"description":38514,"extension":329,"layout":330,"meta":38643,"navGroup":330,"navOrder":249,"navTitle":38644,"navigation":187,"originalPath":38645,"path":38646,"redirect":330,"seo":38647,"stem":38648,"updated":337,"version":338,"__hash__":38649},"docs\u002Fdocs\u002Fuser\u002Finstance-states.md","FlowFuse Node-RED Instance States",{"type":7,"value":38508,"toc":38639},[38509,38512,38515,38519,38580,38584],[10,38510,38506],{"id":38511},"flowfuse-node-red-instance-states",[14,38513,38514],{},"The following list describes the possible states a hosted or remote Node-RED instance can find itself in.",[23,38516,38518],{"id":38517},"stable-states","Stable States",[28,38520,38521,38526,38532,38538,38544,38550,38556,38562,38568,38574],{},[31,38522,38523,38525],{},[364,38524,17036],{},": Instance is fully operational. Flows are deployed and executing normally.",[31,38527,38528,38531],{},[364,38529,38530],{},"suspended",": Instance resources are freed and flows are paused. Can be resumed without redeploying.",[31,38533,38534,38537],{},[364,38535,38536],{},"stopped",": Instance is not executing any flows and is intentionally shut down.",[31,38539,38540,38543],{},[364,38541,38542],{},"error",": Instance cannot operate normally due to a critical configuration or runtime issue. Requires user intervention.",[31,38545,38546,38549],{},[364,38547,38548],{},"crashed",": Instance terminated unexpectedly due to repeated runtime errors or failures.",[31,38551,38552,38555],{},[364,38553,38554],{},"rollback",": A previous working version of the instance has been restored due to deployment failure or manual revert.",[31,38557,38558,38561],{},[364,38559,38560],{},"warning",": Instance is running but has non-critical issues (e.g., node failures, resource limits close to threshold).",[31,38563,38564,38567],{},[364,38565,38566],{},"safe",": Running in safe mode after multiple crashes. Editor works, flows are not started until a deploy action is triggered.",[31,38569,38570,38573],{},[364,38571,38572],{},"protected",": Editor is disabled. Deployment can only occur via pipeline or controlled automation.",[31,38575,38576,38579],{},[364,38577,38578],{},"connected",": Instance has established a successful connection to its runtime environment and is reachable.",[23,38581,38583],{"id":38582},"transitional-states","Transitional States",[28,38585,38586,38592,38598,38603,38609,38615,38621,38627,38633],{},[31,38587,38588,38591],{},[364,38589,38590],{},"loading",": Instance UI or resources are being prepared (initial startup or page load).",[31,38593,38594,38597],{},[364,38595,38596],{},"installing",": Required packages, dependencies, or container layers are being installed.",[31,38599,38600,38602],{},[364,38601,10249],{},": Flows and runtime services are initializing.",[31,38604,38605,38608],{},[364,38606,38607],{},"stopping",": Flows and runtime services are shutting down gracefully.",[31,38610,38611,38614],{},[364,38612,38613],{},"restarting",": Instance is stopping and then immediately starting again, typically after a deployment or configuration update.",[31,38616,38617,38620],{},[364,38618,38619],{},"suspending",": Instance flows are being paused and resources deallocated before entering suspended state.",[31,38622,38623,38626],{},[364,38624,38625],{},"importing",": An external project or configuration is being applied to the instance.",[31,38628,38629,38632],{},[364,38630,38631],{},"pushing",": Changes are being uploaded to the runtime or container registry.",[31,38634,38635,38638],{},[364,38636,38637],{},"pulling",": Artifacts or project content are being fetched from a registry or pipeline source.",{"title":55,"searchDepth":77,"depth":77,"links":38640},[38641,38642],{"id":38517,"depth":77,"text":38518},{"id":38582,"depth":77,"text":38583},{},"Instance States","user\u002Finstance-states.md","\u002Fdocs\u002Fuser\u002Finstance-states",{"title":38506,"description":38514},"docs\u002Fuser\u002Finstance-states","Nf3_NAFmowoF8VbNmnJ_bApVmebEy0pkbECrIxIF-xU",{"id":38651,"title":38652,"body":38653,"description":39157,"extension":329,"layout":330,"meta":39158,"navGroup":330,"navOrder":62,"navTitle":17956,"navigation":187,"originalPath":39159,"path":38186,"redirect":330,"seo":39160,"stem":39161,"updated":337,"version":338,"__hash__":39162},"docs\u002Fdocs\u002Fuser\u002Fintroduction.md","Getting Started with FlowFuse",{"type":7,"value":38654,"toc":39140},[38655,38658,38671,38675,38679,38682,38688,38693,38696,38702,38705,38710,38714,38717,38722,38747,38753,38758,38761,38767,38771,38774,38779,38793,38798,38804,38809,38812,38826,38832,38835,38841,38846,38849,38852,38860,38865,38870,38874,38877,38880,38886,38894,38898,38912,38916,38919,38959,38965,38969,38972,38979,38983,38999,39003,39006,39025,39029,39103,39107,39115,39119,39128,39132],[10,38656,38652],{"id":38657},"getting-started-with-flowfuse",[14,38659,38660,38661,1706,38665,38670],{},"This guide will help you learn how to use the FlowFuse platform to quickly create new Node-RED applications after a successful ",[41,38662,38664],{"href":38663},"\u002Fdocs\u002Finstall\u002Fintroduction.md","installation",[41,38666,38669],{"href":38667,"rel":38668},"https:\u002F\u002Fapp.flowforge.com\u002Faccount\u002Fcreate",[831],"sign-up"," for FlowFuse Cloud.",[23,38672,38674],{"id":38673},"creating-a-node-red-instance","Creating a Node-RED Instance",[104,38676,38678],{"id":38677},"your-first-hosted-instance","Your First Hosted Instance",[14,38680,38681],{},"Your first Node-RED instance should be automatically created upon your initial login to FlowFuse.",[14,38683,38684],{},[638,38685],{"alt":38686,"src":38687,"dataZoomable":55},"Instance created initial login","\u002Fdocs\u002Fuser\u002Fimages\u002Fgetting-started\u002Fff-home-initial-login.png",[14,38689,38690],{},[364,38691,38692],{},"Accessing the Node-RED Editor:",[14,38694,38695],{},"To access the Node-RED Editor, simply click on the option shown in the image below marked with a red box:",[14,38697,38698],{},[638,38699],{"alt":38700,"src":38701,"dataZoomable":55},"Open Editor Shortcut","\u002Fdocs\u002Fuser\u002Fimages\u002Fgetting-started\u002Fopen-editor-shortcut.png",[14,38703,38704],{},"Alternatively, you can click on that instance and then you will find the \"Open Editor\" button at the top right:",[14,38706,38707],{},[638,38708],{"alt":36513,"src":38709,"dataZoomable":55},"\u002Fdocs\u002Fuser\u002Fimages\u002Fgetting-started\u002FOpen-Editor.png",[104,38711,38713],{"id":38712},"creating-additional-instances","Creating Additional Instances",[14,38715,38716],{},"For utilizing various other FlowFuse features (e.g., DevOps Pipelines), it's highly beneficial to create a second Node-RED instance. A second Node-RED instance is included in both our Starter Tier and the Trial Phase of FlowFuse Cloud.",[14,38718,38719],{},[364,38720,38721],{},"From the Home Page:",[398,38723,38724,38727,38730,38733,38736,38739],{},[31,38725,38726],{},"Click on the \"Add Instance\" button shown in the image below",[31,38728,38729],{},"Enter the name you want for your instance",[31,38731,38732],{},"Select your application in which you want to create it",[31,38734,38735],{},"Select the instance type",[31,38737,38738],{},"Select the Node-RED version from the dropdown",[31,38740,38741,38742,38746],{},"Click \"Next\" and ",[41,38743,38745],{"href":38744},"#selecting-a-blueprint","select the blueprint"," you want to use",[14,38748,38749],{},[638,38750],{"alt":38751,"src":38752,"dataZoomable":55},"Add Instance","\u002Fdocs\u002Fuser\u002Fimages\u002Fgetting-started\u002Fff-home-after-initial-login-add-instance.png",[14,38754,38755],{},[364,38756,38757],{},"From the Applications Page:",[14,38759,38760],{},"Alternatively, you can go to your applications from the left sidebar by clicking \"Applications,\" select your application—in our example, \"Demo's Application\"—and click \"Add Instance,\" then follow the same process.",[14,38762,38763],{},[41,38764,38766],{"href":38765},"#working-with-instances","Learn more about Instances",[104,38768,38770],{"id":38769},"your-first-remote-instance","Your First Remote Instance",[14,38772,38773],{},"A remote instance allows you to run Node-RED on your own hardware while managing it through FlowFuse.",[14,38775,38776],{},[364,38777,38778],{},"Adding a Remote Instance:",[398,38780,38781,38784,38787,38790],{},[31,38782,38783],{},"Click on \"Remote Instances\" from the left sidebar",[31,38785,38786],{},"Click \"Add Remote Instance\"",[31,38788,38789],{},"Enter a name for your instance and select the device type",[31,38791,38792],{},"Select your application and click \"Add\"",[14,38794,38795],{},[638,38796],{"alt":15920,"src":38797,"dataZoomable":55},"\u002Fdocs\u002Fuser\u002Fimages\u002Fgetting-started\u002Fadd-remote-instance.png",[14,38799,38800],{},[638,38801],{"alt":38802,"src":38803,"dataZoomable":55},"Add Remote Instance Form","\u002Fdocs\u002Fuser\u002Fimages\u002Fgetting-started\u002Finstance-add-form.png",[14,38805,38806],{},[364,38807,38808],{},"Installing the Device Agent:",[14,38810,38811],{},"FlowFuse will show you a device configuration window with installation options:",[28,38813,38814,38820],{},[31,38815,38816,38819],{},[364,38817,38818],{},"One-Line Install"," (Recommended): Automatically installs Node.js (if needed), the device agent, and registers your device",[31,38821,38822,38825],{},[364,38823,38824],{},"NPM Installation",": Manual installation instructions for Windows, Mac, or Linux",[14,38827,38828],{},[638,38829],{"alt":38830,"src":38831,"dataZoomable":55},"Device Configuration Window","\u002Fdocs\u002Fuser\u002Fimages\u002Fgetting-started\u002Fdevice-configuration-window-2.gif",[14,38833,38834],{},"Follow the steps in the window to connect your device—it takes less than a minute.",[14,38836,38837],{},[41,38838,38840],{"href":38839},"\u002Fdocs\u002Fdevice-agent\u002Fintroduction.md","Learn more about Device Agent",[14,38842,38843],{},[364,38844,38845],{},"Accessing Your Remote Instance:",[14,38847,38848],{},"Once registered, you can manage your remote Node-RED instance through FlowFuse:",[14,38850,38851],{},"To start building flows:",[398,38853,38854,38857],{},[31,38855,38856],{},"Enable \"Developer Mode\" from the top right",[31,38858,38859],{},"Click \"Open Editor\"",[14,38861,38862],{},[638,38863],{"alt":15697,"src":38864,"dataZoomable":55},"\u002Fdocs\u002Fuser\u002Fimages\u002Fgetting-started\u002Fdeveloper-mode.png",[14,38866,38867],{},[638,38868],{"alt":36513,"src":38869,"dataZoomable":55},"\u002Fdocs\u002Fuser\u002Fimages\u002Fgetting-started\u002Fopen-editor-remote-instance.png",[104,38871,38873],{"id":38872},"selecting-a-blueprint","Selecting a Blueprint",[14,38875,38876],{},"When creating a new Node-RED instance, you have the option to choose a blueprint tailored for specific use cases. For example, our \"ANDON Operator Terminal\" blueprint can be selected, and it will automatically configure the Node-RED instance, install necessary nodes, sparing you the need to start from scratch. Click \"Create Instance\" from the top-right to complete the process.",[14,38878,38879],{},"While these templates are powerful out-of-the-box, they're also fully customizable, allowing you to tweak them to suit your unique requirements. Ultimately, blueprints speed up the learning curve for new users and expedite the solution-building process for experienced ones.",[14,38881,38882],{},[638,38883],{"alt":38884,"src":38885,"dataZoomable":55},"Blueprint selection","\u002Fdocs\u002Fuser\u002Fimages\u002Fgetting-started\u002Fblueprint-selection.png",[14,38887,38888,179,38891],{},[364,38889,38890],{},"NOTE",[1160,38892,38893],{},"Some blueprints may only be available on certain tiers",[23,38895,38897],{"id":38896},"creating-your-first-flow","Creating Your First Flow",[14,38899,38900,38901,38906,38907,273],{},"FlowFuse published an ",[41,38902,38905],{"href":38903,"rel":38904},"https:\u002F\u002Fflowfuse.com\u002Febooks\u002Fbeginner-guide-to-a-professional-nodered\u002F",[831],"eBook on Node-RED development",", which is a great resources when you're new\nto Node-RED. You can also read our ",[41,38908,38911],{"href":38909,"rel":38910},"https:\u002F\u002Fflowfuse.com\u002Fblog\u002F2023\u002F01\u002Fgetting-started-with-node-red\u002F",[831],"blog post on creating your first flow",[23,38913,38915],{"id":38914},"creating-your-first-devops-pipeline","Creating Your First DevOps Pipeline",[14,38917,38918],{},"DevOps Pipelines enable you to link multiple Node-RED instances together in a deployment pipeline.",[398,38920,38921,38935,38941,38947],{},[31,38922,38923,38926,38927,273,38930,38932],{},[364,38924,38925],{},"Add a Pipeline",": Select your application and click ",[18,38928,38929],{},"Add Pipeline",[662,38931],{},[638,38933],{"alt":38929,"src":38934},"\u002Fdocs\u002Fuser\u002Fimages\u002Fgetting-started\u002FAdd-Pipeline.png",[31,38936,38937,38940],{},[364,38938,38939],{},"Name Your Pipeline",": Enter a suitable name.",[31,38942,38943,38946],{},[364,38944,38945],{},"Add Stages",": You can now add stages to your pipeline. In our example, we add a Development Stage and a Production Stage.",[31,38948,38949,38952,38953,38955],{},[364,38950,38951],{},"Execute the Pipeline",": It is now easy to execute the pipeline with one click, promoting your recently created flow to your Production Node-RED instance.",[662,38954],{},[638,38956],{"alt":38957,"src":38958},"Execute Pipeline","\u002Fdocs\u002Fuser\u002Fimages\u002Fgetting-started\u002Fdevops-pipeline-w-stage.png",[14,38960,38961],{},[41,38962,38964],{"href":38963},"\u002Fdocs\u002Fuser\u002Fdevops-pipelines\u002F","Learn more about DevOps Pipelines",[23,38966,38968],{"id":38967},"working-with-devices","Working with Devices",[14,38970,38971],{},"FlowFuse supports managing Node-RED on your own hardware.",[28,38973,38974],{},[31,38975,38976],{},[41,38977,38978],{"href":38839},"Getting started with Devices",[23,38980,38982],{"id":38981},"working-with-teams","Working with Teams",[28,38984,38985,38992],{},[31,38986,38987,38991],{},[41,38988,38990],{"href":38989},"\u002Fdocs\u002Fuser\u002Fteam\u002FREADME.md","Team management"," - How to add and remove users from a team.",[31,38993,38994,38998],{},[41,38995,38997],{"href":38996},"\u002Fdocs\u002Fuser\u002Fteam\u002FREADME.md#role-based-access-control","Role based access control"," - Which privileges are granted to different roles.",[23,39000,39002],{"id":39001},"working-with-files-and-context","Working with Files and Context",[14,39004,39005],{},"FlowFuse supports reading and writing persistent files and persistent context.",[28,39007,39008,39013,39019],{},[31,39009,39010],{},[41,39011,39012],{"href":20072},"Working with Files",[31,39014,39015],{},[41,39016,39018],{"href":39017},"\u002Fdocs\u002Fuser\u002Fstatic-asset-service.md","Working with the Static Asset Service",[31,39020,39021],{},[41,39022,39024],{"href":39023},"\u002Fdocs\u002Fuser\u002Fpersistent-context.md","Working with Context",[23,39026,39028],{"id":39027},"working-with-instances","Working with Instances",[28,39030,39031,39037,39043,39048,39054,39060,39067,39074,39080,39086,39092,39098],{},[31,39032,39033,39036],{},[41,39034,38644],{"href":39035},"\u002Fdocs\u002Fuser\u002Finstance-states.md"," - List of states an instance can be in.",[31,39038,39039,39042],{},[41,39040,11991],{"href":39041},"\u002Fdocs\u002Fuser\u002Fsnapshots.md"," - Create point-in-time backups of your Node-RED instances.",[31,39044,39045,39047],{},[41,39046,11824],{"href":11823}," - How to manage Environment Variables in your Node-RED instances.",[31,39049,39050,39053],{},[41,39051,39052],{"href":4236},"Change Project Stack"," - How to change an instance stack, for example to upgrade Node-RED.",[31,39055,39056,39059],{},[41,39057,15829],{"href":39058},"\u002Fdocs\u002Fuser\u002Flogs.md"," - The Logs available in the FlowFuse application.",[31,39061,39062,39066],{},[41,39063,39065],{"href":39064},"\u002Fdocs\u002Fuser\u002Fprojectnodes.md","Project Link Nodes"," - Custom nodes for sending messages between Node-RED instances and devices.",[31,39068,39069,39073],{},[41,39070,39072],{"href":39071},"\u002Fdocs\u002Fuser\u002Fmqtt-nodes.md","MQTT Nodes"," - Custom nodes for zero config MQTT integration with the team broker.",[31,39075,39076,39079],{},[41,39077,36086],{"href":39078},"\u002Fdocs\u002Fuser\u002Finstance-settings.md"," - Settings available for Node-RED instances.",[31,39081,39082,39085],{},[41,39083,4162],{"href":39084},"\u002Fdocs\u002Fuser\u002Fshared-library.md"," - Share flows easily between different Node-RED instances in your team.",[31,39087,39088,39091],{},[41,39089,39090],{"href":33572},"Node-RED Tools Plugin"," - A plugin for Node-RED that lets you work with your flows outside of FlowFuse.",[31,39093,39094,39097],{},[41,39095,37886],{"href":39096},"\u002Fdocs\u002Fuser\u002Fhigh-availability.md"," - Run multiple copies of your instance for scaling and availability.",[31,39099,39100,39102],{},[41,39101,37125],{"href":1048}," - A Node-RED plugin powered by AI, trained on FlowFuse content, that helps you code faster, build flows, and debug with context-aware guidance.",[23,39104,39106],{"id":39105},"working-with-mqtt","Working with MQTT",[28,39108,39109],{},[31,39110,39111,39114],{},[41,39112,1008],{"href":39113},"\u002Fdocs\u002Fuser\u002Fteambroker.md"," - Working with the FlowFuse bundled MQTT Broker",[23,39116,39118],{"id":39117},"working-with-custom-nodes","Working with Custom Nodes",[28,39120,39121],{},[31,39122,39123,39127],{},[41,39124,39126],{"href":39125},"\u002Fdocs\u002Fuser\u002Fcustom-npm-packages.md","Custom Nodes"," - Publishing Custom Node-RED Nodes",[23,39129,39131],{"id":39130},"working-with-ff-tables","Working with FF Tables",[28,39133,39134],{},[31,39135,39136,39139],{},[41,39137,19845],{"href":39138},"\u002Fdocs\u002Fuser\u002Fff-tables.md"," - Databases",{"title":55,"searchDepth":77,"depth":77,"links":39141},[39142,39148,39149,39150,39151,39152,39153,39154,39155,39156],{"id":38673,"depth":77,"text":38674,"children":39143},[39144,39145,39146,39147],{"id":38677,"depth":88,"text":38678},{"id":38712,"depth":88,"text":38713},{"id":38769,"depth":88,"text":38770},{"id":38872,"depth":88,"text":38873},{"id":38896,"depth":77,"text":38897},{"id":38914,"depth":77,"text":38915},{"id":38967,"depth":77,"text":38968},{"id":38981,"depth":77,"text":38982},{"id":39001,"depth":77,"text":39002},{"id":39027,"depth":77,"text":39028},{"id":39105,"depth":77,"text":39106},{"id":39117,"depth":77,"text":39118},{"id":39130,"depth":77,"text":39131},"This guide will help you learn how to use the FlowFuse platform to quickly create new Node-RED applications after a successful installation or sign-up for FlowFuse Cloud.",{},"user\u002Fintroduction.md",{"title":38652,"description":39157},"docs\u002Fuser\u002Fintroduction","beLfWSmniWX7Pc_ftumzvpsaoHGVBI_-CoXgMFI1_aw",{"id":39164,"title":15829,"body":39165,"description":39171,"extension":329,"layout":330,"meta":39276,"navGroup":330,"navOrder":330,"navTitle":39277,"navigation":187,"originalPath":39278,"path":15828,"redirect":330,"seo":39279,"stem":39280,"updated":337,"version":338,"__hash__":39281},"docs\u002Fdocs\u002Fuser\u002Flogs.md",{"type":7,"value":39166,"toc":39269},[39167,39169,39172,39176,39179,39182,39189,39192,39201,39205,39208,39211,39228,39235,39238,39241,39244,39248,39251,39253,39261,39266],[10,39168,15829],{"id":26067},[14,39170,39171],{},"FlowFuse presents log information in several different places depending on what you are interested in.",[23,39173,39175],{"id":39174},"node-red-logs","Node-RED Logs",[14,39177,39178],{},"The Node-RED logs are available for all instances running within the platform.",[14,39180,39181],{},"They will contain information such as nodes being added and errors relating to your flows.",[14,39183,39184,39185,39188],{},"The log information is kept back to the last time the instance container was restarted, you can view older information on the ",[18,39186,39187],{},"Load earlier..."," link at the top of the log.",[638,39190],{"src":39191,"width":15952},"\u002Fdocs\u002Fuser\u002Fimages\u002Fprojectlog.png",[14,39193,39194,39195,39198,39199,19658],{},"Node-RED logs can also be output from the Containers\u002FPods that run Instances on Docker or Kubernetes. This is enabled by the ",[18,39196,39197],{},"forge.logPassthrough"," option. More details can be found in the ",[41,39200,14953],{"href":20264},[23,39202,39204],{"id":39203},"audit-log","Audit Log",[14,39206,39207],{},"The Audit Log tab on the application and instance views shows key events that have happened.",[14,39209,39210],{},"The events include:",[28,39212,39213,39216,39219,39222,39225],{},[31,39214,39215],{},"User logging into the editor",[31,39217,39218],{},"Flows being updated",[31,39220,39221],{},"Nodes installed",[31,39223,39224],{},"Snapshots being created",[31,39226,39227],{},"Resource utilization warnings",[14,39229,39230,39231,39234],{},"This log contains all events since the instance was created. You can view older data using the ",[18,39232,39233],{},"Load More..."," link at the bottom of the log.",[638,39236],{"src":39237,"width":15952},"\u002Fdocs\u002Fuser\u002Fimages\u002Fprojectactivity.png",[104,39239,39227],{"id":39240},"resource-utilization-warnings",[14,39242,39243],{},"If the CPU or memory usage exceeds 75% for more than 5 minutes, a warning will be displayed in the Audit Log, indicating that measures such as upgrading the instance are recommended.",[23,39245,39247],{"id":39246},"team-audit-log","Team Audit Log",[14,39249,39250],{},"From the Team page the Audit Log shows events relating to the management of the team.",[14,39252,31917],{},[28,39254,39255,39258],{},[31,39256,39257],{},"Applications\u002FInstances being created or deleted",[31,39259,39260],{},"Users being added\u002Fremoved from the team",[14,39262,39263,39264,39234],{},"This log contains all events since the team was created. Tou can view older data using the ",[18,39265,39233],{},[638,39267],{"src":39268,"width":15952},"\u002Fdocs\u002Fuser\u002Fimages\u002Fteamauditlog.png",{"title":55,"searchDepth":77,"depth":77,"links":39270},[39271,39272,39275],{"id":39174,"depth":77,"text":39175},{"id":39203,"depth":77,"text":39204,"children":39273},[39274],{"id":39240,"depth":88,"text":39227},{"id":39246,"depth":77,"text":39247},{},"Logging","user\u002Flogs.md",{"title":15829,"description":39171},"docs\u002Fuser\u002Flogs","t5aVZ-rAJf44GBg2q3pgjlIFocXcAev06nWWUzvKcNk",{"id":39283,"title":18401,"body":39284,"description":39357,"extension":329,"layout":330,"meta":39358,"navGroup":330,"navOrder":330,"navTitle":18401,"navigation":187,"originalPath":39359,"path":39360,"redirect":330,"seo":39361,"stem":39362,"updated":337,"version":338,"__hash__":39363},"docs\u002Fdocs\u002Fuser\u002Fmqtt-nodes.md",{"type":7,"value":39285,"toc":39353},[39286,39289,39295,39298,39302,39305,39319,39328,39339,39342,39346],[10,39287,18401],{"id":39288},"flowfuse-mqtt-nodes",[14,39290,39291,39292,273],{},"Node-RED instances running within FlowFuse include a set of nodes that make it\nsimple to securely connect and send or receive messages between MQTT clients\nin your team or externally connected clients via the ",[41,39293,1008],{"href":39294},"\u002Fdocs\u002Fuser\u002Fteambroker\u002F#getting-started-with-team-broker",[14,39296,39297],{},"The nodes are very similar to the Node-RED MQTT nodes, but without the need\nto configure any settings for your broker, making integration seamless.",[104,39299,39301],{"id":39300},"nodes","Nodes",[14,39303,39304],{},"There are two nodes in this collection:",[28,39306,39307,39313],{},[31,39308,39309,39312],{},[18,39310,39311],{},"MQTT In"," - subscribes to fixed or dynamic topics",[31,39314,39315,39318],{},[18,39316,39317],{},"MQTT Out"," - publishes messages to fixed or dynamic topics",[14,39320,372,39321,39323,39324,39327],{},[18,39322,39311],{}," node receives messages from the topic defined in the node's\nconfiguration or the ",[18,39325,39326],{},"msg.topic"," property if it is set.",[14,39329,372,39330,39332,39333,39336,39337,39327],{},[18,39331,39317],{}," node sends the ",[18,39334,39335],{},"msg.payload"," value to the topic defined in the node's\nconfiguration or the ",[18,39338,39326],{},[14,39340,39341],{},"See the built-in help on the Node-RED sidebar for more information about using these nodes.",[104,39343,39345],{"id":39344},"github","GitHub",[14,39347,39348,39349,273],{},"The nodes are published under an Apache-2.0 license and available on ",[41,39350,39345],{"href":39351,"rel":39352},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnr-mqtt-nodes",[831],{"title":55,"searchDepth":77,"depth":77,"links":39354},[39355,39356],{"id":39300,"depth":88,"text":39301},{"id":39344,"depth":88,"text":39345},"Node-RED instances running within FlowFuse include a set of nodes that make it\nsimple to securely connect and send or receive messages between MQTT clients\nin your team or externally connected clients via the Team Broker.",{},"user\u002Fmqtt-nodes.md","\u002Fdocs\u002Fuser\u002Fmqtt-nodes",{"title":18401,"description":39357},"docs\u002Fuser\u002Fmqtt-nodes","EE8Vz2JvuzFDS1jhCe3vrFRCtlMBZhAVezupYX9INPk",{"id":39365,"title":23859,"body":39366,"description":39580,"extension":329,"layout":330,"meta":39581,"navGroup":330,"navOrder":330,"navTitle":330,"navigation":187,"originalPath":39582,"path":37933,"redirect":330,"seo":39583,"stem":39584,"updated":337,"version":338,"__hash__":39585},"docs\u002Fdocs\u002Fuser\u002Fpersistent-context.md",{"type":7,"value":39367,"toc":39575},[39368,39371,39378,39380,39383,39397,39405,39409,39412,39416,39427,39446,39452,39456,39468,39513,39516,39523,39529,39531,39561,39564,39572],[10,39369,23859],{"id":39370},"flowfuse-persistent-context",[14,39372,39373,39374,39377],{},"Some Node-RED flows require the ability to persist context values between restarts and FlowFuse stack updates. By default, context data in Node-RED is ephemeral, meaning it does not survive restarts or stack updates. With FlowFuse Starter, Team and Enterprise tiers, however, you can enable ",[364,39375,39376],{},"persistent context storage",", ensuring that your context values persist across restarts, upgrades, and more.",[23,39379,33688],{"id":33687},[14,39381,39382],{},"In Node-RED with FlowFuse, you now have two context store options:",[398,39384,39385,39391],{},[31,39386,39387,39390],{},[364,39388,39389],{},"Memory Context",": This is the default ephemeral context. Values stored in memory are not persistent and will be lost when Node-RED restarts or the Node-RED stack is updated.",[31,39392,39393,39396],{},[364,39394,39395],{},"Persistent Context",": This allows context values to persist even when Node-RED restarts, updates.",[14,39398,39399,39400,273],{},"The amount of persistent storage available to you depends on the FlowFuse plan you're subscribed to. FlowFuse offers different storage sizes for each plan, allowing you to select the appropriate level of storage for your needs. For detailed information on storage options, please refer to the ",[41,39401,39404],{"href":39402,"rel":39403},"https:\u002F\u002Fflowfuse.com\u002Fpricing\u002F",[831],"pricing page",[104,39406,39408],{"id":39407},"how-to-use-flowfuse-persistent-context","How to Use FlowFuse Persistent Context",[14,39410,39411],{},"Using persistent context in Node-RED is similar to using memory context, with the key difference being that you specify the storage type for your context data. Here’s how to use persistent context it:",[768,39413,39415],{"id":39414},"using-persistent-context-in-nodes","Using Persistent Context in Nodes",[14,39417,39418,39419,39422,39423,39426],{},"When configuring persistent context in different nodes (e.g., Change, Inject, or Switch nodes), you can select the type of context storage to use. By default, the context type is set to ",[364,39420,39421],{},"Memory",", but you can change it to ",[364,39424,39425],{},"Persistent"," to store values across restarts.",[28,39428,39429],{},[31,39430,39431,3939,39434,39436,39437,39439,39440,39443,39444,273],{},[364,39432,39433],{},"Change Node, Inject Node, Switch Node",[662,39435],{},"\nWhen you configure these nodes to store or access context data, you’ll notice a storage option at the right corner. By default, it will be set to ",[364,39438,39421],{},". To make the context ",[364,39441,39442],{},"persistent",", simply switch the selection to ",[364,39445,39425],{},[14,39447,39448],{},[638,39449],{"alt":39450,"src":39451},"Persistent Store Option in Change Node","\u002Fdocs\u002Fuser\u002Fimages\u002Fvariables-in-node-red-change-node-persistent-store-option.gif",[768,39453,39455],{"id":39454},"using-persistent-context-in-function-nodes","Using Persistent Context in Function Nodes",[14,39457,39458,39459,39462,39463,302,39465,39467],{},"In ",[364,39460,39461],{},"Function nodes",", you interact with context data using the ",[18,39464,17399],{},[18,39466,5144],{}," methods. These methods allow you to specify where the context data should be stored or accessed from.",[28,39469,39470],{},[31,39471,39472,39475,39476,39478,39479,39481,39482],{},[364,39473,39474],{},"Setting Context",":\nTo set a persistent context value, use the ",[18,39477,17399],{}," method with three arguments. The third argument specifies the context store, which should be set to ",[364,39480,39442],{},". For example:",[50,39483,39487],{"className":39484,"code":39485,"language":39486,"meta":55,"style":55},"language-javascript shiki shiki-themes github-light github-dark","context.set('myKey', 'myValue', 'persistent');\n","javascript",[18,39488,39489],{"__ignoreMap":55},[59,39490,39491,39494,39496,39498,39501,39503,39506,39508,39511],{"class":61,"line":62},[59,39492,39493],{"class":178},"context.",[59,39495,17399],{"class":65},[59,39497,5013],{"class":178},[59,39499,39500],{"class":69},"'myKey'",[59,39502,3012],{"class":178},[59,39504,39505],{"class":69},"'myValue'",[59,39507,3012],{"class":178},[59,39509,39510],{"class":69},"'persistent'",[59,39512,5019],{"class":178},[14,39514,39515],{},"This argument is optional and defaults to memory. This means that if you leave it empty, it will use memory as the context store.",[28,39517,39518],{},[31,39519,39520,3939],{},[364,39521,39522],{},"Getting Context",[14,39524,39525,39526,39528],{},"When retrieving persistent context, use the ",[18,39527,5144],{}," method with two arguments. The second argument is optional and specifies the context store (either memory or persistent).",[14,39530,31733],{},[50,39532,39534],{"className":39484,"code":39533,"language":39486,"meta":55,"style":55},"var value = context.get('myKey', 'persistent');\n",[18,39535,39536],{"__ignoreMap":55},[59,39537,39538,39541,39544,39546,39549,39551,39553,39555,39557,39559],{"class":61,"line":62},[59,39539,39540],{"class":1372},"var",[59,39542,39543],{"class":178}," value ",[59,39545,1373],{"class":1372},[59,39547,39548],{"class":178}," context.",[59,39550,5144],{"class":65},[59,39552,5013],{"class":178},[59,39554,39500],{"class":69},[59,39556,3012],{"class":178},[59,39558,39510],{"class":69},[59,39560,5019],{"class":178},[14,39562,39563],{},"If you don't specify the store, it defaults to memory.",[14,39565,39566,39567,273],{},"For more detailed information, refer to the article ",[41,39568,39571],{"href":39569,"rel":39570},"https:\u002F\u002Fflowfuse.com\u002Fblog\u002F2024\u002F05\u002Funderstanding-node-flow-global-environment-variables-in-node-red\u002F",[831],"Understanding Node, Flow, Global, and Environment Variables in Node-RED",[316,39573,39574],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":55,"searchDepth":77,"depth":77,"links":39576},[39577],{"id":33687,"depth":77,"text":33688,"children":39578},[39579],{"id":39407,"depth":88,"text":39408},"Some Node-RED flows require the ability to persist context values between restarts and FlowFuse stack updates. By default, context data in Node-RED is ephemeral, meaning it does not survive restarts or stack updates. With FlowFuse Starter, Team and Enterprise tiers, however, you can enable persistent context storage, ensuring that your context values persist across restarts, upgrades, and more.",{},"user\u002Fpersistent-context.md",{"title":23859,"description":39580},"docs\u002Fuser\u002Fpersistent-context","1x3g5Q-kVP37pTzvhclFDvZ4Auk8tL4YYxEKDDWm8GY",{"id":39587,"title":18365,"body":39588,"description":39595,"extension":329,"layout":330,"meta":39668,"navGroup":330,"navOrder":330,"navTitle":18365,"navigation":187,"originalPath":39669,"path":796,"redirect":330,"seo":39670,"stem":39671,"updated":337,"version":338,"__hash__":39672},"docs\u002Fdocs\u002Fuser\u002Fprojectnodes.md",{"type":7,"value":39589,"toc":39664},[39590,39593,39596,39599,39602,39605,39607,39610,39630,39644,39647,39650,39653,39656,39658],[10,39591,18365],{"id":39592},"flowfuse-project-nodes",[14,39594,39595],{},"Node-RED instances running within FlowFuse include a set of nodes that make it\nvery quick and easy to securely send and receive messages between different\ninstances in a team.",[14,39597,39598],{},"The nodes act in a similar way to the Node-RED Link nodes, but by allowing the\nlinks to extended between different instances and devices, they open up a wide\nrange of possibilities.",[14,39600,39601],{},"For example, a single Node-RED instance may contain a set of utility flows that\nyou want to reuse in other instances. Rather than copy the flows around, the\nProject Nodes allow you to easily call those flows and get the result back.",[14,39603,39604],{},"The project nodes are only available in the Team and Enterprise tiers of FlowFuse.",[104,39606,39301],{"id":39300},[14,39608,39609],{},"There are three nodes in this collection:",[28,39611,39612,39618,39624],{},[31,39613,39614,39617],{},[18,39615,39616],{},"Project In"," - listens for messages being broadcast by other Node-RED instances, or for\nmessages being sent just to this instance",[31,39619,39620,39623],{},[18,39621,39622],{},"Project Out"," - sends messages to other Node-RED instances",[31,39625,39626,39629],{},[18,39627,39628],{},"Project Call"," - sends messages to other Node-RED instances and waits for a response",[14,39631,39632,39633,39636,39637,3601,39640,39643],{},"The nodes send the whole ",[18,39634,39635],{},"msg"," object. Due to the way the nodes\nencode messages, there are some data types that do not get sent. For example,\nthe ",[18,39638,39639],{},"msg.req",[18,39641,39642],{},"msg.res"," properties used by the core HTTP nodes will not be sent.\nInstead, they are temporarily removed from the message and re-attached when the\nmessage is received back.",[14,39645,39646],{},"Each node is configured with a topic on which it either sends or receives messages\non. This is similar in concept to MQTT topics - although the nodes do not currently\nsupport using MQTT wildcards in their topics.",[14,39648,39649],{},"The Project Out nodes can either broadcast messages on a topic to anyone listening,\nor they can send messages on a topic to a specific other instance.",[14,39651,39652],{},"The Project In nodes do the opposite - they can either listen for messages being\nbroadcast, or for messages sent directly to them.",[14,39654,39655],{},"The Project Call node can be used to send a message to another Project In node\nand then wait for a response, with a built-in timeout if it doesn't arrive.\nThe response is sent back using a Project Out node configured to respond to the call\nnode.",[104,39657,39345],{"id":39344},[14,39659,39348,39660,273],{},[41,39661,39345],{"href":39662,"rel":39663},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnr-project-nodes",[831],{"title":55,"searchDepth":77,"depth":77,"links":39665},[39666,39667],{"id":39300,"depth":88,"text":39301},{"id":39344,"depth":88,"text":39345},{},"user\u002Fprojectnodes.md",{"title":18365,"description":39595},"docs\u002Fuser\u002Fprojectnodes","CNaVvQqU_TiIu06DcGwXUpM5zjcNsqRWrlRGxLP-X5c",{"id":39674,"title":39675,"body":39676,"description":39683,"extension":329,"layout":330,"meta":40688,"navGroup":330,"navOrder":330,"navTitle":39675,"navigation":187,"originalPath":40689,"path":40690,"redirect":330,"seo":40691,"stem":40692,"updated":337,"version":338,"__hash__":40693},"docs\u002Fdocs\u002Fuser\u002Frole-based-access-control.md","Role-Based Access Control",{"type":7,"value":39677,"toc":40674},[39678,39681,39684,39688,39691,39705,39708,39711,39715,39723,39731,39739,39747,39751,39754,40432,40437,40445,40449,40454,40458,40461,40471,40477,40485,40491,40496,40499,40503,40506,40523,40529,40537,40542,40545,40548,40551,40553,40559,40565,40569,40572,40594,40598,40609,40622,40626,40629,40649,40655,40660,40666,40671],[10,39679,39675],{"id":39680},"role-based-access-control",[14,39682,39683],{},"Role-based access control (RBAC) determines what actions users can perform within FlowFuse. By assigning roles to team members, you control who can create, modify, view, or delete resources.",[23,39685,39687],{"id":39686},"rbac-levels","RBAC Levels",[14,39689,39690],{},"FlowFuse provides role-based access control at two levels:",[398,39692,39693,39699],{},[31,39694,39695,39698],{},[364,39696,39697],{},"Team-Level RBAC"," - Defines default permissions across all team resources",[31,39700,39701,39704],{},[364,39702,39703],{},"Application-Level RBAC"," - Overrides team-level permissions for specific applications",[23,39706,39697],{"id":39707},"team-level-rbac",[14,39709,39710],{},"Team-level roles establish baseline permissions for all resources within a team. Every team member is assigned one of four roles.",[104,39712,39714],{"id":39713},"roles","Roles",[14,39716,39717,39720,39722],{},[364,39718,39719],{},"Owner",[662,39721],{},"\nFull administrative control over the team, including managing settings, members, and all resources.",[14,39724,39725,39728,39730],{},[364,39726,39727],{},"Member",[662,39729],{},"\nCan develop and manage flows, create snapshots, and modify environment variables. Cannot manage team settings or create\u002Fdelete applications and instances.",[14,39732,39733,39736,39738],{},[364,39734,39735],{},"Viewer",[662,39737],{},"\nRead-only access to view flows, instance details, and snapshots. Cannot make any modifications.",[14,39740,39741,39744,39746],{},[364,39742,39743],{},"Dashboard Only",[662,39745],{},"\nRestricted access limited to viewing dashboards and HTTP endpoints only.",[104,39748,39750],{"id":39749},"permissions","Permissions",[14,39752,39753],{},"The table below shows which actions each role can perform.",[2289,39755,39756,39770],{},[2292,39757,39758],{},[2295,39759,39760,39762,39764,39766,39768],{},[2298,39761,36047],{},[2298,39763,39719],{},[2298,39765,39727],{},[2298,39767,39735],{},[2298,39769,39743],{},[2305,39771,39772,39787,39801,39814,39827,39840,39854,39868,39881,39894,39907,39920,39935,39948,39960,39972,39985,39998,40011,40024,40037,40050,40063,40078,40091,40104,40118,40130,40143,40156,40169,40182,40195,40208,40223,40236,40249,40261,40274,40287,40300,40313,40326,40340,40353,40366,40379,40393,40406,40419],{},[2295,39773,39774,39779,39781,39783,39785],{},[2310,39775,39776],{},[364,39777,39778],{},"Team Management",[2310,39780],{},[2310,39782],{},[2310,39784],{},[2310,39786],{},[2295,39788,39789,39792,39795,39797,39799],{},[2310,39790,39791],{},"Manage Team Settings",[2310,39793,39794],{},"✓",[2310,39796,5612],{},[2310,39798,5612],{},[2310,39800,5612],{},[2295,39802,39803,39806,39808,39810,39812],{},[2310,39804,39805],{},"View Team Audit Log",[2310,39807,39794],{},[2310,39809,5612],{},[2310,39811,5612],{},[2310,39813,5612],{},[2295,39815,39816,39819,39821,39823,39825],{},[2310,39817,39818],{},"Invite User",[2310,39820,39794],{},[2310,39822,5612],{},[2310,39824,5612],{},[2310,39826,5612],{},[2295,39828,39829,39832,39834,39836,39838],{},[2310,39830,39831],{},"Change User Role",[2310,39833,39794],{},[2310,39835,5612],{},[2310,39837,5612],{},[2310,39839,5612],{},[2295,39841,39842,39845,39847,39850,39852],{},[2310,39843,39844],{},"Remove User from Team",[2310,39846,39794],{},[2310,39848,39849],{},"§1",[2310,39851,39849],{},[2310,39853,39849],{},[2295,39855,39856,39860,39862,39864,39866],{},[2310,39857,39858],{},[364,39859,15721],{},[2310,39861],{},[2310,39863],{},[2310,39865],{},[2310,39867],{},[2295,39869,39870,39873,39875,39877,39879],{},[2310,39871,39872],{},"Create Application",[2310,39874,39794],{},[2310,39876,5612],{},[2310,39878,5612],{},[2310,39880,5612],{},[2295,39882,39883,39886,39888,39890,39892],{},[2310,39884,39885],{},"Delete Application",[2310,39887,39794],{},[2310,39889,5612],{},[2310,39891,5612],{},[2310,39893,5612],{},[2295,39895,39896,39899,39901,39903,39905],{},[2310,39897,39898],{},"Modify Application Settings",[2310,39900,39794],{},[2310,39902,5612],{},[2310,39904,5612],{},[2310,39906,5612],{},[2295,39908,39909,39912,39914,39916,39918],{},[2310,39910,39911],{},"View Application Logs",[2310,39913,39794],{},[2310,39915,39794],{},[2310,39917,39794],{},[2310,39919,5612],{},[2295,39921,39922,39927,39929,39931,39933],{},[2310,39923,39924],{},[364,39925,39926],{},"Instances",[2310,39928],{},[2310,39930],{},[2310,39932],{},[2310,39934],{},[2295,39936,39937,39940,39942,39944,39946],{},[2310,39938,39939],{},"Create Instance",[2310,39941,39794],{},[2310,39943,5612],{},[2310,39945,5612],{},[2310,39947,5612],{},[2295,39949,39950,39952,39954,39956,39958],{},[2310,39951,38266],{},[2310,39953,39794],{},[2310,39955,5612],{},[2310,39957,5612],{},[2310,39959,5612],{},[2295,39961,39962,39964,39966,39968,39970],{},[2310,39963,38248],{},[2310,39965,39794],{},[2310,39967,5612],{},[2310,39969,5612],{},[2310,39971,5612],{},[2295,39973,39974,39977,39979,39981,39983],{},[2310,39975,39976],{},"View Instance Details",[2310,39978,39794],{},[2310,39980,39794],{},[2310,39982,39794],{},[2310,39984,5612],{},[2295,39986,39987,39990,39992,39994,39996],{},[2310,39988,39989],{},"Start, Stop, Suspend Instance",[2310,39991,39794],{},[2310,39993,5612],{},[2310,39995,5612],{},[2310,39997,5612],{},[2295,39999,40000,40003,40005,40007,40009],{},[2310,40001,40002],{},"Modify Instance Settings",[2310,40004,39794],{},[2310,40006,5612],{},[2310,40008,5612],{},[2310,40010,5612],{},[2295,40012,40013,40016,40018,40020,40022],{},[2310,40014,40015],{},"Modify Environment Variables",[2310,40017,39794],{},[2310,40019,39794],{},[2310,40021,5612],{},[2310,40023,5612],{},[2295,40025,40026,40029,40031,40033,40035],{},[2310,40027,40028],{},"Manage Assets",[2310,40030,39794],{},[2310,40032,39794],{},[2310,40034,5612],{},[2310,40036,5612],{},[2295,40038,40039,40042,40044,40046,40048],{},[2310,40040,40041],{},"View Node-RED Logs",[2310,40043,39794],{},[2310,40045,39794],{},[2310,40047,39794],{},[2310,40049,5612],{},[2295,40051,40052,40055,40057,40059,40061],{},[2310,40053,40054],{},"Access Dashboard or HTTP Endpoint",[2310,40056,39794],{},[2310,40058,39794],{},[2310,40060,39794],{},[2310,40062,39794],{},[2295,40064,40065,40070,40072,40074,40076],{},[2310,40066,40067],{},[364,40068,40069],{},"Flows",[2310,40071],{},[2310,40073],{},[2310,40075],{},[2310,40077],{},[2295,40079,40080,40083,40085,40087,40089],{},[2310,40081,40082],{},"Access Flow Editor",[2310,40084,39794],{},[2310,40086,39794],{},[2310,40088,39794],{},[2310,40090,5612],{},[2295,40092,40093,40096,40098,40100,40102],{},[2310,40094,40095],{},"Modify Flows",[2310,40097,39794],{},[2310,40099,39794],{},[2310,40101,5612],{},[2310,40103,5612],{},[2295,40105,40106,40110,40112,40114,40116],{},[2310,40107,40108],{},[364,40109,11991],{},[2310,40111],{},[2310,40113],{},[2310,40115],{},[2310,40117],{},[2295,40119,40120,40122,40124,40126,40128],{},[2310,40121,11984],{},[2310,40123,39794],{},[2310,40125,39794],{},[2310,40127,5612],{},[2310,40129,5612],{},[2295,40131,40132,40135,40137,40139,40141],{},[2310,40133,40134],{},"Restore Snapshot",[2310,40136,39794],{},[2310,40138,39794],{},[2310,40140,5612],{},[2310,40142,5612],{},[2295,40144,40145,40148,40150,40152,40154],{},[2310,40146,40147],{},"Set as Device Target",[2310,40149,39794],{},[2310,40151,39794],{},[2310,40153,5612],{},[2310,40155,5612],{},[2295,40157,40158,40161,40163,40165,40167],{},[2310,40159,40160],{},"View Snapshots",[2310,40162,39794],{},[2310,40164,39794],{},[2310,40166,39794],{},[2310,40168,5612],{},[2295,40170,40171,40174,40176,40178,40180],{},[2310,40172,40173],{},"Download Snapshot",[2310,40175,39794],{},[2310,40177,39794],{},[2310,40179,5612],{},[2310,40181,5612],{},[2295,40183,40184,40187,40189,40191,40193],{},[2310,40185,40186],{},"Upload Snapshot",[2310,40188,39794],{},[2310,40190,5612],{},[2310,40192,5612],{},[2310,40194,5612],{},[2295,40196,40197,40200,40202,40204,40206],{},[2310,40198,40199],{},"Delete Snapshot",[2310,40201,39794],{},[2310,40203,5612],{},[2310,40205,5612],{},[2310,40207,5612],{},[2295,40209,40210,40215,40217,40219,40221],{},[2310,40211,40212],{},[364,40213,40214],{},"Devices",[2310,40216],{},[2310,40218],{},[2310,40220],{},[2310,40222],{},[2295,40224,40225,40228,40230,40232,40234],{},[2310,40226,40227],{},"View Devices",[2310,40229,39794],{},[2310,40231,39794],{},[2310,40233,39794],{},[2310,40235,5612],{},[2295,40237,40238,40241,40243,40245,40247],{},[2310,40239,40240],{},"Modify Device Settings",[2310,40242,39794],{},[2310,40244,5612],{},[2310,40246,5612],{},[2310,40248,5612],{},[2295,40250,40251,40253,40255,40257,40259],{},[2310,40252,40015],{},[2310,40254,39794],{},[2310,40256,39794],{},[2310,40258,5612],{},[2310,40260,5612],{},[2295,40262,40263,40266,40268,40270,40272],{},[2310,40264,40265],{},"Assign to\u002FRemove from Application",[2310,40267,39794],{},[2310,40269,5612],{},[2310,40271,5612],{},[2310,40273,5612],{},[2295,40275,40276,40279,40281,40283,40285],{},[2310,40277,40278],{},"Assign to\u002FRemove from Instance",[2310,40280,39794],{},[2310,40282,5612],{},[2310,40284,5612],{},[2310,40286,5612],{},[2295,40288,40289,40292,40294,40296,40298],{},[2310,40290,40291],{},"Delete Device",[2310,40293,39794],{},[2310,40295,5612],{},[2310,40297,5612],{},[2310,40299,5612],{},[2295,40301,40302,40305,40307,40309,40311],{},[2310,40303,40304],{},"Bulk Move Devices",[2310,40306,39794],{},[2310,40308,5612],{},[2310,40310,5612],{},[2310,40312,5612],{},[2295,40314,40315,40318,40320,40322,40324],{},[2310,40316,40317],{},"Bulk Delete Devices",[2310,40319,39794],{},[2310,40321,5612],{},[2310,40323,5612],{},[2310,40325,5612],{},[2295,40327,40328,40332,40334,40336,40338],{},[2310,40329,40330],{},[364,40331,778],{},[2310,40333],{},[2310,40335],{},[2310,40337],{},[2310,40339],{},[2295,40341,40342,40345,40347,40349,40351],{},[2310,40343,40344],{},"Add an Item",[2310,40346,39794],{},[2310,40348,39794],{},[2310,40350,5612],{},[2310,40352,5612],{},[2295,40354,40355,40358,40360,40362,40364],{},[2310,40356,40357],{},"Modify an Item",[2310,40359,39794],{},[2310,40361,39794],{},[2310,40363,5612],{},[2310,40365,5612],{},[2295,40367,40368,40371,40373,40375,40377],{},[2310,40369,40370],{},"Delete an Item",[2310,40372,39794],{},[2310,40374,39794],{},[2310,40376,5612],{},[2310,40378,5612],{},[2295,40380,40381,40385,40387,40389,40391],{},[2310,40382,40383],{},[364,40384,1008],{},[2310,40386],{},[2310,40388],{},[2310,40390],{},[2310,40392],{},[2295,40394,40395,40398,40400,40402,40404],{},[2310,40396,40397],{},"Create Client",[2310,40399,39794],{},[2310,40401,39794],{},[2310,40403,5612],{},[2310,40405,5612],{},[2295,40407,40408,40411,40413,40415,40417],{},[2310,40409,40410],{},"Delete Client",[2310,40412,39794],{},[2310,40414,39794],{},[2310,40416,5612],{},[2310,40418,5612],{},[2295,40420,40421,40424,40426,40428,40430],{},[2310,40422,40423],{},"List Clients",[2310,40425,39794],{},[2310,40427,39794],{},[2310,40429,5612],{},[2310,40431,5612],{},[14,40433,40434],{},[364,40435,40436],{},"Notes:",[28,40438,40439,40442],{},[31,40440,40441],{},"§1 Users in any role can remove themselves from a team",[31,40443,40444],{},"Platform Administrators have owner-level access to all teams but cannot access the Flow Editor",[104,40446,40448],{"id":40447},"managing-team-level-roles","Managing Team-Level Roles",[14,40450,40451,40452,4576],{},"Team Owners can manage member roles from the ",[364,40453,38476],{},[768,40455,40457],{"id":40456},"setting-roles-when-inviting-members","Setting Roles When Inviting Members",[14,40459,40460],{},"When inviting a new team member:",[398,40462,40463,40466],{},[31,40464,40465],{},"Navigate to the Team Members",[31,40467,15596,40468],{},[364,40469,40470],{},"Invite Member",[14,40472,40473],{},[638,40474],{"alt":40475,"dataZoomable":55,"src":40476},"Invite team member popup","\u002Fdocs\u002Fuser\u002Fimages\u002Finvite-members.png",[398,40478,40479,40482],{"start":88},[31,40480,40481],{},"Enter the user's username or email address",[31,40483,40484],{},"Select the initial role (Owner, Member, Viewer, or Dashboard Only)",[14,40486,40487],{},[638,40488],{"alt":40489,"dataZoomable":55,"src":40490},"Select role while inviting","\u002Fdocs\u002Fuser\u002Fimages\u002Finvite-popup.png",[398,40492,40493],{"start":156},[31,40494,40495],{},"Send the invitation",[14,40497,40498],{},"The invited user will have the assigned role once they accept the invitation.",[768,40500,40502],{"id":40501},"changing-existing-member-roles","Changing Existing Member Roles",[14,40504,40505],{},"To change a team member's role:",[398,40507,40508,40511,40514,40517],{},[31,40509,40510],{},"Navigate to the Team Members page",[31,40512,40513],{},"Locate the user whose role you want to change",[31,40515,40516],{},"Click the three-dot icon next to their username",[31,40518,40519,40520],{},"Select ",[364,40521,40522],{},"Change Role",[14,40524,40525],{},[638,40526],{"alt":40527,"dataZoomable":55,"src":40528},"Change member role","\u002Fdocs\u002Fuser\u002Fimages\u002Fchange-role.png",[398,40530,40531,40534],{"start":156},[31,40532,40533],{},"Choose the new role from the popup (similar to the invitation process)",[31,40535,40536],{},"Confirm the change",[14,40538,40539,40541],{},[364,40540,1798],{}," An Owner can only change their own role if at least one other Owner exists on the team.",[23,40543,39703],{"id":40544},"application-level-rbac",[14,40546,40547],{},"Application-Level RBAC enables you to control permissions at the individual application level within a team. This allows different team members to have different permission levels for different applications without creating multiple teams.",[14,40549,40550],{},"This is an Enterprise lisenced feature for Self Hosted Users and requires an Entprise Team on FlowFuse Cloud.",[104,40552,15465],{"id":35730},[14,40554,40555,40556,40558],{},"Team-level roles define default permissions across all resources.",[662,40557],{},"\nApplication-level roles override these defaults for specific applications.",[14,40560,40561,40562,40564],{},"When you assign an application-level role to a team member, it takes precedence over their team-level role ",[364,40563,1355],{}," for that application. Their team-level role applies to all other applications.",[104,40566,40568],{"id":40567},"available-roles","Available Roles",[14,40570,40571],{},"Application-level roles follow the same structure:",[28,40573,40574,40579,40584,40589],{},[31,40575,40576,40578],{},[364,40577,39719],{}," – Full control over the application",[31,40580,40581,40583],{},[364,40582,39727],{}," – Can develop and manage flows, create snapshots, modify environment variables",[31,40585,40586,40588],{},[364,40587,39735],{}," – Read-only access",[31,40590,40591,40593],{},[364,40592,39743],{}," – Can only view dashboards and HTTP endpoints",[104,40595,40597],{"id":40596},"permission-hierarchy","Permission Hierarchy",[398,40599,40600,40603,40606],{},[31,40601,40602],{},"If a user has an application-level role, that determines their permissions for the application.",[31,40604,40605],{},"If not, their team-level role applies.",[31,40607,40608],{},"Team Owners always have full access to all applications.",[14,40610,40611,40613,40615,40616,40618,40619,40621],{},[364,40612,31733],{},[662,40614],{},"\nA team-level ",[1160,40617,39727],{}," is assigned ",[1160,40620,39735],{}," permissions for one production application. They can only view flows in that application, but retain normal Member permissions for all others.",[104,40623,40625],{"id":40624},"configuring-application-level-roles","Configuring Application-Level Roles",[14,40627,40628],{},"Team Owners can configure application-level roles:",[398,40630,40631,40634,40643],{},[31,40632,40633],{},"Navigate to the application",[31,40635,8610,40636,40639,40640],{},[364,40637,40638],{},"Application Settings"," → ",[364,40641,40642],{},"User Access",[31,40644,40645,40646],{},"Click the three-dot icon next to the user and select ",[364,40647,40648],{},"Edit Permission",[14,40650,40651],{},[638,40652],{"alt":40653,"dataZoomable":55,"src":40654},"Application user access settings","\u002Fdocs\u002Fuser\u002Fimages\u002Fapplication-rbac.png",[398,40656,40657],{"start":99},[31,40658,40659],{},"In the popup, assign the desired application-level role",[14,40661,40662],{},[638,40663],{"alt":40664,"dataZoomable":55,"src":40665},"Application RBAC popup","\u002Fdocs\u002Fuser\u002Fimages\u002Fapplication-rbac-popup.png",[398,40667,40668],{"start":156},[31,40669,40670],{},"Changes apply immediately",[14,40672,40673],{},"To remove an application-level assignment, simply clear the role. The user will fall back to their team-level role for that application.",{"title":55,"searchDepth":77,"depth":77,"links":40675},[40676,40677,40682],{"id":39686,"depth":77,"text":39687},{"id":39707,"depth":77,"text":39697,"children":40678},[40679,40680,40681],{"id":39713,"depth":88,"text":39714},{"id":39749,"depth":88,"text":39750},{"id":40447,"depth":88,"text":40448},{"id":40544,"depth":77,"text":39703,"children":40683},[40684,40685,40686,40687],{"id":35730,"depth":88,"text":15465},{"id":40567,"depth":88,"text":40568},{"id":40596,"depth":88,"text":40597},{"id":40624,"depth":88,"text":40625},{},"user\u002Frole-based-access-control.md","\u002Fdocs\u002Fuser\u002Frole-based-access-control",{"title":39675,"description":39683},"docs\u002Fuser\u002Frole-based-access-control","vVbFeUtFkaM0Rx6IcITd9doch5AKv0Zk00Azg3JUkyA",{"id":40695,"title":4162,"body":40696,"description":40815,"extension":329,"layout":330,"meta":40816,"navGroup":330,"navOrder":330,"navTitle":4162,"navigation":187,"originalPath":40817,"path":784,"redirect":330,"seo":40818,"stem":40819,"updated":337,"version":338,"__hash__":40820},"docs\u002Fdocs\u002Fuser\u002Fshared-library.md",{"type":7,"value":40697,"toc":40810},[40698,40701,40711,40714,40717,40725,40729,40732,40757,40761,40764,40787,40792,40796,40799],[10,40699,4162],{"id":40700},"shared-team-library",[14,40702,40703,40704,302,40707,40710],{},"Node-RED allows you to import and export ",[364,40705,40706],{},"flows",[364,40708,40709],{},"functions"," to a local library. This is helpful\nfor saving pieces of flow that you want to reuse.",[14,40712,40713],{},"With FlowFuse Premium, each Node-RED instance has access to a Team Library that makes\nit very easy to share flows and functions without having to manually copy them around.",[14,40715,40716],{},"For example, you may have a standard set of flows that you want each Node-RED instance\nto include. By exporting them to the Team Library, you can quickly import them wherever\nyou want to use them.",[14,40718,40719,40720,273],{},"A short video is available on ",[41,40721,40724],{"href":40722,"rel":40723},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=B7XK3TUklUU",[831],"how to use this feature",[104,40726,40728],{"id":40727},"exporting-flows","Exporting flows",[14,40730,40731],{},"To export flows to the library within the Node-RED editor:",[398,40733,40734,40737,40747,40751,40754],{},[31,40735,40736],{},"Select the nodes to be included",[31,40738,40739,40740,40743,40744,660],{},"Open the Export Dialog (",[18,40741,40742],{},"Main Menu -> Export",", or ",[18,40745,40746],{},"Ctrl\u002FCmd-E",[31,40748,16421,40749,35813],{},[18,40750,778],{},[31,40752,40753],{},"Enter a name for the library entry",[31,40755,40756],{},"Click Export",[104,40758,40760],{"id":40759},"importing-flows","Importing flows",[14,40762,40763],{},"To import flows within the Node-RED editor:",[398,40765,40766,40775,40778,40781,40784],{},[31,40767,40768,40769,40743,40772,660],{},"Open the Import Dialog (",[18,40770,40771],{},"Main Menu -> Import",[18,40773,40774],{},"Ctrl\u002FCmd-I",[31,40776,40777],{},"Select the 'Team Library' tab",[31,40779,40780],{},"Select the flow to import",[31,40782,40783],{},"Click Import",[31,40785,40786],{},"Place the imported nodes within your flows",[14,40788,40789],{},[638,40790],{"alt":55,"src":40791},"\u002Fdocs\u002Fuser\u002Fimages\u002Fshared-lib-import.png",[104,40793,40795],{"id":40794},"viewing-team-library","Viewing Team Library",[14,40797,40798],{},"It is possible to explore your Team Library within FlowFuse by clicking \"Library\" in your Team options.",[14,40800,40801,40802,40805,40806,40809],{},"You can inspect the contents of any ",[18,40803,40804],{},".json"," flow file, or ",[18,40807,40808],{},".js"," function file here too.",{"title":55,"searchDepth":77,"depth":77,"links":40811},[40812,40813,40814],{"id":40727,"depth":88,"text":40728},{"id":40759,"depth":88,"text":40760},{"id":40794,"depth":88,"text":40795},"Node-RED allows you to import and export flows and functions to a local library. This is helpful\nfor saving pieces of flow that you want to reuse.",{},"user\u002Fshared-library.md",{"title":4162,"description":40815},"docs\u002Fuser\u002Fshared-library","KIX2CPQaF9QHswVGsk8gaj6e1spTQopvzI_6iFOBo3g",{"id":40822,"title":11991,"body":40823,"description":55,"extension":329,"layout":330,"meta":41670,"navGroup":330,"navOrder":330,"navTitle":11991,"navigation":187,"originalPath":41671,"path":949,"redirect":330,"seo":41672,"stem":41673,"updated":337,"version":338,"__hash__":41674},"docs\u002Fdocs\u002Fuser\u002Fsnapshots.md",{"type":7,"value":40824,"toc":41647},[40825,40828,40831,40834,40851,40855,40968,40972,40975,40978,41006,41015,41018,41021,41025,41028,41074,41083,41087,41090,41093,41102,41105,41108,41143,41152,41156,41158,41161,41184,41187,41191,41194,41211,41214,41217,41236,41239,41242,41245,41248,41310,41313,41316,41319,41368,41371,41374,41390,41399,41403,41406,41410,41416,41432,41435,41439,41444,41460,41463,41466,41472,41476,41482,41488,41491,41494,41497,41500,41503,41506,41509,41517,41525,41529,41532,41535,41579,41582,41585,41600,41608,41629,41637],[10,40826,11991],{"id":40827},"snapshots",[23,40829,4659],{"id":40830},"introduction",[14,40832,40833],{},"A Snapshot is a point-in-time backup of a Node-RED instance. It captures:",[398,40835,40836,40839,40842,40845,40848],{},[31,40837,40838],{},"The flows",[31,40840,40841],{},"Credentials",[31,40843,40844],{},"Environment variables",[31,40846,40847],{},"NPM packages, with locked versions",[31,40849,40850],{},"Runtime settings.",[23,40852,40854],{"id":40853},"in-this-document","In this document",[28,40856,40857,40864,40886,40893,40899,40905,40912,40919,40926,40933,40940,40947,40954,40961],{},[31,40858,40859,40863],{},[41,40860,40862],{"href":40861},"#application-snapshots-overview","Application Snapshots Overview"," - An overview of all snapshots belonging to an application and the available actions",[31,40865,40866,40870,40871],{},[41,40867,40869],{"href":40868},"#instance-snapshots-overview","Instance Snapshots Overview"," - An overview of all snapshots belonging to an instance\n",[28,40872,40873,40879],{},[31,40874,40875,40878],{},[41,40876,11991],{"href":40877},"#snapshot-list"," - A list of all snapshots belonging to an instance and the available actions",[31,40880,40881,40885],{},[41,40882,40884],{"href":40883},"#timeline-view","Timeline"," - A visual timeline of instance changes and snapshots interleaved",[31,40887,40888,40892],{},[41,40889,40891],{"href":40890},"#device-snapshots-overview","Device Snapshots Overview"," - An overview of all snapshots belonging to a device and the available actions",[31,40894,40895,40898],{},[41,40896,11904],{"href":40897},"#create-a-snapshot"," - Create a snapshot of a device or an instance",[31,40900,40901,40904],{},[41,40902,40134],{"href":40903},"#restore-a-snapshot"," - Apply a snapshot to the runtime of a device or an instance",[31,40906,40907,40911],{},[41,40908,40910],{"href":40909},"#edit-a-snapshot","Edit a snapshot"," - Edit the name and description of a snapshot",[31,40913,40914,40918],{},[41,40915,40917],{"href":40916},"#upload-a-snapshot","Upload a snapshot"," - Upload a snapshot to a device or an instance",[31,40920,40921,40925],{},[41,40922,40924],{"href":40923},"#download-a-snapshot","Download a snapshot"," - Download a snapshot to your local machine",[31,40927,40928,40932],{},[41,40929,40931],{"href":40930},"#delete-a-snapshot","Delete a snapshot"," - Delete a snapshot",[31,40934,40935,40939],{},[41,40936,40938],{"href":40937},"#instance-owned-devices","Set Device Target (instance)"," - Set a snapshot as the target for all devices belonging to an instance",[31,40941,40942,40946],{},[41,40943,40945],{"href":40944},"#creating-a-snapshot-from-a-device","Creating a Snapshot from a device"," - Create a snapshot from the device overview page",[31,40948,40949,40953],{},[41,40950,40952],{"href":40951},"#creating-a-snapshot-locally","Creating a Snapshot from within a device"," - Create a snapshot from within Node-RED",[31,40955,40956,40960],{},[41,40957,40959],{"href":40958},"#auto-snapshots","Auto Snapshots"," - Automatically create snapshots when flows are deployed",[31,40962,40963,40967],{},[41,40964,40966],{"href":40965},"#previewing-snapshots","Previewing Snapshots"," - Preview the flows of a snapshot",[23,40969,40971],{"id":40970},"snapshot-views","Snapshot Views:",[104,40973,40862],{"id":40974},"application-snapshots-overview",[14,40976,40977],{},"All snapshots belonging to the instances and devices of an application are gathered and presented in a single list where you can perform the following actions:",[28,40979,40980,40984,40989,40996,41001],{},[31,40981,40982,40911],{},[41,40983,40910],{"href":40909},[31,40985,40986,40967],{},[41,40987,40988],{"href":40965},"View Snapshot",[31,40990,40991,40995],{},[41,40992,40994],{"href":40993},"#comparing-snapshots","Compare Snapshot"," - Compare the snapshot with another snapshot",[31,40997,40998,41000],{},[41,40999,40173],{"href":40923}," - Download the snapshot to your local machine",[31,41002,41003,41005],{},[41,41004,40199],{"href":40930}," - Delete the snapshot",[14,41007,41008,41012],{},[638,41009],{"alt":41010,"src":41011},"Application Snapshots","\u002Fdocs\u002Fuser\u002Fimages\u002Fsnapshots\u002Fapplication-snapshots.png",[1160,41013,41014],{},"Screenshot showing Applications Snapshot list",[104,41016,40869],{"id":41017},"instance-snapshots-overview",[14,41019,41020],{},"Snapshots belonging to an instance are presented as a list or a visual timeline:",[768,41022,41024],{"id":41023},"snapshot-list","Snapshot List",[14,41026,41027],{},"Snapshots belonging to an instance are gathered and presented in a single list where you can perform the following actions:",[28,41029,41030,41035,41040,41046,41052,41057,41061,41065,41070],{},[31,41031,41032,41034],{},[41,41033,40186],{"href":40916}," - Upload a snapshot to the instance",[31,41036,41037,41039],{},[41,41038,11984],{"href":40897}," - Create a snapshot of the instance",[31,41041,41042,41045],{},[41,41043,40134],{"href":41044},"#setting-a-device-target-snapshot"," - Restore the snapshot to the instance",[31,41047,41048,41051],{},[41,41049,41050],{"href":40909},"Edit Snapshot"," - Edit the snapshot name and description",[31,41053,41054,41056],{},[41,41055,40988],{"href":40965}," - Preview the snapshot flows",[31,41058,41059,40995],{},[41,41060,40994],{"href":40993},[31,41062,41063,41000],{},[41,41064,40173],{"href":40923},[31,41066,41067,41069],{},[41,41068,40147],{"href":41044}," - Set the snapshot as the device target snapshot",[31,41071,41072,41005],{},[41,41073,40199],{"href":40930},[14,41075,41076,41080],{},[638,41077],{"alt":41078,"src":41079},"Instance Snapshots","\u002Fdocs\u002Fuser\u002Fimages\u002Fsnapshots\u002Finstance-snapshots.png",[1160,41081,41082],{},"Screenshot showing Instance Snapshot list",[768,41084,41086],{"id":41085},"timeline-view","Timeline View",[14,41088,41089],{},"The timeline view shows a visual representation of changes made to an instance with\navailable snapshots interleaved to signify when they were created.",[14,41091,41092],{},"Any snapshots displayed inline on the timeline will have the same actions available as in the Snapshot List view above.",[14,41094,41095,41099],{},[638,41096],{"alt":41097,"src":41098},"Visual Timeline","\u002Fdocs\u002Fuser\u002Fimages\u002Fsnapshots\u002Finstance-timeline.png",[1160,41100,41101],{},"Screenshot showing Instance Visual Timeline",[104,41103,40891],{"id":41104},"device-snapshots-overview",[14,41106,41107],{},"Snapshots belonging to a device are presented in a single list where you can perform the following actions:",[28,41109,41110,41114,41118,41123,41127,41131,41135,41139],{},[31,41111,41112,41034],{},[41,41113,40186],{"href":40916},[31,41115,41116,41039],{},[41,41117,11984],{"href":40897},[31,41119,41120,41122],{},[41,41121,40134],{"href":41044}," - Set the snapshot as the devices target snapshot",[31,41124,41125,41051],{},[41,41126,41050],{"href":40909},[31,41128,41129,41056],{},[41,41130,40988],{"href":40965},[31,41132,41133,40995],{},[41,41134,40994],{"href":40993},[31,41136,41137,41000],{},[41,41138,40173],{"href":40923},[31,41140,41141,41005],{},[41,41142,40199],{"href":40930},[14,41144,41145,41149],{},[638,41146],{"alt":41147,"src":41148},"Device Snapshots","\u002Fdocs\u002Fuser\u002Fimages\u002Fsnapshots\u002Fdevice-snapshots.png",[1160,41150,41151],{},"Screenshot showing Device Snapshot list",[23,41153,41155],{"id":41154},"snapshot-actions","Snapshot Actions",[104,41157,11904],{"id":33736},[14,41159,41160],{},"To create a snapshot:",[398,41162,41163,41168,41172,41180],{},[31,41164,41165,41166,16058],{},"Go to the device or instance's page and select the ",[364,41167,11991],{},[31,41169,15930,41170,15933],{},[364,41171,11984],{},[31,41173,41174,41175,41177,41178,273],{},"You will be prompted to give the snapshot a ",[364,41176,5782],{}," and optional ",[364,41179,440],{},[31,41181,15596,41182],{},[364,41183,16097],{},[14,41185,41186],{},"The list of snapshots will update with the newly created entry at the top.",[104,41188,41190],{"id":41189},"restore-a-snapshot","Restore a snapshot",[14,41192,41193],{},"To restore a snapshot:",[398,41195,41196,41201,41206],{},[31,41197,41198,41199,16058],{},"Go to the desired device or instance page and select the ",[364,41200,11991],{},[31,41202,41203,41204,16370],{},"Open the dropdown menu to the right of the snapshot you want to restore and\nselect the ",[364,41205,40134],{},[31,41207,41208,41209,9521],{},"You will be asked to confirm - click ",[364,41210,35890],{},[104,41212,40910],{"id":41213},"edit-a-snapshot",[14,41215,41216],{},"To edit a snapshot:",[398,41218,41219,41223,41228,41231],{},[31,41220,34717,41221,16058],{},[364,41222,11991],{},[31,41224,41225,41226,16370],{},"Open the dropdown menu to the right of the snapshot you want to edit and\nselect the ",[364,41227,41050],{},[31,41229,41230],{},"Update the name and description as required.",[31,41232,15596,41233],{},[364,41234,41235],{},"Update",[14,41237,41238],{},"NOTE:\nChanges made to a snapshot will not be immediately reflected in the Node-RED runtime already running this snapshot.",[104,41240,40917],{"id":41241},"upload-a-snapshot",[14,41243,41244],{},"A snapshot can be uploaded to an device or instance from your local machine.",[14,41246,41247],{},"To upload a snapshot:",[398,41249,41250,41255,41259,41262,41265,41299,41305],{},[31,41251,41252,41253,16058],{},"Go to the desired instance or device overview page and select the ",[364,41254,11991],{},[31,41256,15930,41257,15933],{},[364,41258,40186],{},[31,41260,41261],{},"Select the snapshot file from your local machine.",[31,41263,41264],{},"Update the name and description if required.",[31,41266,41267,41268],{},"Select the components to upload:\n",[28,41269,41270,41275,41280],{},[31,41271,41272,41274],{},[364,41273,40069],{},": Include the snapshots flows",[31,41276,41277,41279],{},[364,41278,40841],{},": Include the snapshots flows credentials (visible only if the snapshot contains credentials)",[31,41281,41282,41284,41285],{},[364,41283,11824],{},": Include environment variables in the snapshot\n",[28,41286,41287,41293],{},[31,41288,41289,41292],{},[364,41290,41291],{},"Keys and Values",": Include the keys and values of the environment variables",[31,41294,41295,41298],{},[364,41296,41297],{},"Keys Only",": Include only the keys of the environment variables",[31,41300,41301,41302,41304],{},"If the snapshot contains credentials and the ",[18,41303,40841],{}," component is checked,\nyou will be asked to enter a Secret. This will be used to later decrypt any credentials in the snapshots flows.",[31,41306,15596,41307],{},[364,41308,41309],{},"Upload",[104,41311,40924],{"id":41312},"download-a-snapshot",[14,41314,41315],{},"A snapshot can be downloaded to your local machine for backup or sharing.",[14,41317,41318],{},"To download a snapshot:",[398,41320,41321,41326,41332,41361,41364],{},[31,41322,41323,41324,16058],{},"Go to the desired application, instance or device overview page and select the ",[364,41325,11991],{},[31,41327,41328,41329,41331],{},"Open the dropdown menu to the right of the snapshot you want to download and\nclick the ",[364,41330,40173],{}," option to open the download dialog.",[31,41333,41334,41335],{},"Select the required components to download.\n",[28,41336,41337,41342,41347],{},[31,41338,41339,41341],{},[364,41340,40069],{},": Include the snapshot flows",[31,41343,41344,41346],{},[364,41345,40841],{},": Include the snapshot flows credentials",[31,41348,41349,41284,41351],{},[364,41350,11824],{},[28,41352,41353,41357],{},[31,41354,41355,41292],{},[364,41356,41291],{},[31,41358,41359,41298],{},[364,41360,41297],{},[31,41362,41363],{},"Enter a secret to encrypt any credentials in the snapshot (optional, depends on components selected).",[31,41365,15596,41366],{},[364,41367,16123],{},[104,41369,40931],{"id":41370},"delete-a-snapshot",[14,41372,41373],{},"To delete a snapshot:",[398,41375,41376,41380,41386],{},[31,41377,34717,41378,16058],{},[364,41379,11991],{},[31,41381,41382,41383,16370],{},"Open the dropdown menu to the right of the snapshot you want to delete and\nselect the ",[364,41384,41385],{},"Delete snapshot",[31,41387,41208,41388,9521],{},[364,41389,16694],{},[14,41391,41392,41394,41395,41398],{},[1160,41393,1798],{}," If the snapshot is the current ",[364,41396,41397],{},"Device Target"," snapshot, this will\ncause any connected devices to stop running the snapshot when they next check in.",[104,41400,41402],{"id":41401},"setting-a-device-target-snapshot","Setting a Device Target snapshot",[14,41404,41405],{},"Snapshots are used to identify a version of the Node-RED instance that should be pushed\nout to any connected devices. This allows you to develop your flows in FlowFuse\nand only push out to the devices when it is ready.",[768,41407,41409],{"id":41408},"instance-owned-devices","Instance owned devices",[14,41411,41412,41413,41415],{},"To set the ",[364,41414,41397],{}," of an instance owned device:",[398,41417,41418,41422,41427],{},[31,41419,34717,41420,16058],{},[364,41421,11991],{},[31,41423,41424,41425,16370],{},"Open the dropdown menu to the right of the snapshot you want to set as the\ndevice target and select the ",[364,41426,40147],{},[31,41428,41208,41429,9521],{},[364,41430,41431],{},"Set Target",[14,41433,41434],{},"This will cause the snapshot to be pushed out to any connected devices the\nnext time it checks in.",[768,41436,41438],{"id":41437},"application-owned-devices","Application owned devices",[14,41440,41412,41441,41443],{},[364,41442,41397],{}," of an application owned device:",[398,41445,41446,41451,41454],{},[31,41447,41448,41449,16058],{},"Go to the devices's page and select the ",[364,41450,11991],{},[31,41452,41453],{},"In the list of snapshots available, a \"Restore Snapshot\" button will be displayed\nfor each snapshot as you hover over it.",[31,41455,41456,41457,41459],{},"You will be asked to confirm - click the ",[364,41458,35890],{}," button to set it as the target snapshot.",[14,41461,41462],{},"This will cause the snapshot to be pushed out to the device the\nnext time it checks in.",[104,41464,40945],{"id":41465},"creating-a-snapshot-from-a-device",[14,41467,41468,41469,41471],{},"It is possible to create a Snapshot from a device that is connected to the platform.\nThe device must be set to Developer Mode for this to work.\nSee ",[41,41470,38968],{"href":12167}," for\nmore information.",[104,41473,41475],{"id":41474},"creating-a-snapshot-locally","Creating a Snapshot locally",[14,41477,41478,41479,41481],{},"Using the ",[41,41480,39090],{"href":33572}," it is also possible to create\nSnapshots in a local copy of Node-RED and push them back into your FlowFuse\nmanaged Node-RED instances.",[14,41483,41484,41485,273],{},"For more information, see the ",[41,41486,41487],{"href":33572},"Node-RED Tools Plugin guide",[104,41489,40959],{"id":41490},"auto-snapshots",[14,41492,41493],{},"FlowFuse can automatically create snapshots whenever flows are deployed\nto the instance. This is useful for tracking changes, and rolling back.",[14,41495,41496],{},"FlowFuse will label these snapshots as \"Auto snapshot - yyyy-mm-dd hh:mm:ss\".\nA limit of 10 auto snapshots will be kept, with the oldest being deleted when a new one is created.",[14,41498,41499],{},"Devices can optionally disable auto snapshots, in the developer mode tab. This can be helpful to\navoid excessive data usage when a device is in the field or on a cellular connection.",[14,41501,41502],{},"NOTE: This feature is only available to Team and Enterprise tier teams",[104,41504,40966],{"id":41505},"previewing-snapshots",[14,41507,41508],{},"From any Snapshots tab, you can preview the flows of a snapshot by selecting the Snapshot's actions,\nand selecting \"View Snapshot\".",[14,41510,41511,41515],{},[638,41512],{"alt":41513,"src":41514},"Screenshot to show the available \"Actions\" for a given Snapshot","\u002Fdocs\u002Fuser\u002Fimages\u002Fsnapshots-actions.png",[1160,41516,41513],{},[14,41518,41519,41523],{},[638,41520],{"alt":41521,"src":41522},"Screenshot to an example flow preview for a Snapshot in FlowFuse","\u002Fdocs\u002Fuser\u002Fimages\u002Fsnapshots-preview.png",[1160,41524,41521],{},[104,41526,41528],{"id":41527},"comparing-snapshots","Comparing Snapshots",[14,41530,41531],{},"From any Snapshots tab, you can compare two snapshots by selecting the Snapshot's action, then selecting \"Compare Snapshots\". This will open a dialog where you choose a second snapshot to compare with.",[14,41533,41534],{},"The comparison view has three panels:",[28,41536,41537,41563,41569],{},[31,41538,41539,41542,41543],{},[364,41540,41541],{},"Left sidebar"," — lists every node that differs between the two snapshots. Each entry shows a node-type badge (config node, tab, or regular node) and one of three statuses:\n",[28,41544,41545,41551,41557],{},[31,41546,41547,41550],{},[364,41548,41549],{},"Added"," — the node exists in the newer snapshot but not the older one",[31,41552,41553,41556],{},[364,41554,41555],{},"Deleted"," — the node existed in the older snapshot but has been removed",[31,41558,41559,41562],{},[364,41560,41561],{},"Changed"," — the node exists in both snapshots but one or more properties differ",[31,41564,41565,41568],{},[364,41566,41567],{},"Flow canvas"," — highlights the selected node and scrolls to it automatically, giving a visual indication of where the change is in your flow",[31,41570,41571,41574,41575,41578],{},[364,41572,41573],{},"Right panel"," — shows the property and code changes for the selected node. Use the ",[364,41576,41577],{},"Prev \u002F Next"," buttons to step through changes one at a time",[14,41580,41581],{},"Selecting an entry in the sidebar highlights the corresponding node on the canvas. The highlight clears automatically when you select a different entry.",[14,41583,41584],{},"There are two types of diff shown in the right panel:",[28,41586,41587],{},[31,41588,41589,41592,41593,41595,41596,41599],{},[364,41590,41591],{},"Property diffs"," — each changed property is shown with the old and new value side by side, with red ",[18,41594,5612],{}," for removed and green ",[18,41597,41598],{},"+"," for added",[14,41601,41602,41606],{},[638,41603],{"alt":41604,"src":41605},"Screenshot showing property-level diff with old and new values displayed side by side","\u002Fdocs\u002Fuser\u002Fimages\u002Fsnapshots\u002Fsnapshot-diff-prop-change.png",[1160,41607,41604],{},[28,41609,41610],{},[31,41611,41612,41615,41616,41618,41619,41621,41622,302,41625,41628],{},[364,41613,41614],{},"Code diffs"," — for multiline properties such as function and template nodes, changes appear as a line-level diff with red ",[18,41617,5612],{}," for removed lines and green ",[18,41620,41598],{}," for added, in the same format as a git diff. Use the ",[364,41623,41624],{},"Prettify",[364,41626,41627],{},"Wrap"," toggles to make large or nested values easier to read",[14,41630,41631,41635],{},[638,41632],{"alt":41633,"src":41634},"Screenshot showing a code diff for a function node with red and green line-level changes","\u002Fdocs\u002Fuser\u002Fimages\u002Fsnapshots\u002Fsnapshot-diff-code-change.png",[1160,41636,41633],{},[14,41638,41639,41640,302,41643,41646],{},"Not every difference between two snapshots is meaningful. Computed layout properties such as group node ",[18,41641,41642],{},"w",[18,41644,41645],{},"h"," values are automatically excluded because they are recalculated by Node-RED and do not reflect intentional edits. Position-only changes (nodes that moved on the canvas but had no property edits) can be hidden when you want to focus on substantive changes.",{"title":55,"searchDepth":77,"depth":77,"links":41648},[41649,41650,41651,41656],{"id":40830,"depth":77,"text":4659},{"id":40853,"depth":77,"text":40854},{"id":40970,"depth":77,"text":40971,"children":41652},[41653,41654,41655],{"id":40974,"depth":88,"text":40862},{"id":41017,"depth":88,"text":40869},{"id":41104,"depth":88,"text":40891},{"id":41154,"depth":77,"text":41155,"children":41657},[41658,41659,41660,41661,41662,41663,41664,41665,41666,41667,41668,41669],{"id":33736,"depth":88,"text":11904},{"id":41189,"depth":88,"text":41190},{"id":41213,"depth":88,"text":40910},{"id":41241,"depth":88,"text":40917},{"id":41312,"depth":88,"text":40924},{"id":41370,"depth":88,"text":40931},{"id":41401,"depth":88,"text":41402},{"id":41465,"depth":88,"text":40945},{"id":41474,"depth":88,"text":41475},{"id":41490,"depth":88,"text":40959},{"id":41505,"depth":88,"text":40966},{"id":41527,"depth":88,"text":41528},{},"user\u002Fsnapshots.md",{"title":11991,"description":55},"docs\u002Fuser\u002Fsnapshots","TaO7U2MCSCBPCt7mb3whshMXzwBwMh7nmXhbgTONlzk",{"id":41676,"title":41677,"body":41678,"description":41685,"extension":329,"layout":330,"meta":41921,"navGroup":330,"navOrder":62,"navTitle":41677,"navigation":187,"originalPath":41922,"path":900,"redirect":330,"seo":41923,"stem":41924,"updated":337,"version":338,"__hash__":41925},"docs\u002Fdocs\u002Fuser\u002Fstatic-asset-service.md","Static asset service",{"type":7,"value":41679,"toc":41908},[41680,41683,41686,41690,41693,41695,41697,41705,41707,41710,41712,41746,41748,41751,41772,41775,41779,41783,41786,41791,41795,41798,41802,41805,41810,41814,41818,41821,41825,41828,41833,41837,41840,41845,41849,41852,41856,41859,41862,41868,41874,41880,41883,41891,41895,41898],[10,41681,41677],{"id":41682},"static-asset-service",[14,41684,41685],{},"Our platform now includes a Static Asset Service, enabling you to manage files seamlessly within your hosted Node-RED instances.",[23,41687,41689],{"id":41688},"what-is-static-asset-service","What is Static asset service?",[14,41691,41692],{},"The Static Asset Service allows you to store files permanently within your FlowFuse Instances. Files that you upload, generate, or modify will remain accessible even after your session ends or the application restarts.",[23,41694,26],{"id":25},[104,41696,4067],{"id":4081},[28,41698,41699,41702],{},[31,41700,41701],{},"A Instance Stack with a launcher version of 2.8.0 or greater.",[31,41703,41704],{},"Team or Enterprise Team Type.",[104,41706,34662],{"id":34661},[14,41708,41709],{},"This feature is available only on self-hosted Enterprise licensed versions of FlowFuse.",[104,41711,33598],{"id":33597},[28,41713,41714,41717,41743],{},[31,41715,41716],{},"Uploaded file sizes must not exceed 5MB.",[31,41718,41719,41720],{},"Unsupported characters (dependent on the operating system):\n",[28,41721,41722,41736],{},[31,41723,41724,41725,3012,41727,1706,41730,3012,41733,660],{},"empty paths (eg: ",[18,41726,3601],{},[18,41728,41729],{},"\u002F\u002F",[18,41731,41732],{},"\\",[18,41734,41735],{},"\\\\",[31,41737,41738,41739,3012,41741],{},"special chars: ",[18,41740,1795],{},[18,41742,3939],{},[31,41744,41745],{},"Team permissions required: owner \u002F member",[23,41747,17956],{"id":552},[14,41749,41750],{},"If the prerequisites are met, you will be able to use the Static Asset Service capabilities of FlowFuse Instances from two locations:",[28,41752,41753,41764],{},[31,41754,41755,41756,41759,41760],{},"Instance Assets tab within the ",[364,41757,41758],{},"Instance Details"," page.\n",[638,41761],{"alt":41762,"dataZoomable":55,"src":41763},"Instance Details Page","\u002Fdocs\u002Fuser\u002Fimages\u002Fassets-tab-instance.png",[31,41765,41755,41766,7666,41769],{},[364,41767,41768],{},"Immersive Editor",[638,41770],{"alt":41768,"dataZoomable":55,"src":41771},"\u002Fdocs\u002Fuser\u002Fimages\u002Fassets-tab-editor.png",[14,41773,41774],{},"The following steps assume that you have navigated to one of these locations and have the assets tab opened.",[104,41776,41778],{"id":41777},"files","Files",[768,41780,41782],{"id":41781},"uploading-a-file","Uploading a File",[14,41784,41785],{},"To upload a file, click the 'Upload' button, select your desired file, and click confirm. This will upload the file to the current storage folder.",[14,41787,41788],{},[1160,41789,41790],{},"Note: Uploading a file with the same name as an existing one will overwrite the existing file.",[768,41792,41794],{"id":41793},"deleting-a-file","Deleting a File",[14,41796,41797],{},"Navigate to the file you want to delete, click on the kebab menu (three vertical dots) associated with the file, select 'Delete File,' and confirm when prompted.",[768,41799,41801],{"id":41800},"renaming-a-file","Renaming a File",[14,41803,41804],{},"Currently, this feature is not supported. To rename a file, upload the file with the desired name and delete the old one from the instance's Persistent Storage.",[14,41806,41807],{},[1160,41808,41809],{},"Caution: Renaming a file will also affect any linked files or relative paths to that file in the Node-RED instance.",[104,41811,41813],{"id":41812},"folders","Folders",[768,41815,41817],{"id":41816},"creating-a-folder","Creating a Folder",[14,41819,41820],{},"To create a folder, click the 'New Folder' button, enter your desired folder name, and click confirm. This will create the folder inside the current directory.",[768,41822,41824],{"id":41823},"deleting-a-folder","Deleting a Folder",[14,41826,41827],{},"Navigate to the folder you want to delete, click on the kebab menu associated with the folder, select 'Delete Folder,' and confirm when prompted.",[14,41829,41830],{},[1160,41831,41832],{},"Caution: Deleting a folder that contains files or other folders will permanently delete all nested files and folders.",[768,41834,41836],{"id":41835},"renaming-a-folder","Renaming a Folder",[14,41838,41839],{},"Navigate to the folder you want to rename, click on the kebab menu, select 'Edit Folder,' and confirm the new name when prompted.",[14,41841,41842],{},[1160,41843,41844],{},"Caution: Renaming a folder will also affect any linked files or relative paths of nested files or directories in the Node-RED instance.",[768,41846,41848],{"id":41847},"folder-navigation","Folder Navigation",[14,41850,41851],{},"You can navigate through folder structures by clicking on any folder and return by using the Working Directory Breadcrumbs located at the top of the Search Files input.",[768,41853,41855],{"id":41854},"folder-visibility","Folder Visibility",[14,41857,41858],{},"Following the 2.9.0 release, you can set the folder's visibility using the Visibility selector found in the Navigation section.\nThis means that users can set the visibility of their uploaded files to public and make them accessible outside the node-red instance itself.",[14,41860,41861],{},"When setting a folder's visibility to public you are required to set a static file path on which the files will be served by your instance.",[14,41863,41864],{},[638,41865],{"alt":41866,"dataZoomable":55,"src":41867},"static-assets-visibility-selector.png","\u002Fdocs\u002Fuser\u002Fimages\u002Fstatic-assets-visibility-selector.png",[14,41869,41870],{},[638,41871],{"alt":41872,"dataZoomable":55,"src":41873},"static-assets-select-static-path.png","\u002Fdocs\u002Fuser\u002Fimages\u002Fstatic-assets-select-static-path.png",[14,41875,41876],{},[638,41877],{"alt":41878,"dataZoomable":55,"src":41879},"static-assets-public-visibility.png","\u002Fdocs\u002Fuser\u002Fimages\u002Fstatic-assets-public-visibility.png",[14,41881,41882],{},"Considerations:",[28,41884,41885,41888],{},[31,41886,41887],{},"Visibility and static path maps can be set on folders only.",[31,41889,41890],{},"Any change in visibility settings require an instance restart in order for the changes to take effect.",[104,41892,41894],{"id":41893},"how-to-use","How to use",[14,41896,41897],{},"The following video is a quick demonstration on how to use assets inside a FlowFuse Node RED Instance:",[41899,41900,21376,41902,41907],"video",{"controls":187,"width":41901},800,[41903,41904],"source",{"src":41905,"type":41906},"https:\u002F\u002Fwebsite-data.s3.eu-west-1.amazonaws.com\u002FAssets+Service+Demo.mp4","video\u002Fmp4","\n  Your browser does not support the video tag.\n",{"title":55,"searchDepth":77,"depth":77,"links":41909},[41910,41911,41916],{"id":41688,"depth":77,"text":41689},{"id":25,"depth":77,"text":26,"children":41912},[41913,41914,41915],{"id":4081,"depth":88,"text":4067},{"id":34661,"depth":88,"text":34662},{"id":33597,"depth":88,"text":33598},{"id":552,"depth":77,"text":17956,"children":41917},[41918,41919,41920],{"id":41777,"depth":88,"text":41778},{"id":41812,"depth":88,"text":41813},{"id":41893,"depth":88,"text":41894},{},"user\u002Fstatic-asset-service.md",{"title":41677,"description":41685},"docs\u002Fuser\u002Fstatic-asset-service","T80z1oXciP9G5SmTh1QN8GIlC4ZRLljSuhE2EsZc9ps",{"id":41927,"title":41928,"body":41929,"description":41936,"extension":329,"layout":330,"meta":41995,"navGroup":330,"navOrder":330,"navTitle":41928,"navigation":187,"originalPath":41996,"path":41997,"redirect":330,"seo":41998,"stem":41999,"updated":337,"version":338,"__hash__":42000},"docs\u002Fdocs\u002Fuser\u002Fteam\u002Findex.md","Teams",{"type":7,"value":41930,"toc":41987},[41931,41934,41937,41939,41943,41946,41956,41960,41963,41968,41972,41979,41981],[10,41932,41928],{"id":41933},"teams",[14,41935,41936],{},"Teams are groups of users that collaborate on their Node-RED applications.",[23,41938,750],{"id":749},[104,41940,41942],{"id":41941},"creating-a-new-team","Creating a New Team",[14,41944,41945],{},"Users can create new teams. Creating a team requires a name and a unique url. The user who creates the team is added as its \"owner\".",[14,41947,41948,41950,41951,41955],{},[364,41949,1798],{}," Administrators might not ",[41,41952,41954],{"href":41953},"\u002Fdocs\u002Fadmin\u002Fintroduction#admin-settings","allow all users to create teams",". FlowFuse Cloud does allow any user to create a new team.",[104,41957,41959],{"id":41958},"adding-team-members","Adding Team Members",[14,41961,41962],{},"Each user with \"owner\" permissions on a team can invite new members on the \"Members\" tab of the team page. Invitations can be sent to existing users on the platform by their username, or via email. Users have up to 7 days to accept, or decline, the invitation before it will expire.",[14,41964,41965,41966,273],{},"When inviting a member, you can assign them an initial role (Owner, Member, Viewer, or Dashboard Only). This role can be changed later by team owners. For more details on managing roles, see ",[41,41967,39675],{"href":40690},[104,41969,41971],{"id":41970},"removing-team-members","Removing Team Members",[14,41973,41974,41975,41978],{},"Owners can remove a member from a team by clicking the dropdown menu next to the username and selecting ",[18,41976,41977],{},"Remove from team",". The team member will no longer have access to any team data.",[23,41980,39675],{"id":39680},[14,41982,41983,41984,273],{},"FlowFuse uses role-based access control to manage what users can do within teams and applications. For detailed information about roles, permissions, and access control at both team and application levels, see the ",[41,41985,41986],{"href":40690},"Role-Based Access Control documentation",{"title":55,"searchDepth":77,"depth":77,"links":41988},[41989,41994],{"id":749,"depth":77,"text":750,"children":41990},[41991,41992,41993],{"id":41941,"depth":88,"text":41942},{"id":41958,"depth":88,"text":41959},{"id":41970,"depth":88,"text":41971},{"id":39680,"depth":77,"text":39675},{},"user\u002Fteam\u002FREADME.md","\u002Fdocs\u002Fuser\u002Fteam",{"title":41928,"description":41936},"docs\u002Fuser\u002Fteam\u002Findex","zI7ioM6DvKnUSVKP_lMrBtp4Ccu_KyafmrZM_RlP0w0",{"id":42002,"title":42003,"body":42004,"description":42011,"extension":329,"layout":330,"meta":42109,"navGroup":330,"navOrder":330,"navTitle":1008,"navigation":187,"originalPath":42110,"path":1014,"redirect":330,"seo":42111,"stem":42112,"updated":337,"version":338,"__hash__":42113},"docs\u002Fdocs\u002Fuser\u002Fteambroker.md","Getting Started with Team Broker",{"type":7,"value":42005,"toc":42104},[42006,42009,42012,42015,42019,42025,42028,42032,42039,42046,42050,42054,42059,42073,42076,42082,42087,42094,42096,42100],[10,42007,42003],{"id":42008},"getting-started-with-team-broker",[14,42010,42011],{},"When FlowFuse is deployed with an Enterprise license from v2.11.0 onwards comes with the option to enable a MQTT broker for each Team.",[14,42013,42014],{},"This is a single shared MQTT broker, but each team has their own separate topic space and the ability to provision credentials for clients.",[23,42016,42018],{"id":42017},"foreword","Foreword",[14,42020,42021,42022,42024],{},"FlowFuse offers zero config MQTT integration with the Team Broker via the ",[41,42023,18401],{"href":18400}," that greatly simplifies the whole process by removing the need for manual configuration.",[14,42026,42027],{},"If you wish to continue using traditional MQTT clients, the below sections will guide you through the process of creating clients and connecting to the broker.",[23,42029,42031],{"id":42030},"creating-clients","Creating Clients",[14,42033,42034,42035,4494,42037,273],{},"When creating clients you can specify a username, it will prepended to the the Team's id e.g.  ",[18,42036,4493],{},[18,42038,4497],{},[14,42040,42041,42042,273],{},"This username should also be used as the MQTT Client ID in order to connect to the broker. Examples of how to do this are in the ",[41,42043,42045],{"href":42044},"#connecting-to-the-broker","next section",[14,42047,42048],{},[638,42049],{"alt":4503,"src":4504},[23,42051,42053],{"id":42052},"connecting-to-the-broker","Connecting to the Broker",[14,42055,42056,42057,4467],{},"The broker for FlowFuse Cloud is available on ",[18,42058,4466],{},[28,42060,42061,42065,42069],{},[31,42062,4472,42063],{},[18,42064,4475],{},[31,42066,4478,42067],{},[18,42068,4481],{},[31,42070,4484,42071],{},[18,42072,4487],{},[14,42074,42075],{},"For Self Hosted instances, please ask your Administrator for hostname and ports.",[14,42077,42078,42079],{},"You can connect to the broker using any MQTT client, for example ",[18,42080,42081],{},"mosquitto_sub",[50,42083,42085],{"className":42084,"code":4511,"language":3920},[3918],[18,42086,4511],{"__ignoreMap":55},[14,42088,42089,42090,42093],{},"Please note that username ",[364,42091,42092],{},"must"," also be used for the client id to connect to the team broker. This does mean that each\nusername\u002Fpassword can only be used with a single MQTT client at a time.",[14,42095,4516],{},[14,42097,42098],{},[638,42099],{"alt":4521,"src":4522},[14,42101,42102],{},[638,42103],{"alt":4527,"src":4528},{"title":55,"searchDepth":77,"depth":77,"links":42105},[42106,42107,42108],{"id":42017,"depth":77,"text":42018},{"id":42030,"depth":77,"text":42031},{"id":42052,"depth":77,"text":42053},{},"user\u002Fteambroker.md",{"title":42003,"description":42011},"docs\u002Fuser\u002Fteambroker","5dNuEnVz0uaszyHxyAWvmsn74XLjat5hXn9ZG1a05Q4",{"id":42115,"title":42116,"body":42117,"description":42124,"extension":329,"layout":330,"meta":42205,"navGroup":330,"navOrder":330,"navTitle":42116,"navigation":187,"originalPath":42206,"path":42207,"redirect":330,"seo":42208,"stem":42209,"updated":337,"version":338,"__hash__":42210},"docs\u002Fdocs\u002Fuser\u002Fuser-settings.md","User Settings",{"type":7,"value":42118,"toc":42196},[42119,42122,42125,42130,42133,42136,42152,42155,42157,42160,42162,42166,42169,42173,42176,42183,42187],[10,42120,42116],{"id":42121},"user-settings",[14,42123,42124],{},"Access the FlowFuse User settings by clicking on your username in the top right corner.",[14,42126,42127],{},[638,42128],{"alt":42116,"src":42129},"\u002Fdocs\u002Fuser\u002Fimages\u002Fuser-settings.png",[23,42131,17558],{"id":42132},"settings",[14,42134,42135],{},"In the Settings section, you can modify various characteristics of your personal user account:",[28,42137,42138,42142,42146],{},[31,42139,42140],{},[364,42141,1548],{},[31,42143,42144],{},[364,42145,15605],{},[31,42147,42148,42151],{},[364,42149,42150],{},"E-Mail"," (modifiable only if SSO is disabled)",[14,42153,42154],{},"Additionally, you can select a default Team, especially useful if your account is associated with multiple Teams.",[23,42156,41928],{"id":41933},[14,42158,42159],{},"The Teams tab provides an overview of all teams associated with your User account. Here, you can also view and respond to open invitations.",[23,42161,38287],{"id":38401},[104,42163,42165],{"id":42164},"change-password","Change Password",[14,42167,42168],{},"Under the Security section, you have the option to change your password. For this, you need to enter your current and new password.",[104,42170,42172],{"id":42171},"two-factor-authentication","Two-Factor Authentication",[14,42174,42175],{},"Two-factor authentication adds an extra layer of security to your account. It requires a second form of identification when logging in. Note that when signing in via your SSO provider, you will not be prompted for a two-factor authentication code.",[14,42177,42178,42179,42182],{},"To set up Two-factor authentication, click on ",[18,42180,42181],{},"Enable two factor authentication"," and follow the instructions.",[104,42184,42186],{"id":42185},"access-tokens","Access Tokens",[14,42188,42189,42190,42195],{},"Access Tokens are useful for interacting with ",[41,42191,42194],{"href":42192,"rel":42193},"https:\u002F\u002Fflowfuse.com\u002Fdocs\u002Fapi\u002F",[831],"FlowFuse APIs",". You can set these tokens to have a limited or unlimited lifespan. Tokens can be revoked at any time by removing them from your account. Remember, tokens with an expiry date will automatically delete upon reaching that date. It's important to note that the token value is only displayed once, at the time of creation. There is no way to retrieve the token value after this point.",{"title":55,"searchDepth":77,"depth":77,"links":42197},[42198,42199,42200],{"id":42132,"depth":77,"text":17558},{"id":41933,"depth":77,"text":41928},{"id":38401,"depth":77,"text":38287,"children":42201},[42202,42203,42204],{"id":42164,"depth":88,"text":42165},{"id":42171,"depth":88,"text":42172},{"id":42185,"depth":88,"text":42186},{},"user\u002Fuser-settings.md","\u002Fdocs\u002Fuser\u002Fuser-settings",{"title":42116,"description":42124},"docs\u002Fuser\u002Fuser-settings","oAQB6FzAobxaXo6rAxjfPFD_QWICyWNmw34RwfSKYW0",1781119784273]