[{"data":1,"prerenderedAt":2871},["ShallowReactive",2],{"docs-\u002Fdocs\u002Fcontribute\u002Fintroduction\u002F":3,"featureCatalog":2031,"docs-nav":2426},{"id":4,"title":5,"body":6,"description":16,"extension":2020,"layout":2021,"meta":2022,"navGroup":2021,"navGroupOrder":2021,"navOrder":203,"navTitle":2023,"navigation":857,"originalPath":2024,"path":2025,"redirect":2021,"seo":2026,"stem":2027,"updated":2028,"version":2029,"__hash__":2030},"docs\u002Fdocs\u002Fcontribute\u002Fintroduction.md","Contributing to FlowFuse",{"type":7,"value":8,"toc":1993},"minimark",[9,13,17,20,25,28,64,68,79,171,175,178,187,190,245,248,255,342,350,354,361,514,519,564,567,570,577,594,601,606,609,623,626,638,641,668,671,693,700,703,716,730,733,741,751,769,772,783,786,789,809,813,816,826,829,1029,1042,1067,1074,1078,1081,1088,1091,1094,1097,1113,1116,1123,1126,1130,1133,1165,1170,1173,1306,1310,1320,1359,1362,1365,1389,1392,1400,1402,1962,1989],[10,11,5],"h1",{"id":12},"contributing-to-flowfuse",[14,15,16],"p",{},"This guide will help you get setup to contribute to the FlowFuse project.",[14,18,19],{},"The core of the FlowFuse platform is available under the Apache-2.0 license and\nwe welcome contributions from the community.",[21,22,24],"h3",{"id":23},"software-requirements","Software Requirements",[14,26,27],{},"This guide assumes you have a working development environment including:",[29,30,31,35,61],"ul",{},[32,33,34],"li",{},"Node.js 18\u002F20",[32,36,37,38],{},"Platform build tools\n",[29,39,40,47,53],{},[32,41,42,43],{},"Linux: ",[44,45,46],"code",{},"apt-get install build-essential",[32,48,49,50],{},"MacOS: ",[44,51,52],{},"xcode-select --install",[32,54,55,56],{},"Windows: installed as part of the official node.js installer\n",[29,57,58],{},[32,59,60],{},"☑️ Automatically install the necessary tools must be checked",[32,62,63],{},"Git",[21,65,67],{"id":66},"project-repositories","Project Repositories",[14,69,70,71,78],{},"There are a number of repositories under the ",[72,73,77],"a",{"href":74,"rel":75},"https:\u002F\u002Fgithub.com\u002FFlowFuse",[76],"nofollow","FlowFuse GitHub organisation","\nthat make up the platform.",[80,81,82,95],"table",{},[83,84,85],"thead",{},[86,87,88,92],"tr",{},[89,90,91],"th",{},"Repository",[89,93,94],{},"Description",[96,97,98,111,123,135,147,159],"tbody",{},[86,99,100,108],{},[101,102,103],"td",{},[72,104,107],{"href":105,"rel":106},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse",[76],"FlowFuse",[101,109,110],{},"This is the core of the platform.",[86,112,113,120],{},[101,114,115],{},[72,116,119],{"href":117,"rel":118},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Finstaller",[76],"installer",[101,121,122],{},"The installer for the platform",[86,124,125,132],{},[101,126,127],{},[72,128,131],{"href":129,"rel":130},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdriver-localfs",[76],"driver-localfs",[101,133,134],{},"The LocalFS driver. This deploys instances to the local system.",[86,136,137,144],{},[101,138,139],{},[72,140,143],{"href":141,"rel":142},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdriver-docker",[76],"driver-docker",[101,145,146],{},"The Docker driver. This deploys instances as containers in a Docker-managed environment.",[86,148,149,156],{},[101,150,151],{},[72,152,155],{"href":153,"rel":154},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdriver-k8s",[76],"driver-k8s",[101,157,158],{},"The Kubernetes driver. This deploys instances as containers in a Kubernetes-managed environment.",[86,160,161,168],{},[101,162,163],{},[72,164,167],{"href":165,"rel":166},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnr-launcher",[76],"nr-launcher",[101,169,170],{},"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.",[21,172,174],{"id":173},"setting-up-a-development-environment","Setting Up A Development Environment",[14,176,177],{},"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,179,180,181,186],{},"To make it easier, you can use the ",[72,182,185],{"href":183,"rel":184},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdev-env",[76],"FlowFuse Development Environment"," project to get set up.",[14,188,189],{},"The following steps will get your development environment setup in no time:",[191,192,197],"pre",{"className":193,"code":194,"language":195,"meta":196,"style":196},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","git clone https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdev-env.git\ncd dev-env\nnpm install\nnpm run init\n","bash","",[44,198,199,215,225,234],{"__ignoreMap":196},[200,201,204,208,212],"span",{"class":202,"line":203},"line",1,[200,205,207],{"class":206},"sBMFI","git",[200,209,211],{"class":210},"sfazB"," clone",[200,213,214],{"class":210}," https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdev-env.git\n",[200,216,218,222],{"class":202,"line":217},2,[200,219,221],{"class":220},"s2Zo4","cd",[200,223,224],{"class":210}," dev-env\n",[200,226,228,231],{"class":202,"line":227},3,[200,229,230],{"class":206},"npm",[200,232,233],{"class":210}," install\n",[200,235,237,239,242],{"class":202,"line":236},4,[200,238,230],{"class":206},[200,240,241],{"class":210}," run",[200,243,244],{"class":210}," init\n",[14,246,247],{},"This clones all of the main project repositories, installs their dependencies and builds\nthe repositories that need it.",[14,249,250,251,254],{},"All of the repositories are cloned under the ",[44,252,253],{},"packages"," directory:",[191,256,260],{"className":257,"code":258,"language":259,"meta":196,"style":196},"language-txt shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","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","txt",[44,261,262,267,272,277,282,288,294,300,306,312,318,324,330,336],{"__ignoreMap":196},[200,263,264],{"class":202,"line":203},[200,265,266],{},"dev-env\n",[200,268,269],{"class":202,"line":217},[200,270,271],{},"└── packages\n",[200,273,274],{"class":202,"line":227},[200,275,276],{},"    ├── device-agent\n",[200,278,279],{"class":202,"line":236},[200,280,281],{},"    ├── docker-compose\n",[200,283,285],{"class":202,"line":284},5,[200,286,287],{},"    ├── driver-docker\n",[200,289,291],{"class":202,"line":290},6,[200,292,293],{},"    ├── driver-k8s\n",[200,295,297],{"class":202,"line":296},7,[200,298,299],{},"    ├── driver-localfs\n",[200,301,303],{"class":202,"line":302},8,[200,304,305],{},"    ├── file-server\n",[200,307,309],{"class":202,"line":308},9,[200,310,311],{},"    ├── flowfuse\n",[200,313,315],{"class":202,"line":314},10,[200,316,317],{},"    ├── helm\n",[200,319,321],{"class":202,"line":320},11,[200,322,323],{},"    ├── installer\n",[200,325,327],{"class":202,"line":326},12,[200,328,329],{},"    ├── nr-file-nodes\n",[200,331,333],{"class":202,"line":332},13,[200,334,335],{},"    ├── nr-launcher\n",[200,337,339],{"class":202,"line":338},14,[200,340,341],{},"    └── nr-project-nodes\n",[14,343,344,345,349],{},"More details on using the FlowForge Development Environment are available in its\n",[72,346,348],{"href":183,"rel":347},[76],"documentation",".",[21,351,353],{"id":352},"flowfuse-code-structure","FlowFuse Code Structure",[14,355,356,357,360],{},"The ",[44,358,359],{},"FlowFuse\u002Fflowfuse"," repository is the core of the platform and where you'll\nlikely want to begin.",[191,362,364],{"className":257,"code":363,"language":259,"meta":196,"style":196},".\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",[44,365,366,371,376,381,386,391,396,401,406,411,416,421,426,431,436,442,448,454,460,466,472,478,484,490,496,502,508],{"__ignoreMap":196},[200,367,368],{"class":202,"line":203},[200,369,370],{},".\n",[200,372,373],{"class":202,"line":217},[200,374,375],{},"├── bin\n",[200,377,378],{"class":202,"line":227},[200,379,380],{},"├── config               - build config files\n",[200,382,383],{"class":202,"line":236},[200,384,385],{},"├── docs\n",[200,387,388],{"class":202,"line":284},[200,389,390],{},"├── etc                  - FlowFuse platform configuration files\n",[200,392,393],{"class":202,"line":290},[200,394,395],{},"├── forge                - Platform core code\n",[200,397,398],{"class":202,"line":296},[200,399,400],{},"│   ├── config\n",[200,402,403],{"class":202,"line":302},[200,404,405],{},"│   ├── containers\n",[200,407,408],{"class":202,"line":308},[200,409,410],{},"│   ├── db\n",[200,412,413],{"class":202,"line":314},[200,414,415],{},"│   ├── ee\n",[200,417,418],{"class":202,"line":320},[200,419,420],{},"│   ├── lib\n",[200,422,423],{"class":202,"line":326},[200,424,425],{},"│   ├── licensing\n",[200,427,428],{"class":202,"line":332},[200,429,430],{},"│   ├── monitor\n",[200,432,433],{"class":202,"line":338},[200,434,435],{},"│   ├── postoffice\n",[200,437,439],{"class":202,"line":438},15,[200,440,441],{},"│   ├── routes\n",[200,443,445],{"class":202,"line":444},16,[200,446,447],{},"│   └── settings\n",[200,449,451],{"class":202,"line":450},17,[200,452,453],{},"├── frontend             - Frontend code\n",[200,455,457],{"class":202,"line":456},18,[200,458,459],{},"│   ├── dist             - build output - created by `npm run build`\n",[200,461,463],{"class":202,"line":462},19,[200,464,465],{},"│   ├── public           - static assets\n",[200,467,469],{"class":202,"line":468},20,[200,470,471],{},"│   └── src              - vue src\n",[200,473,475],{"class":202,"line":474},21,[200,476,477],{},"│       ├── api\n",[200,479,481],{"class":202,"line":480},22,[200,482,483],{},"│       ├── components\n",[200,485,487],{"class":202,"line":486},23,[200,488,489],{},"│       ├── pages\n",[200,491,493],{"class":202,"line":492},24,[200,494,495],{},"│       ├── routes\n",[200,497,499],{"class":202,"line":498},25,[200,500,501],{},"│       └── store\n",[200,503,505],{"class":202,"line":504},26,[200,506,507],{},"├── test                 - tests for FlowFuse\n",[200,509,511],{"class":202,"line":510},27,[200,512,513],{},"└── var                  - where the database and localfs project directories are created\n",[515,516,518],"h2",{"id":517},"development-setup","Development Setup",[520,521,522,528,534,540,546,552,558],"ol",{},[32,523,524],{},[72,525,527],{"href":526},"#create-a-stack","Create a Stack",[32,529,530],{},[72,531,533],{"href":532},"#running-flowfuse","Running FlowFuse",[32,535,536],{},[72,537,539],{"href":538},"#configuring-flowfuse","Configuring FlowFuse",[32,541,542],{},[72,543,545],{"href":544},"#mocking-email","Mocking email",[32,547,548],{},[72,549,551],{"href":550},"#testing","Testing",[32,553,554],{},[72,555,557],{"href":556},"#vscode-tips","VSCode Tips",[32,559,560],{},[72,561,563],{"href":562},"\u002Fdocs\u002Fcontribute\u002Fteam-broker","Team Broker",[21,565,527],{"id":566},"create-a-stack",[14,568,569],{},"You will need to setup the version(s) of Node-RED you want to use in your stacks.",[14,571,572,573,576],{},"From the ",[44,574,575],{},"flowfuse"," directory run",[191,578,580],{"className":193,"code":579,"language":195,"meta":196,"style":196},"npm run install-stack --vers=3.1.9\n",[44,581,582],{"__ignoreMap":196},[200,583,584,586,588,591],{"class":202,"line":203},[200,585,230],{"class":206},[200,587,241],{"class":210},[200,589,590],{"class":210}," install-stack",[200,592,593],{"class":210}," --vers=3.1.9\n",[14,595,596,597,600],{},"Where ",[44,598,599],{},"3.1.9"," is the version of Node-RED you want to use in the stack.",[602,603,605],"h4",{"id":604},"working-with-local-nodes","Working with Local Nodes",[14,607,608],{},"If you want to test local, in-development nodes in FlowFuse, you can create a dedicated stack for this purpose.",[191,610,611],{"className":193,"code":579,"language":195,"meta":196,"style":196},[44,612,613],{"__ignoreMap":196},[200,614,615,617,619,621],{"class":202,"line":203},[200,616,230],{"class":206},[200,618,241],{"class":210},[200,620,590],{"class":210},[200,622,593],{"class":210},[14,624,625],{},"Navigate to the stacks directory:",[191,627,629],{"className":193,"code":628,"language":195,"meta":196,"style":196},"cd \u002Fvar\u002Fstacks\n",[44,630,631],{"__ignoreMap":196},[200,632,633,635],{"class":202,"line":203},[200,634,221],{"class":220},[200,636,637],{"class":210}," \u002Fvar\u002Fstacks\n",[14,639,640],{},"Rename the directory to something more appropriate:",[191,642,644],{"className":193,"code":643,"language":195,"meta":196,"style":196},"mv \"3.1.9\" \"3.1.9-local\"\n",[44,645,646],{"__ignoreMap":196},[200,647,648,651,655,657,660,662,665],{"class":202,"line":203},[200,649,650],{"class":206},"mv",[200,652,654],{"class":653},"sMK4o"," \"",[200,656,599],{"class":210},[200,658,659],{"class":653},"\"",[200,661,654],{"class":653},[200,663,664],{"class":210},"3.1.9-local",[200,666,667],{"class":653},"\"\n",[14,669,670],{},"Install your local repository directly into the stack:",[191,672,674],{"className":193,"code":673,"language":195,"meta":196,"style":196},"cd 3.1.9-local\nnpm install \u002Fpath\u002Fto\u002Fyour\u002Fnodes-repo\n",[44,675,676,683],{"__ignoreMap":196},[200,677,678,680],{"class":202,"line":203},[200,679,221],{"class":220},[200,681,682],{"class":210}," 3.1.9-local\n",[200,684,685,687,690],{"class":202,"line":217},[200,686,230],{"class":206},[200,688,689],{"class":210}," install",[200,691,692],{"class":210}," \u002Fpath\u002Fto\u002Fyour\u002Fnodes-repo\n",[14,694,695,696,349],{},"With your stack created from the terminal, you can now add it via the FlowFuse Admin UI - see ",[72,697,699],{"href":698},"\u002Fdocs\u002Fadmin\u002Fintroduction\u002F#create-new-stack","Managing Stacks",[21,701,533],{"id":702},"running-flowfuse",[14,704,705,706,708,709,712,713,715],{},"A number of ",[44,707,230],{}," tasks are defined in the ",[44,710,711],{},"package.json"," file of this repository.\nTo get started from the ",[44,714,575],{}," directory use:",[191,717,719],{"className":193,"code":718,"language":195,"meta":196,"style":196},"npm run serve\n",[44,720,721],{"__ignoreMap":196},[200,722,723,725,727],{"class":202,"line":203},[200,724,230],{"class":206},[200,726,241],{"class":210},[200,728,729],{"class":210}," serve\n",[14,731,732],{},"This does a couple of things in parallel:",[29,734,735,738],{},[32,736,737],{},"Starts the core FlowFuse application and watches the source code for any\nchanges - triggering a restart if needed.",[32,739,740],{},"Builds the frontend application using WebPack and watches for any changes - triggering a rebuild as needed.",[14,742,743,744,747,748,349],{},"When running like this, the ",[44,745,746],{},"NODE_ENV"," environment variable gets set to ",[44,749,750],{},"development",[14,752,753,757,758,761,762,765,766,349],{},[754,755,756],"em",{},"Note",": if you have not used the ",[72,759,185],{"href":760},"#setting-up-a-development-environment",", then you will need to run ",[44,763,764],{},"npm run build","\nto build the platform before you can use ",[44,767,768],{},"npm run serve",[21,770,539],{"id":771},"configuring-flowfuse",[14,773,774,775,778,779,782],{},"When running in development mode, the core app will use ",[44,776,777],{},"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 ",[44,780,781],{},"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.",[21,784,545],{"id":785},"mocking-email",[14,787,788],{},"If you are developing locally and need to enable external email sending, you can either:",[29,790,791,798],{},[32,792,793,794],{},"Setup a local test SMTP server. For example, the Nodemailer project provides a\nuseful app that does the job: ",[72,795,796],{"href":796,"rel":797},"https:\u002F\u002Fgithub.com\u002Fnodemailer\u002Fnodemailer-app\u002Freleases",[76],[32,799,800,801,804,805,808],{},"Alternatively, set the ",[44,802,803],{},"email.debug"," option to ",[44,806,807],{},"true"," in your configuration file\nand the app will print all emails to its log.",[21,810,812],{"id":811},"configuring-billing","Configuring billing",[14,814,815],{},"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,817,356,818,821,822,825],{},[754,819,820],{},"development-only"," EE licence is provided in ",[44,823,824],{},"flowfuse\u002Fforge\u002Flicensing\u002Findex.js",". This\nlicence is not valid for production usage.",[14,827,828],{},"For FlowForge Inc. employees the configuration is provided in 1Password as 'Stripe Testing Configuration'.",[191,830,834],{"className":831,"code":832,"language":833,"meta":196,"style":196},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","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","yaml",[44,835,836,853,859,867,874,885,896,907,918,929,940,951,962,973,983,990,997,1008,1019],{"__ignoreMap":196},[200,837,838,842,845,849],{"class":202,"line":203},[200,839,841],{"class":840},"swJcz","license",[200,843,844],{"class":653},":",[200,846,848],{"class":847},"s7zQu"," *",[200,850,852],{"class":851},"sTEyZ","**\n",[200,854,855],{"class":202,"line":217},[200,856,858],{"emptyLinePlaceholder":857},true,"\n",[200,860,861,864],{"class":202,"line":227},[200,862,863],{"class":840},"billing",[200,865,866],{"class":653},":\n",[200,868,869,872],{"class":202,"line":236},[200,870,871],{"class":840},"  stripe",[200,873,866],{"class":653},[200,875,876,879,881,883],{"class":202,"line":284},[200,877,878],{"class":840},"    key",[200,880,844],{"class":653},[200,882,848],{"class":847},[200,884,852],{"class":851},[200,886,887,890,892,894],{"class":202,"line":290},[200,888,889],{"class":840},"    wh_secret",[200,891,844],{"class":653},[200,893,848],{"class":847},[200,895,852],{"class":851},[200,897,898,901,903,905],{"class":202,"line":296},[200,899,900],{"class":840},"    team_price",[200,902,844],{"class":653},[200,904,848],{"class":847},[200,906,852],{"class":851},[200,908,909,912,914,916],{"class":202,"line":302},[200,910,911],{"class":840},"    team_product",[200,913,844],{"class":653},[200,915,848],{"class":847},[200,917,852],{"class":851},[200,919,920,923,925,927],{"class":202,"line":308},[200,921,922],{"class":840},"    project_price",[200,924,844],{"class":653},[200,926,848],{"class":847},[200,928,852],{"class":851},[200,930,931,934,936,938],{"class":202,"line":314},[200,932,933],{"class":840},"    project_product",[200,935,844],{"class":653},[200,937,848],{"class":847},[200,939,852],{"class":851},[200,941,942,945,947,949],{"class":202,"line":320},[200,943,944],{"class":840},"    device_price",[200,946,844],{"class":653},[200,948,848],{"class":847},[200,950,852],{"class":851},[200,952,953,956,958,960],{"class":202,"line":326},[200,954,955],{"class":840},"    device_product",[200,957,844],{"class":653},[200,959,848],{"class":847},[200,961,852],{"class":851},[200,963,964,967,969],{"class":202,"line":332},[200,965,966],{"class":840},"    deviceCost",[200,968,844],{"class":653},[200,970,972],{"class":971},"sbssI"," 10\n",[200,974,975,978,980],{"class":202,"line":338},[200,976,977],{"class":840},"    new_customer_free_credit",[200,979,844],{"class":653},[200,981,982],{"class":971}," 1000\n",[200,984,985,988],{"class":202,"line":438},[200,986,987],{"class":840},"    teams",[200,989,866],{"class":653},[200,991,992,995],{"class":202,"line":444},[200,993,994],{"class":840},"      starter",[200,996,866],{"class":653},[200,998,999,1002,1004,1006],{"class":202,"line":450},[200,1000,1001],{"class":840},"        price",[200,1003,844],{"class":653},[200,1005,848],{"class":847},[200,1007,852],{"class":851},[200,1009,1010,1013,1015,1017],{"class":202,"line":456},[200,1011,1012],{"class":840},"        product",[200,1014,844],{"class":653},[200,1016,848],{"class":847},[200,1018,852],{"class":851},[200,1020,1021,1024,1026],{"class":202,"line":462},[200,1022,1023],{"class":840},"        userCost",[200,1025,844],{"class":653},[200,1027,1028],{"class":971}," 0\n",[14,1030,1031,1032,1037,1038,1041],{},"You will also need to install the ",[72,1033,1036],{"href":1034,"rel":1035},"https:\u002F\u002Fstripe.com\u002Fdocs\u002Fcli\u002F",[76],"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 ",[44,1039,1040],{},"billing.stripe.key"," from\nabove.",[191,1043,1045],{"className":193,"code":1044,"language":195,"meta":196,"style":196},"stripe listen --forward-to localhost:3000\u002Fee\u002Fbilling\u002Fcallback --api-key ***\n",[44,1046,1047],{"__ignoreMap":196},[200,1048,1049,1052,1055,1058,1061,1064],{"class":202,"line":203},[200,1050,1051],{"class":206},"stripe",[200,1053,1054],{"class":210}," listen",[200,1056,1057],{"class":210}," --forward-to",[200,1059,1060],{"class":210}," localhost:3000\u002Fee\u002Fbilling\u002Fcallback",[200,1062,1063],{"class":210}," --api-key",[200,1065,1066],{"class":851}," ***\n",[14,1068,1069,1070,1073],{},"Note that due to the way Stripe works, you will receive events for ",[754,1071,1072],{},"all"," 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.",[602,1075,1077],{"id":1076},"free-trials","Free Trials",[14,1079,1080],{},"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,1082,1083,1084,1087],{},"To enable trials, set the ",[44,1085,1086],{},"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,1089,1090],{},"The Stripe webhook forwarder must be running as the credit is handled as part of the webhook handling.",[21,1092,551],{"id":1093},"testing",[14,1095,1096],{},"Our testing philosophy follows the principle of:",[1098,1099,1100],"blockquote",{},[14,1101,1102,1103],{},"Write tests. Not too many. Mostly integration ",[1104,1105,1106],"sup",{},[72,1107,1112],{"href":1108,"ariaDescribedBy":1109,"dataFootnoteRef":196,"id":1111},"#user-content-fn-1",[1110],"footnote-label","user-content-fnref-1","1",[14,1114,1115],{},"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,1117,1118,1119,1122],{},"We use code coverage reporting as ",[754,1120,1121],{},"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,1124,1125],{},"Unit tests should provide sufficient coverage to give us confidence that a\ncomponent's behavior does not unexpectedly change.",[602,1127,1129],{"id":1128},"running-tests","Running tests",[14,1131,1132],{},"To run the tests for the project, you can use the following npm tasks:",[29,1134,1135,1141,1147,1153,1159],{},[32,1136,1137,1140],{},[44,1138,1139],{},"npm run test"," - runs the whole test suite, covering code linting, unit and systems tests.",[32,1142,1143,1146],{},[44,1144,1145],{},"npm run lint"," - runs the linting tests",[32,1148,1149,1152],{},[44,1150,1151],{},"npm run test:unit"," - runs the unit tests",[32,1154,1155,1158],{},[44,1156,1157],{},"npm run test:system"," - runs the system tests",[32,1160,1161,1164],{},[44,1162,1163],{},"npm run test:docs"," - checks the validity of links in the documentation",[1166,1167,1169],"h5",{"id":1168},"testing-against-postgresql","Testing against PostgreSQL",[14,1171,1172],{},"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:",[520,1174,1175,1214],{},[32,1176,1177,1178],{},"Ensure you have an instance of PostgreSQL running locally. For example, via docker:",[191,1179,1181],{"className":193,"code":1180,"language":195,"meta":196,"style":196},"   docker run -it -p 5432:5432 --name ff-postgres -e POSTGRES_PASSWORD=secret postgres:14\n",[44,1182,1183],{"__ignoreMap":196},[200,1184,1185,1188,1190,1193,1196,1199,1202,1205,1208,1211],{"class":202,"line":203},[200,1186,1187],{"class":206},"   docker",[200,1189,241],{"class":210},[200,1191,1192],{"class":210}," -it",[200,1194,1195],{"class":210}," -p",[200,1197,1198],{"class":210}," 5432:5432",[200,1200,1201],{"class":210}," --name",[200,1203,1204],{"class":210}," ff-postgres",[200,1206,1207],{"class":210}," -e",[200,1209,1210],{"class":210}," POSTGRES_PASSWORD=secret",[200,1212,1213],{"class":210}," postgres:14\n",[32,1215,1216,1217,1237,1240,1241],{},"Enable PostgreSQL mode by setting the following environment variable:",[191,1218,1220],{"className":193,"code":1219,"language":195,"meta":196,"style":196},"   export FF_TEST_DB_POSTGRES=true\n",[44,1221,1222],{"__ignoreMap":196},[200,1223,1224,1228,1231,1234],{"class":202,"line":203},[200,1225,1227],{"class":1226},"spNyl","   export",[200,1229,1230],{"class":851}," FF_TEST_DB_POSTGRES",[200,1232,1233],{"class":653},"=",[200,1235,1236],{"class":851},"true\n",[1238,1239],"br",{},"The database connection can be set using the following env vars (default values shown)",[191,1242,1244],{"className":193,"code":1243,"language":195,"meta":196,"style":196},"   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",[44,1245,1246,1258,1270,1282,1294],{"__ignoreMap":196},[200,1247,1248,1250,1253,1255],{"class":202,"line":203},[200,1249,1227],{"class":1226},[200,1251,1252],{"class":851}," FF_TEST_DB_POSTGRES_HOST",[200,1254,1233],{"class":653},[200,1256,1257],{"class":851},"localhost\n",[200,1259,1260,1262,1265,1267],{"class":202,"line":217},[200,1261,1227],{"class":1226},[200,1263,1264],{"class":851}," FF_TEST_DB_POSTGRES_PORT",[200,1266,1233],{"class":653},[200,1268,1269],{"class":971},"5432\n",[200,1271,1272,1274,1277,1279],{"class":202,"line":227},[200,1273,1227],{"class":1226},[200,1275,1276],{"class":851}," FF_TEST_DB_POSTGRES_USER",[200,1278,1233],{"class":653},[200,1280,1281],{"class":851},"postgres\n",[200,1283,1284,1286,1289,1291],{"class":202,"line":236},[200,1285,1227],{"class":1226},[200,1287,1288],{"class":851}," FF_TEST_DB_POSTGRES_PASSWORD",[200,1290,1233],{"class":653},[200,1292,1293],{"class":851},"secret\n",[200,1295,1296,1298,1301,1303],{"class":202,"line":284},[200,1297,1227],{"class":1226},[200,1299,1300],{"class":851}," FF_TEST_DB_POSTGRES_DATABASE",[200,1302,1233],{"class":653},[200,1304,1305],{"class":851},"flowforge_test\n",[602,1307,1309],{"id":1308},"reporting-code-coverage","Reporting code coverage",[14,1311,356,1312,1315,1316,1319],{},[44,1313,1314],{},"test:*"," tasks have corresponding code coverage tasks. These tasks run the\ntests using ",[44,1317,1318],{},"nyc"," to generate code coverage information.",[29,1321,1322,1332,1342,1350],{},[32,1323,1324,1327,1328,1331],{},[44,1325,1326],{},"npm run cover"," - runs the whole test suite (excluding linting) with code\ncoverage enabled and generates a report (via the ",[44,1329,1330],{},"cover:report"," task)",[32,1333,1334,1337,1338,1341],{},[44,1335,1336],{},"npm run cover:unit"," - runs the unit tests with code coverage enabled. It\ndoes ",[754,1339,1340],{},"not"," generate the report.",[32,1343,1344,1347,1348,1341],{},[44,1345,1346],{},"npm run cover:system"," - runs the system tests with code coverage enabled. It\ndoes ",[754,1349,1340],{},[32,1351,1352,1355,1356],{},[44,1353,1354],{},"npm run cover:report"," - generates a report of the code coverage. This is\nprinted to the console and generates a browsable HTML copy under ",[44,1357,1358],{},"coverage\u002Findex.html",[21,1360,557],{"id":1361},"vscode-tips",[14,1363,1364],{},"To step debug in VSCode",[520,1366,1367,1374,1381],{},[32,1368,1369,1370,1373],{},"Open ",[44,1371,1372],{},"launch.json"," config and enter the JavaScript below",[32,1375,1376,1377,1380],{},"Choose ",[44,1378,1379],{},"Start-Watch"," from the \"Run and Debug\" menu",[32,1382,1383,1384,1388],{},"Press ▶️ or ",[1385,1386,1387],"kbd",{},"F5"," to start debugging",[14,1390,1391],{},"There are 2 other \"Run and Debug\" entries in the menu...",[29,1393,1394,1397],{},[32,1395,1396],{},"\"Attach by Process ID\" - this will allow you to attach to a launched driver",[32,1398,1399],{},"\"Debug Current Test\" - this will enable you to step debug a test (starts debugging the currently open test file)",[602,1401],{"id":196},[191,1403,1407],{"className":1404,"code":1405,"language":1406,"meta":196,"style":196},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\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","json",[44,1408,1409,1414,1420,1425,1430,1452,1466,1471,1492,1511,1531,1551,1565,1582,1587,1592,1596,1615,1635,1654,1667,1676,1681,1698,1702,1706,1724,1742,1762,1783,1797,1810,1822,1838,1850,1862,1874,1884,1889,1902,1919,1925,1944,1950,1956],{"__ignoreMap":196},[200,1410,1411],{"class":202,"line":203},[200,1412,1413],{"class":653},"{\n",[200,1415,1416],{"class":202,"line":217},[200,1417,1419],{"class":1418},"sHwdD","    \u002F\u002F Use IntelliSense to learn about possible attributes.\n",[200,1421,1422],{"class":202,"line":227},[200,1423,1424],{"class":1418},"    \u002F\u002F Hover to view descriptions of existing attributes.\n",[200,1426,1427],{"class":202,"line":236},[200,1428,1429],{"class":1418},"    \u002F\u002F For more information, visit: https:\u002F\u002Fgo.microsoft.com\u002Ffwlink\u002F?linkid=830387\n",[200,1431,1432,1435,1438,1440,1442,1444,1447,1449],{"class":202,"line":284},[200,1433,1434],{"class":653},"    \"",[200,1436,1437],{"class":1226},"version",[200,1439,659],{"class":653},[200,1441,844],{"class":653},[200,1443,654],{"class":653},[200,1445,1446],{"class":210},"0.2.0",[200,1448,659],{"class":653},[200,1450,1451],{"class":653},",\n",[200,1453,1454,1456,1459,1461,1463],{"class":202,"line":290},[200,1455,1434],{"class":653},[200,1457,1458],{"class":1226},"configurations",[200,1460,659],{"class":653},[200,1462,844],{"class":653},[200,1464,1465],{"class":653}," [\n",[200,1467,1468],{"class":202,"line":296},[200,1469,1470],{"class":653},"        {\n",[200,1472,1473,1476,1479,1481,1483,1485,1488,1490],{"class":202,"line":302},[200,1474,1475],{"class":653},"            \"",[200,1477,1478],{"class":206},"command",[200,1480,659],{"class":653},[200,1482,844],{"class":653},[200,1484,654],{"class":653},[200,1486,1487],{"class":210},"npm run start-watch",[200,1489,659],{"class":653},[200,1491,1451],{"class":653},[200,1493,1494,1496,1499,1501,1503,1505,1507,1509],{"class":202,"line":308},[200,1495,1475],{"class":653},[200,1497,1498],{"class":206},"name",[200,1500,659],{"class":653},[200,1502,844],{"class":653},[200,1504,654],{"class":653},[200,1506,1379],{"class":210},[200,1508,659],{"class":653},[200,1510,1451],{"class":653},[200,1512,1513,1515,1518,1520,1522,1524,1527,1529],{"class":202,"line":314},[200,1514,1475],{"class":653},[200,1516,1517],{"class":206},"request",[200,1519,659],{"class":653},[200,1521,844],{"class":653},[200,1523,654],{"class":653},[200,1525,1526],{"class":210},"launch",[200,1528,659],{"class":653},[200,1530,1451],{"class":653},[200,1532,1533,1535,1538,1540,1542,1544,1547,1549],{"class":202,"line":320},[200,1534,1475],{"class":653},[200,1536,1537],{"class":206},"type",[200,1539,659],{"class":653},[200,1541,844],{"class":653},[200,1543,654],{"class":653},[200,1545,1546],{"class":210},"node-terminal",[200,1548,659],{"class":653},[200,1550,1451],{"class":653},[200,1552,1553,1555,1558,1560,1562],{"class":202,"line":326},[200,1554,1475],{"class":653},[200,1556,1557],{"class":206},"env",[200,1559,659],{"class":653},[200,1561,844],{"class":653},[200,1563,1564],{"class":653}," {\n",[200,1566,1567,1570,1572,1574,1576,1578,1580],{"class":202,"line":332},[200,1568,1569],{"class":653},"                \"",[200,1571,746],{"class":971},[200,1573,659],{"class":653},[200,1575,844],{"class":653},[200,1577,654],{"class":653},[200,1579,750],{"class":210},[200,1581,667],{"class":653},[200,1583,1584],{"class":202,"line":338},[200,1585,1586],{"class":653},"            }\n",[200,1588,1589],{"class":202,"line":438},[200,1590,1591],{"class":653},"        },\n",[200,1593,1594],{"class":202,"line":444},[200,1595,1470],{"class":653},[200,1597,1598,1600,1602,1604,1606,1608,1611,1613],{"class":202,"line":450},[200,1599,1475],{"class":653},[200,1601,1498],{"class":206},[200,1603,659],{"class":653},[200,1605,844],{"class":653},[200,1607,654],{"class":653},[200,1609,1610],{"class":210},"Attach by Process ID",[200,1612,659],{"class":653},[200,1614,1451],{"class":653},[200,1616,1617,1619,1622,1624,1626,1628,1631,1633],{"class":202,"line":456},[200,1618,1475],{"class":653},[200,1620,1621],{"class":206},"processId",[200,1623,659],{"class":653},[200,1625,844],{"class":653},[200,1627,654],{"class":653},[200,1629,1630],{"class":210},"${command:PickProcess}",[200,1632,659],{"class":653},[200,1634,1451],{"class":653},[200,1636,1637,1639,1641,1643,1645,1647,1650,1652],{"class":202,"line":462},[200,1638,1475],{"class":653},[200,1640,1517],{"class":206},[200,1642,659],{"class":653},[200,1644,844],{"class":653},[200,1646,654],{"class":653},[200,1648,1649],{"class":210},"attach",[200,1651,659],{"class":653},[200,1653,1451],{"class":653},[200,1655,1656,1658,1661,1663,1665],{"class":202,"line":468},[200,1657,1475],{"class":653},[200,1659,1660],{"class":206},"skipFiles",[200,1662,659],{"class":653},[200,1664,844],{"class":653},[200,1666,1465],{"class":653},[200,1668,1669,1671,1674],{"class":202,"line":474},[200,1670,1569],{"class":653},[200,1672,1673],{"class":210},"\u003Cnode_internals>\u002F**",[200,1675,667],{"class":653},[200,1677,1678],{"class":202,"line":480},[200,1679,1680],{"class":653},"            ],\n",[200,1682,1683,1685,1687,1689,1691,1693,1696],{"class":202,"line":486},[200,1684,1475],{"class":653},[200,1686,1537],{"class":206},[200,1688,659],{"class":653},[200,1690,844],{"class":653},[200,1692,654],{"class":653},[200,1694,1695],{"class":210},"node",[200,1697,667],{"class":653},[200,1699,1700],{"class":202,"line":492},[200,1701,1591],{"class":653},[200,1703,1704],{"class":202,"line":498},[200,1705,1470],{"class":653},[200,1707,1708,1710,1712,1714,1716,1718,1720,1722],{"class":202,"line":504},[200,1709,1475],{"class":653},[200,1711,1537],{"class":206},[200,1713,659],{"class":653},[200,1715,844],{"class":653},[200,1717,654],{"class":653},[200,1719,1695],{"class":210},[200,1721,659],{"class":653},[200,1723,1451],{"class":653},[200,1725,1726,1728,1730,1732,1734,1736,1738,1740],{"class":202,"line":510},[200,1727,1475],{"class":653},[200,1729,1517],{"class":206},[200,1731,659],{"class":653},[200,1733,844],{"class":653},[200,1735,654],{"class":653},[200,1737,1526],{"class":210},[200,1739,659],{"class":653},[200,1741,1451],{"class":653},[200,1743,1745,1747,1749,1751,1753,1755,1758,1760],{"class":202,"line":1744},28,[200,1746,1475],{"class":653},[200,1748,1498],{"class":206},[200,1750,659],{"class":653},[200,1752,844],{"class":653},[200,1754,654],{"class":653},[200,1756,1757],{"class":210},"Debug Current Test",[200,1759,659],{"class":653},[200,1761,1451],{"class":653},[200,1763,1765,1767,1770,1772,1774,1776,1779,1781],{"class":202,"line":1764},29,[200,1766,1475],{"class":653},[200,1768,1769],{"class":206},"program",[200,1771,659],{"class":653},[200,1773,844],{"class":653},[200,1775,654],{"class":653},[200,1777,1778],{"class":210},"${workspaceFolder}\u002Fnode_modules\u002Fmocha\u002Fbin\u002F_mocha",[200,1780,659],{"class":653},[200,1782,1451],{"class":653},[200,1784,1786,1788,1791,1793,1795],{"class":202,"line":1785},30,[200,1787,1475],{"class":653},[200,1789,1790],{"class":206},"args",[200,1792,659],{"class":653},[200,1794,844],{"class":653},[200,1796,1465],{"class":653},[200,1798,1800,1803,1806,1808],{"class":202,"line":1799},31,[200,1801,1802],{"class":653},"              \"",[200,1804,1805],{"class":210},"--no-warnings",[200,1807,659],{"class":653},[200,1809,1451],{"class":653},[200,1811,1813,1815,1818,1820],{"class":202,"line":1812},32,[200,1814,1802],{"class":653},[200,1816,1817],{"class":210},"-u",[200,1819,659],{"class":653},[200,1821,1451],{"class":653},[200,1823,1825,1827,1830,1832,1835],{"class":202,"line":1824},33,[200,1826,1802],{"class":653},[200,1828,1829],{"class":210},"bdd",[200,1831,659],{"class":653},[200,1833,1834],{"class":653},",",[200,1836,1837],{"class":1418},"\u002F\u002F set to bdd, not tdd\n",[200,1839,1841,1843,1846,1848],{"class":202,"line":1840},34,[200,1842,1802],{"class":653},[200,1844,1845],{"class":210},"--timeout",[200,1847,659],{"class":653},[200,1849,1451],{"class":653},[200,1851,1853,1855,1858,1860],{"class":202,"line":1852},35,[200,1854,1802],{"class":653},[200,1856,1857],{"class":210},"999999",[200,1859,659],{"class":653},[200,1861,1451],{"class":653},[200,1863,1865,1867,1870,1872],{"class":202,"line":1864},36,[200,1866,1802],{"class":653},[200,1868,1869],{"class":210},"--colors",[200,1871,659],{"class":653},[200,1873,1451],{"class":653},[200,1875,1877,1879,1882],{"class":202,"line":1876},37,[200,1878,1802],{"class":653},[200,1880,1881],{"class":210},"${file}",[200,1883,667],{"class":653},[200,1885,1887],{"class":202,"line":1886},38,[200,1888,1680],{"class":653},[200,1890,1892,1894,1896,1898,1900],{"class":202,"line":1891},39,[200,1893,1475],{"class":653},[200,1895,1557],{"class":206},[200,1897,659],{"class":653},[200,1899,844],{"class":653},[200,1901,1564],{"class":653},[200,1903,1905,1907,1909,1911,1913,1915,1917],{"class":202,"line":1904},40,[200,1906,1569],{"class":653},[200,1908,746],{"class":971},[200,1910,659],{"class":653},[200,1912,844],{"class":653},[200,1914,654],{"class":653},[200,1916,750],{"class":210},[200,1918,667],{"class":653},[200,1920,1922],{"class":202,"line":1921},41,[200,1923,1924],{"class":653},"            },\n",[200,1926,1928,1930,1933,1935,1937,1939,1942],{"class":202,"line":1927},42,[200,1929,1475],{"class":653},[200,1931,1932],{"class":206},"internalConsoleOptions",[200,1934,659],{"class":653},[200,1936,844],{"class":653},[200,1938,654],{"class":653},[200,1940,1941],{"class":210},"openOnSessionStart",[200,1943,667],{"class":653},[200,1945,1947],{"class":202,"line":1946},43,[200,1948,1949],{"class":653},"        }\n",[200,1951,1953],{"class":202,"line":1952},44,[200,1954,1955],{"class":653},"    ]\n",[200,1957,1959],{"class":202,"line":1958},45,[200,1960,1961],{"class":653},"}\n",[1963,1964,1967,1972],"section",{"className":1965,"dataFootnotes":196},[1966],"footnotes",[515,1968,1971],{"className":1969,"id":1110},[1970],"sr-only","Footnotes",[520,1973,1974],{},[32,1975,1977,1981,1982],{"id":1976},"user-content-fn-1",[72,1978,1979],{"href":1979,"rel":1980},"https:\u002F\u002Fkentcdodds.com\u002Fblog\u002Fwrite-tests",[76]," ",[72,1983,1988],{"href":1984,"ariaLabel":1985,"className":1986,"dataFootnoteBackref":196},"#user-content-fnref-1","Back to reference 1",[1987],"data-footnote-backref","↩",[1990,1991,1992],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":196,"searchDepth":236,"depth":236,"links":1994},[1995,1996,1997,1998,1999,2019],{"id":23,"depth":227,"text":24},{"id":66,"depth":227,"text":67},{"id":173,"depth":227,"text":174},{"id":352,"depth":227,"text":353},{"id":517,"depth":217,"text":518,"children":2000},[2001,2004,2005,2006,2007,2010,2016],{"id":566,"depth":227,"text":527,"children":2002},[2003],{"id":604,"depth":236,"text":605},{"id":702,"depth":227,"text":533},{"id":771,"depth":227,"text":539},{"id":785,"depth":227,"text":545},{"id":811,"depth":227,"text":812,"children":2008},[2009],{"id":1076,"depth":236,"text":1077},{"id":1093,"depth":227,"text":551,"children":2011},[2012,2015],{"id":1128,"depth":236,"text":1129,"children":2013},[2014],{"id":1168,"depth":284,"text":1169},{"id":1308,"depth":236,"text":1309},{"id":1361,"depth":227,"text":557,"children":2017},[2018],{"id":196,"depth":236,"text":196},{"id":1110,"depth":217,"text":1971},"md",null,{},"Introduction","contribute\u002Fintroduction.md","\u002Fdocs\u002Fcontribute\u002Fintroduction",{"title":5,"description":16},"docs\u002Fcontribute\u002Fintroduction","2025-05-09 10:20:24 +0100","2.33.2","rEzxDuRwjZEU7GaFxT37ypLVHZsoffs2dgI8FuTEBO0",{"id":2032,"extension":2033,"meta":2034,"sections":2035,"stem":2424,"__hash__":2425},"featureCatalog\u002Ffeature-catalog.yml","yml",{},[2036,2087,2188,2250,2327,2406],{"id":2037,"title":2038,"features":2039},"ai-automation","AI & Automation",[2040,2050,2060,2070,2076,2082],{"id":2041,"title":2042,"description":2043,"docsLink":2044,"changelog":2045,"tiers":2049},"flowfuse-expert-ai","FlowFuse Expert AI","Build industrial apps with agents, and query the state of the factory with an agent. Adapt your current hardware and machines so agentic work can be done against them, without ripping and replacing what's already on the plant floor.","\u002Fdocs\u002Fuser\u002Fexpert\u002F",[2046],{"url":2047,"release":2048},"\u002Fchangelog\u002F2026\u002F02\u002Fff-expert-update-banner\u002F","2.28",{"edge":857,"hub":857,"fleet":857},{"id":2051,"title":2052,"description":2053,"docsLink":2054,"changelog":2055,"subfeature":857,"showOnPricing":2058,"tiers":2059},"flowfuse-expert-support-mode","Support Mode","Chat-based assistance for FlowFuse and Node-RED, including Node-RED instance management through natural language.","\u002Fdocs\u002Fuser\u002Fexpert\u002Fchat\u002F#support-mode",[2056],{"url":2057,"release":2048},"\u002Fchangelog\u002F2026\u002F02\u002Fff-expert-debug-log-context\u002F",false,{"edge":857,"hub":857,"fleet":857},{"id":2061,"title":2062,"description":2063,"docsLink":2064,"changelog":2065,"subfeature":857,"showOnPricing":2058,"tiers":2069},"flowfuse-expert-application-building","Application Building","Describe what you want to build and FlowFuse Expert assembles it on your workspace, adding tabs, wiring nodes, and configuring properties.","\u002Fdocs\u002Fuser\u002Fexpert\u002Fchat\u002F",[2066],{"url":2067,"release":2068},"\u002Fchangelog\u002F2026\u002F05\u002Fexpert-application-building\u002F","2.30",{"edge":857,"hub":857,"fleet":857},{"id":2071,"title":2072,"description":2073,"docsLink":2074,"subfeature":857,"beta":857,"showOnPricing":2058,"tiers":2075},"flowfuse-expert-insights-mode","Insights Mode","Connects FlowFuse Expert to MCP servers in your Node-RED instances, enabling real-time data queries and actions through a single chat interface.","\u002Fdocs\u002Fuser\u002Fexpert\u002Fchat\u002F#insights-mode",{"edge":857,"hub":857,"fleet":857},{"id":2077,"title":2078,"description":2079,"docsLink":2080,"tiers":2081},"mcp-servers","Agentic Operations","Expose your Node-RED flows as tools an AI agent can call directly, so agents can query the state of the factory or trigger actions without custom integration work.","\u002Fnode-red\u002Fflowfuse\u002Fmcp\u002F",{"edge":857,"hub":857,"fleet":857},{"id":2083,"title":2084,"description":2085,"tiers":2086},"onnx-integration","ONNX Integration","Run trained machine learning models directly in your flows, including on edge hardware, without sending data out to an external inference service.",{"edge":857,"hub":857,"fleet":857},{"id":2088,"title":2089,"features":2090},"build","Build",[2091,2097,2103,2113,2119,2125,2129,2135,2141,2149,2155,2159,2163,2172,2180],{"id":2092,"title":2093,"description":2094,"docsLink":2095,"tiers":2096},"edge-development","Edge Development","Develop and test Node-RED flows directly on edge devices with a remote editor proxy.","\u002Fdocs\u002Fdevice-agent\u002Fquickstart\u002F",{"edge":857,"hub":2058,"fleet":857},{"id":2098,"title":2099,"description":2100,"docsLink":2101,"tiers":2102},"private-npm-registry","Custom Node-RED Nodes","Create and manage your own private npm registry for Node-RED nodes, so you can share custom nodes across your team and devices without publishing them publicly.","\u002Fdocs\u002Fuser\u002Fcustom-npm-packages\u002F",{"edge":857,"hub":857,"fleet":857},{"id":2104,"title":2105,"description":2106,"docsLink":2107,"changelog":2108,"tiers":2112},"flowfuse-tables","FlowFuse Tables","A managed PostgreSQL database for every application, so you can store and query structured data without standing up and maintaining your own database.","\u002Fdocs\u002Fuser\u002Fff-tables\u002F",[2109],{"url":2110,"release":2111},"\u002Fchangelog\u002F2026\u002F07\u002Fexpert-tables-automation\u002F","2.33",{"edge":857,"hub":857,"fleet":857},{"id":2114,"title":2115,"description":2116,"docsLink":2117,"tiers":2118},"persistent-files","File Storage","Store and retrieve files from your Node-RED flows, with automatic replication and backup across your devices and hosted instances.","\u002Fdocs\u002Finstall\u002Ffile-storage\u002F",{"edge":857,"hub":857,"fleet":857},{"id":2120,"title":2121,"description":2122,"docsLink":2123,"showOnPricing":2058,"tiers":2124},"persistent-context","Persistent Context","In-memory values defined in a Node-RED flow persist across project restarts and upgrades.","\u002Fdocs\u002Fuser\u002Fpersistent-context\u002F",{"edge":857,"hub":857,"fleet":857},{"id":2126,"title":2127,"showOnPricing":2058,"tiers":2128},"static-assets","Static Assets",{"edge":857,"hub":857,"fleet":857},{"id":2130,"title":2131,"description":2132,"docsLink":2133,"tiers":2134},"team-library","Team Library","Set up standard nodes and flows that can be shared with all team members across your organisation.","\u002Fdocs\u002Fuser\u002Fshared-library\u002F",{"edge":857,"hub":857,"fleet":857},{"id":2136,"title":2137,"description":2138,"docsLink":2139,"tiers":2140},"personalised-multi-user-dashboards","Personalised Multi-User Dashboards","Build applications that provide unique data to each logged-in user using personalised multi-user dashboards.","https:\u002F\u002Fdashboard.flowfuse.com\u002Fuser\u002Fmulti-tenancy.html",{"edge":857,"hub":857,"fleet":857},{"id":2142,"title":2143,"description":2144,"changelog":2145,"subfeature":857,"showOnPricing":2058,"tiers":2148},"dashboards-view","Dashboards View","Browse and open every dashboard across your team from a dedicated Dashboards view, at both team and application level, without leaving FlowFuse.",[2146],{"url":2147,"release":2111},"\u002Fchangelog\u002F2026\u002F07\u002Fteam-and-application-dashboards\u002F",{"edge":857,"hub":857,"fleet":857},{"id":2150,"title":2151,"description":2152,"docsLink":2153,"tiers":2154},"blueprints-converge","Blueprints","Ready-made starting points for your apps, from cross-team Converge templates to OT and IT specific blueprints.","\u002Fdocs\u002Fuser\u002Fconcepts\u002F#blueprint",{"edge":857,"hub":857,"fleet":857},{"id":2156,"title":2157,"subfeature":857,"showOnPricing":2058,"tiers":2158},"blueprints-ot-apps","Blueprints - OT APPS",{"edge":857,"hub":2058,"fleet":857},{"id":2160,"title":2161,"subfeature":857,"showOnPricing":2058,"tiers":2162},"blueprints-it-apps","Blueprints - IT APPS",{"edge":2058,"hub":857,"fleet":2058},{"id":2164,"title":2165,"description":2166,"changelog":2167,"showOnPricing":2058,"tiers":2171},"immersive-editor-snapshots","Snapshot Details in Immersive Editor","View and manage snapshot details directly inside the immersive editor without leaving your editing session.",[2168],{"url":2169,"release":2170},"\u002Fchangelog\u002F2026\u002F03\u002Fsnapshot-detail-modal-immersive-editor\u002F","2.29",{"edge":857,"hub":857,"fleet":857},{"id":2173,"title":2174,"description":2175,"changelog":2176,"showOnPricing":2058,"tiers":2179},"immersive-editor-drawer","Customisable Immersive Editor Drawer","Pin, move, resize, or full-screen the immersive editor drawer. Your preferences are remembered between sessions.",[2177],{"url":2178,"release":2068},"\u002Fchangelog\u002F2026\u002F04\u002Fimmersive-editor-drawer\u002F",{"edge":857,"hub":857,"fleet":857},{"id":2181,"title":2182,"description":2183,"changelog":2184,"showOnPricing":2058,"tiers":2187},"embedded-editor-tab-title","Embedded Editor Browser Tab Title","The browser tab title updates to reflect the active Node-RED canvas tab when working in the embedded editor.",[2185],{"url":2186,"release":2170},"\u002Fchangelog\u002F2026\u002F03\u002Fembedded-editor-tab-title\u002F",{"edge":857,"hub":857,"fleet":857},{"id":2189,"title":2190,"features":2191},"deploy","Deploy",[2192,2198,2207,2213,2219,2225,2231,2237,2242],{"id":2193,"title":2194,"description":2195,"docsLink":2196,"tiers":2197},"hosted-instances","Cloud Instances","Run Node-RED instances managed and hosted by FlowFuse.","\u002Fdocs\u002Fuser\u002Fintroduction\u002F#creating-a-node-red-instance",{"edge":857,"hub":857,"fleet":857},{"id":2199,"title":2200,"description":2201,"docsLink":2202,"changelog":2203,"tiers":2206},"edge-devices","Edge Instances","Deploy and mange your Node-RED instances on edge PLCs and gateways, with full visibility and control from the cloud.","\u002Fdocs\u002Fdevice-agent\u002Fintroduction\u002F",[2204],{"url":2205,"release":2048},"\u002Fchangelog\u002F2026\u002F02\u002Fdevice-agent-nodejs-options\u002F",{"edge":857,"hub":2058,"fleet":857},{"id":2208,"title":2209,"description":2210,"docsLink":2211,"tiers":2212},"custom-hostnames","Custom Hostnames","Access your Node-RED application via your own domain name.","\u002Fdocs\u002Fuser\u002Fcustom-hostnames\u002F",{"edge":2058,"hub":857,"fleet":2058},{"id":2214,"title":2215,"description":2216,"docsLink":2217,"tiers":2218},"mqtt-broker","MQTT Broker","Manage and create MQTT clients to transport data for efficient messaging and communication within your applications.","\u002Fdocs\u002Fuser\u002Fteambroker\u002F",{"edge":857,"hub":2058,"fleet":857},{"id":2220,"title":2221,"description":2222,"docsLink":2223,"showOnPricing":2058,"tiers":2224},"project-nodes","Project Nodes aka seamless project comms","FlowFuse Project Nodes enable the passing of data and messages between your Node-RED projects.","\u002Fdocs\u002Fuser\u002Fprojectnodes\u002F",{"edge":857,"hub":857,"fleet":857},{"id":2226,"title":2227,"description":2228,"docsLink":2229,"tiers":2230},"devops-pipelines","DevOps Pipelines","Set up different environments for development, testing, and production Node-RED instances to support a full software delivery lifecycle.","\u002Fdocs\u002Fuser\u002Fdevops-pipelines\u002F",{"edge":857,"hub":857,"fleet":857},{"id":2232,"title":2233,"description":2234,"docsLink":2235,"tiers":2236},"git-integration","Git Integration","Back up your flows to a remote Git repository through a DevOps Pipeline. Supports GitHub and Azure DevOps repositories.","\u002Fdocs\u002Fuser\u002Fdevops-pipelines\u002F#git-repository-stage",{"edge":2058,"hub":857,"fleet":2058},{"id":2238,"title":2239,"description":2240,"docsLink":2235,"subfeature":857,"showOnPricing":2058,"tiers":2241},"git-integration-github","GitHub","Push and pull snapshots to GitHub repositories through DevOps Pipeline Git Stages.",{"edge":2058,"hub":857,"fleet":2058},{"id":2243,"title":2244,"description":2245,"docsLink":2235,"changelog":2246,"subfeature":857,"showOnPricing":2058,"tiers":2249},"git-integration-azure","Azure DevOps","Push and pull snapshots to Azure DevOps repositories through DevOps Pipeline Git Stages.",[2247],{"url":2248,"release":2170},"\u002Fchangelog\u002F2026\u002F03\u002Fazure-dev-ops-gitops\u002F",{"edge":2058,"hub":857,"fleet":2058},{"id":2251,"title":2252,"features":2253},"operate-maintain","Operate & Maintain",[2254,2260,2266,2271,2279,2283,2287,2292,2298,2304,2309,2315,2319,2323],{"id":2255,"title":2256,"description":2257,"docsLink":2258,"tiers":2259},"snapshots","Snapshots & Version History","Automatic snapshots on remote devices and hosted instances, plus a full version history timeline so you can roll back to any prior state.","\u002Fdocs\u002Fuser\u002Fsnapshots\u002F",{"edge":857,"hub":857,"fleet":857},{"id":2261,"title":2262,"description":2263,"docsLink":2264,"subfeature":857,"showOnPricing":2058,"tiers":2265},"auto-snapshot-remote","Auto Snapshot (Remote)","Automatically capture a snapshot every time a remote instance is deployed, so you always have a recoverable history of what was running on each device.","\u002Fdocs\u002Fuser\u002Fsnapshots\u002F#auto-snapshots",{"edge":857,"hub":857,"fleet":857},{"id":2267,"title":2268,"description":2269,"docsLink":2264,"subfeature":857,"showOnPricing":2058,"tiers":2270},"auto-snapshot-hosted","Auto Snapshot (Hosted)","Automatically capture a snapshot every time a hosted instance is deployed, so you always have a recoverable history of what was running.",{"edge":857,"hub":857,"fleet":857},{"id":2272,"title":2273,"description":2274,"changelog":2275,"subfeature":857,"showOnPricing":2058,"tiers":2278},"snapshot-comparison","Snapshot Comparison","Compare two snapshots side-by-side with a navigable diff view. Step through every changed, added, or deleted node and see property and code diffs.",[2276],{"url":2277,"release":2170},"\u002Fchangelog\u002F2026\u002F04\u002Fsnapshot-diff-viewer\u002F",{"edge":857,"hub":857,"fleet":857},{"id":2280,"title":2281,"subfeature":857,"showOnPricing":2058,"tiers":2282},"version-history-timeline","Version History Timeline",{"edge":857,"hub":857,"fleet":857},{"id":2284,"title":2285,"tiers":2286},"unlimited-workflow-executions","Unlimited Workflow Executions",{"edge":857,"hub":857,"fleet":857},{"id":2288,"title":2289,"description":2290,"tiers":2291},"device-fleet-updates","Device Fleet Updates","Connect to edge devices to quickly assess and update logic. Debug one device and roll out improvements to your fleet in minutes, securely without requiring full device access for your whole organisation.",{"edge":857,"hub":2058,"fleet":857},{"id":2293,"title":2294,"description":2295,"docsLink":2296,"tiers":2297},"device-group-management","Device Group Management","Logically group devices assigned to an application and integrate device groups into your DevOps Pipeline for coordinated fleet updates.","\u002Fdocs\u002Fuser\u002Fdevice-groups\u002F",{"edge":857,"hub":2058,"fleet":857},{"id":2299,"title":2300,"description":2301,"docsLink":2302,"tiers":2303},"high-availability","High Availability","Leverage horizontal scaling for reliable and scalable processing of your data through Node-RED.","\u002Fdocs\u002Fuser\u002Fhigh-availability\u002F",{"edge":2058,"hub":857,"fleet":2058},{"id":2305,"title":2306,"description":2307,"tiers":2308},"performance-monitoring","Performance Monitoring & Alerts","Track CPU, memory, and event loop performance across your instances and devices, with email alerts when something needs your attention.",{"edge":857,"hub":857,"fleet":857},{"id":2310,"title":2311,"description":2312,"docsLink":2313,"subfeature":857,"showOnPricing":2058,"tiers":2314},"instance-monitoring","Instance Monitoring","Enable alerts to be sent via email when your Node-RED instances encounter issues.","\u002Fdocs\u002Fuser\u002Finstance-settings\u002F#alerts",{"edge":857,"hub":857,"fleet":857},{"id":2316,"title":2317,"subfeature":857,"showOnPricing":2058,"tiers":2318},"email-alerts","Email Alerts",{"edge":857,"hub":857,"fleet":857},{"id":2320,"title":2321,"showOnPricing":2058,"tiers":2322},"api-debug-length-limit","API\u002FDebug Length Limit",{"edge":857,"hub":857,"fleet":857},{"id":2324,"title":2325,"tiers":2326},"protected-instances","Protected Instances",{"edge":2058,"hub":857,"fleet":2058},{"id":2328,"title":2329,"features":2330},"govern-secure","Govern and Secure",[2331,2340,2346,2352,2357,2363,2369,2375,2383,2389,2395,2401],{"id":2332,"title":2333,"description":2334,"docsLink":2335,"changelog":2336,"tiers":2339},"single-sign-on","Single Sign-On (SSO)","Configure FlowFuse to work with your own SSO provider, allowing users to access FlowFuse with a single set of login credentials.","\u002Fdocs\u002Fadmin\u002Fsso\u002F",[2337],{"url":2338,"release":2111},"\u002Fchangelog\u002F2026\u002F07\u002Fapplication-sso-groups\u002F",{"edge":857,"hub":857,"fleet":857},{"id":2341,"title":2342,"description":2343,"docsLink":2344,"tiers":2345},"two-factor-authentication","Two-Factor Authentication","Two-factor authentication adds an extra layer of security to your FlowFuse account.","\u002Fdocs\u002Fuser\u002Fuser-settings\u002F#two-factor-authentication",{"edge":857,"hub":857,"fleet":857},{"id":2347,"title":2348,"description":2349,"docsLink":2350,"tiers":2351},"certified-nodes-it","Certified Nodes - IT","IT certified node bundle includes: Redis, MQTT, HTTP Request, AI nodes (Gemini, Claude, ChatGPT, Ollama), MCP Server","\u002Fblog\u002F2025\u002F07\u002Fcertified-nodes-v2\u002F",{"edge":2058,"hub":857,"fleet":857},{"id":2353,"title":2354,"description":2355,"tiers":2356},"certified-nodes-ot","Certified OT Connections - OPC-UA, Modbus, etc.","OT certified node bundle includes: OPC-UA, Modbus TCP & RTU, RTSP, EtherNet\u002FIP, AI nodes (Gemini, Claude, ChatGPT, Ollama), MCP Server",{"edge":857,"hub":2058,"fleet":2058},{"id":2358,"title":2359,"description":2360,"docsLink":2361,"tiers":2362},"audit-log","Audit Log","Keep track of everything going on in your Node-RED instances and FlowFuse. Audit Logs provide details on what actions have taken place, when they happened, and who did them.","\u002Fdocs\u002Fuser\u002Flogs\u002F#audit-log",{"edge":857,"hub":857,"fleet":857},{"id":2364,"title":2365,"description":2366,"docsLink":2367,"tiers":2368},"role-based-access-control","Role-Based Access Control","Control who can do what at both the team and application level, from viewers to admins.","\u002Fdocs\u002Fuser\u002Frole-based-access-control\u002F",{"edge":857,"hub":857,"fleet":857},{"id":2370,"title":2371,"description":2372,"docsLink":2373,"subfeature":857,"showOnPricing":2058,"tiers":2374},"application-level-rbac","Application-Level RBAC","Fine-grained access control per application, allowing team members to have different permission levels across different applications without requiring separate teams.","\u002Fdocs\u002Fuser\u002Frole-based-access-control\u002F#application-level-rbac",{"edge":857,"hub":857,"fleet":857},{"id":2376,"title":2377,"description":2378,"changelog":2379,"subfeature":857,"showOnPricing":2058,"tiers":2382},"scoped-personal-access-tokens","Scoped Personal Access Tokens","Restrict a Personal Access Token to specific teams, limit it to read-only operations, or control whether it carries admin privileges.",[2380],{"url":2381,"release":2111},"\u002Fchangelog\u002F2026\u002F07\u002Fscoped-pats\u002F",{"edge":857,"hub":857,"fleet":857},{"id":2384,"title":2385,"description":2386,"docsLink":2387,"showOnPricing":2058,"tiers":2388},"team-members","Team Members","Invite multiple team members to collaborate on the same Node-RED flows.","\u002Fdocs\u002Fuser\u002Fteam\u002F#teams",{"edge":857,"hub":857,"fleet":857},{"id":2390,"title":2391,"description":2392,"docsLink":2393,"showOnPricing":2058,"tiers":2394},"endpoint-security","Endpoint Security","Secure HTTP endpoints for hosted Node-RED instances using FlowFuse credentials.","\u002Fdocs\u002Fuser\u002Finstance-settings\u002F#security",{"edge":857,"hub":857,"fleet":857},{"id":2396,"title":2397,"description":2398,"docsLink":2399,"tiers":2400},"baa-for-hipaa","BAA for HIPAA","FlowFuse can sign a Business Associate Agreement to ensure proper safeguarding of protected health information handled on your behalf.","\u002Fhandbook\u002Fsales\u002Fsubscription-agreement-1.5\u002F",{"edge":2058,"hub":857,"fleet":2058},{"id":2402,"title":2403,"description":2404,"tiers":2405},"sbom","Software Bill of Materials","A complete list of all software components used in your Node-RED instances and hosted applications, including version numbers and license information.",{"edge":2058,"hub":857,"fleet":2058},{"id":2407,"title":2408,"features":2409},"support","Support",[2410,2415,2419],{"id":2411,"title":2412,"docsLink":2413,"tiers":2414},"installation-support","Installation Support","\u002Fdocs\u002Finstall\u002Fintroduction\u002F#do-you-need-help-installation-service",{"edge":857,"hub":857,"fleet":857},{"id":2416,"title":2417,"showOnPricing":2058,"tiers":2418},"live-chat-support","Live Chat Support",{"edge":857,"hub":857,"fleet":857},{"id":2420,"title":2421,"docsLink":2422,"tiers":2423},"enterprise-support","Enterprise Support","\u002Fdocs\u002Fpremium-support\u002F",{"edge":857,"hub":857,"fleet":857},"feature-catalog","0AnkhTIPCECCwP9NmbTWP5HvRYx4FTSao4lG93-HaWM",[2427,2434,2453,2463],{"name":2428,"order":203,"children":2429},"FlowFuse User Manuals",[2430],{"title":2431,"path":2432,"group":2428,"groupOrder":203,"order":217,"children":2433},"FlowFuse API","\u002Fdocs\u002Fapi",[],{"name":2435,"order":236,"children":2436},"FlowFuse Self-Hosted",[2437,2441],{"title":2438,"path":2439,"group":2435,"groupOrder":236,"order":203,"children":2440},"Quick Start","\u002Fdocs\u002Fquick-start",[],{"title":2442,"path":2443,"group":2435,"groupOrder":236,"order":227,"children":2444},"Upgrading FlowFuse","\u002Fdocs\u002Fupgrade",[2445,2449],{"title":2446,"path":2447,"order":2021,"children":2448},"Installing a license","\u002Fdocs\u002Fupgrade\u002Fopen-source-to-premium",[],{"title":2450,"path":2451,"order":2021,"children":2452},"Upgrading the Node-RED version","\u002Fdocs\u002Fupgrade\u002Fnodered-version",[],{"name":2408,"order":284,"children":2454},[2455,2459],{"title":2456,"path":2457,"group":2408,"groupOrder":284,"order":227,"children":2458},"Premium Support","\u002Fdocs\u002Fpremium-support",[],{"title":2460,"path":2461,"group":2408,"groupOrder":284,"order":2021,"children":2462},"Debugging Node-RED issues","\u002Fdocs\u002Fdebugging",[],{"name":2464,"order":2021,"children":2465},"Other",[2466,2514,2525,2601,2649,2668,2741,2752],{"title":2467,"path":2468,"order":2021,"children":2469},"admin","\u002Fdocs\u002Fadmin",[2470,2474,2486,2494,2498,2502,2506,2510],{"title":2471,"path":2472,"order":203,"children":2473},"Administering FlowFuse","\u002Fdocs\u002Fadmin\u002Fintroduction",[],{"title":2475,"path":2476,"order":2021,"children":2477},"Configuring Single Sign-On (SSO)","\u002Fdocs\u002Fadmin\u002Fsso",[2478,2482],{"title":2479,"path":2480,"order":2021,"children":2481},"LDAP SSO","\u002Fdocs\u002Fadmin\u002Fsso\u002Fldap",[],{"title":2483,"path":2484,"order":2021,"children":2485},"SAML SSO","\u002Fdocs\u002Fadmin\u002Fsso\u002Fsaml",[],{"title":2487,"path":2488,"order":2021,"children":2489},"hardening","\u002Fdocs\u002Fadmin\u002Fhardening",[2490],{"title":2491,"path":2492,"order":2021,"children":2493},"Kubernetes Hardening","\u002Fdocs\u002Fadmin\u002Fhardening\u002Fkubernetes",[],{"title":2495,"path":2496,"order":2021,"children":2497},"Monitoring","\u002Fdocs\u002Fadmin\u002Fmonitoring",[],{"title":2499,"path":2500,"order":2021,"children":2501},"Observability","\u002Fdocs\u002Fadmin\u002Fobservability",[],{"title":2503,"path":2504,"order":2021,"children":2505},"Soft Launch Enablement","\u002Fdocs\u002Fadmin\u002Ffeature-flags",[],{"title":2507,"path":2508,"order":2021,"children":2509},"Telemetry","\u002Fdocs\u002Fadmin\u002Ftelemetry",[],{"title":2511,"path":2512,"order":2021,"children":2513},"User Management","\u002Fdocs\u002Fadmin\u002Fuser-management",[],{"title":2515,"path":2516,"order":2021,"children":2517},"cloud","\u002Fdocs\u002Fcloud",[2518,2521],{"title":2023,"path":2519,"order":203,"children":2520},"\u002Fdocs\u002Fcloud\u002Fintroduction",[],{"title":2522,"path":2523,"order":2021,"children":2524},"FlowFuse Cloud Billing","\u002Fdocs\u002Fcloud\u002Fbilling",[],{"title":2526,"path":2527,"order":2021,"children":2528},"contribute","\u002Fdocs\u002Fcontribute",[2529,2531,2535,2539,2543,2547,2551,2559,2595,2597],{"title":2023,"path":2025,"order":203,"children":2530},[],{"title":2532,"path":2533,"order":2021,"children":2534},"Adding Template Settings","\u002Fdocs\u002Fcontribute\u002Fadding-template-settings",[],{"title":2536,"path":2537,"order":2021,"children":2538},"API Design","\u002Fdocs\u002Fcontribute\u002Fapi-design",[],{"title":2540,"path":2541,"order":2021,"children":2542},"Creating debug stack containers","\u002Fdocs\u002Fcontribute\u002Fcreating-debug-stack-containers",[],{"title":2544,"path":2545,"order":2021,"children":2546},"Database migrations","\u002Fdocs\u002Fcontribute\u002Fdb-migrations",[],{"title":2548,"path":2549,"order":2021,"children":2550},"FlowFuse Architecture","\u002Fdocs\u002Fcontribute\u002Farchitecture",[],{"title":2552,"path":2553,"order":2021,"children":2554},"Local Install","\u002Fdocs\u002Fcontribute\u002Flocal",[2555],{"title":2556,"path":2557,"order":2021,"children":2558},"Local Stacks","\u002Fdocs\u002Fcontribute\u002Flocal\u002Fstacks",[],{"title":2560,"path":2561,"order":2021,"children":2562},"State Flows","\u002Fdocs\u002Fcontribute\u002Fworkflows",[2563,2567,2571,2575,2579,2583,2587,2591],{"title":2564,"path":2565,"order":2021,"children":2566},"Device Editor","\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fdevice-editor",[],{"title":2568,"path":2569,"order":2021,"children":2570},"Instance states","\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fproject-states",[],{"title":2572,"path":2573,"order":2021,"children":2574},"Invite External Users","\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Finvite-external-user",[],{"title":2576,"path":2577,"order":2021,"children":2578},"Project Creation","\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fproject-create",[],{"title":2580,"path":2581,"order":2021,"children":2582},"Reset Password Flow","\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fpassword-reset",[],{"title":2584,"path":2585,"order":2021,"children":2586},"Team creation Flow","\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fteam-create",[],{"title":2588,"path":2589,"order":2021,"children":2590},"User Login Flows","\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Flogin",[],{"title":2592,"path":2593,"order":2021,"children":2594},"User Sign up Flow","\u002Fdocs\u002Fcontribute\u002Fworkflows\u002Fsignup",[],{"title":563,"path":562,"order":2021,"children":2596},[],{"title":2598,"path":2599,"order":2021,"children":2600},"Working with Feature Flags","\u002Fdocs\u002Fcontribute\u002Ffeature-flags",[],{"title":2602,"path":2603,"order":2021,"children":2604},"device-agent","\u002Fdocs\u002Fdevice-agent",[2605,2609,2612,2616,2620,2624],{"title":2606,"path":2607,"order":203,"children":2608},"FlowFuse Device Agent Introduction","\u002Fdocs\u002Fdevice-agent\u002Fintroduction",[],{"title":2438,"path":2610,"order":217,"children":2611},"\u002Fdocs\u002Fdevice-agent\u002Fquickstart",[],{"title":2613,"path":2614,"order":236,"children":2615},"Register your Remote Instance","\u002Fdocs\u002Fdevice-agent\u002Fregister",[],{"title":2617,"path":2618,"order":284,"children":2619},"Running the Agent","\u002Fdocs\u002Fdevice-agent\u002Frunning",[],{"title":2621,"path":2622,"order":290,"children":2623},"Deploying your Flows","\u002Fdocs\u002Fdevice-agent\u002Fdeploy",[],{"title":2625,"path":2626,"order":2021,"children":2627},"install","\u002Fdocs\u002Fdevice-agent\u002Finstall",[2628,2633,2637,2641,2645],{"title":2629,"path":2630,"group":2631,"order":203,"children":2632},"Overview","\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Foverview","DeviceAgentInstallation",[],{"title":2634,"path":2635,"group":2631,"order":217,"children":2636},"Device Agent Installer","\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Fdevice-agent-installer",[],{"title":2638,"path":2639,"group":2631,"order":227,"children":2640},"Docker Install","\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Fdocker",[],{"title":2642,"path":2643,"group":2631,"order":236,"children":2644},"Kubernetes Install","\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Fkubernetes",[],{"title":2646,"path":2647,"group":2631,"order":284,"children":2648},"Manual Install with NPM","\u002Fdocs\u002Fdevice-agent\u002Finstall\u002Fmanual",[],{"title":2650,"path":2651,"order":2021,"children":2652},"hardware","\u002Fdocs\u002Fhardware",[2653,2656,2660,2664],{"title":2629,"path":2654,"order":203,"children":2655},"\u002Fdocs\u002Fhardware\u002Fintroduction",[],{"title":2657,"path":2658,"order":217,"children":2659},"ctrlX - Node-RED","\u002Fdocs\u002Fhardware\u002Fctrlx-node-red",[],{"title":2661,"path":2662,"order":227,"children":2663},"ctrlX - Device Agent","\u002Fdocs\u002Fhardware\u002Fctrlx-device-agent",[],{"title":2665,"path":2666,"order":227,"children":2667},"Raspberry Pi with Raspbian","\u002Fdocs\u002Fhardware\u002Fraspbian",[],{"title":2625,"path":2669,"order":2021,"children":2670},"\u002Fdocs\u002Finstall",[2671,2674,2677,2681,2701,2705,2709,2713],{"title":2629,"path":2672,"order":203,"children":2673},"\u002Fdocs\u002Finstall\u002Fintroduction",[],{"title":539,"path":2675,"order":2021,"children":2676},"\u002Fdocs\u002Finstall\u002Fconfiguration",[],{"title":2678,"path":2679,"order":2021,"children":2680},"DNS Setup","\u002Fdocs\u002Finstall\u002Fdns-setup",[],{"title":2682,"path":2683,"order":2021,"children":2684},"Docker install","\u002Fdocs\u002Finstall\u002Fdocker",[2685,2689,2693,2697],{"title":2686,"path":2687,"order":2021,"children":2688},"Add Project Stacks on Docker","\u002Fdocs\u002Finstall\u002Fdocker\u002Fstacks",[],{"title":2690,"path":2691,"order":2021,"children":2692},"Docker Engine on Windows","\u002Fdocs\u002Finstall\u002Fdocker\u002Fwindows-docker-ce",[],{"title":2694,"path":2695,"order":2021,"children":2696},"Docker from AWS Market Place","\u002Fdocs\u002Finstall\u002Fdocker\u002Faws-marketplace",[],{"title":2698,"path":2699,"order":2021,"children":2700},"Docker on Digital Ocean","\u002Fdocs\u002Finstall\u002Fdocker\u002Fdigital-ocean",[],{"title":2702,"path":2703,"order":2021,"children":2704},"Email configuration","\u002Fdocs\u002Finstall\u002Femail-providers",[],{"title":2706,"path":2707,"order":2021,"children":2708},"First Run Setup","\u002Fdocs\u002Finstall\u002Ffirst-run",[],{"title":2710,"path":2711,"order":2021,"children":2712},"FlowFuse File Storage","\u002Fdocs\u002Finstall\u002Ffile-storage",[],{"title":2714,"path":2715,"order":2021,"children":2716},"Install FlowFuse on Kubernetes","\u002Fdocs\u002Finstall\u002Fkubernetes",[2717,2721,2725,2729,2733,2737],{"title":2718,"path":2719,"order":2021,"children":2720},"AWS EKS Installation","\u002Fdocs\u002Finstall\u002Fkubernetes\u002Faws",[],{"title":2722,"path":2723,"order":2021,"children":2724},"AWS EKS Installation with Terraform and Helm","\u002Fdocs\u002Finstall\u002Fkubernetes\u002Faws-terraform",[],{"title":2726,"path":2727,"order":2021,"children":2728},"Digital Ocean Kubernetes Installation","\u002Fdocs\u002Finstall\u002Fkubernetes\u002Fdigital-ocean",[],{"title":2730,"path":2731,"order":2021,"children":2732},"Ingress Controller Migration","\u002Fdocs\u002Finstall\u002Fkubernetes\u002Fingress-controller-migration",[],{"title":2734,"path":2735,"order":2021,"children":2736},"Kubernetes Project Stacks","\u002Fdocs\u002Finstall\u002Fkubernetes\u002Fstacks",[],{"title":2738,"path":2739,"order":2021,"children":2740},"OpenShift Installation","\u002Fdocs\u002Finstall\u002Fkubernetes\u002Fopenshift",[],{"title":2742,"path":2743,"order":2021,"children":2744},"migration","\u002Fdocs\u002Fmigration",[2745,2748],{"title":2023,"path":2746,"order":203,"children":2747},"\u002Fdocs\u002Fmigration\u002Fintroduction",[],{"title":2749,"path":2750,"order":2021,"children":2751},"Node-RED Tools plugin","\u002Fdocs\u002Fmigration\u002Fnode-red-tools",[],{"title":2753,"path":2754,"order":2021,"children":2755},"user","\u002Fdocs\u002Fuser",[2756,2760,2764,2768,2772,2776,2780,2783,2787,2791,2794,2798,2810,2814,2818,2822,2826,2829,2833,2837,2841,2845,2849,2852,2856,2860,2863,2867],{"title":2757,"path":2758,"order":203,"children":2759},"Getting Started","\u002Fdocs\u002Fuser\u002Fintroduction",[],{"title":2761,"path":2762,"order":203,"children":2763},"Static asset service","\u002Fdocs\u002Fuser\u002Fstatic-asset-service",[],{"title":2765,"path":2766,"order":217,"children":2767},"Bill of Materials","\u002Fdocs\u002Fuser\u002Fbill-of-materials",[],{"title":2769,"path":2770,"order":217,"children":2771},"FlowFuse Concepts","\u002Fdocs\u002Fuser\u002Fconcepts",[],{"title":2773,"path":2774,"order":314,"children":2775},"Instance States","\u002Fdocs\u002Fuser\u002Finstance-states",[],{"title":2777,"path":2778,"order":2021,"children":2779},"Changing the Stack","\u002Fdocs\u002Fuser\u002Fchangestack",[],{"title":2209,"path":2781,"order":2021,"children":2782},"\u002Fdocs\u002Fuser\u002Fcustom-hostnames",[],{"title":2784,"path":2785,"order":2021,"children":2786},"Custom Node Packages","\u002Fdocs\u002Fuser\u002Fcustom-npm-packages",[],{"title":2788,"path":2789,"order":2021,"children":2790},"Dashboards","\u002Fdocs\u002Fuser\u002Fdashboards",[],{"title":2227,"path":2792,"order":2021,"children":2793},"\u002Fdocs\u002Fuser\u002Fdevops-pipelines",[],{"title":2795,"path":2796,"order":2021,"children":2797},"Environment Variables","\u002Fdocs\u002Fuser\u002Fenvvar",[],{"title":2799,"path":2800,"order":2021,"children":2801},"FlowFuse Expert","\u002Fdocs\u002Fuser\u002Fexpert",[2802,2806],{"title":2803,"path":2804,"order":2021,"children":2805},"AI in Node-RED","\u002Fdocs\u002Fuser\u002Fexpert\u002Fnode-red-embedded-ai",[],{"title":2807,"path":2808,"order":2021,"children":2809},"Chat Interface","\u002Fdocs\u002Fuser\u002Fexpert\u002Fchat",[],{"title":2811,"path":2812,"order":2021,"children":2813},"FlowFuse File Nodes","\u002Fdocs\u002Fuser\u002Ffilenodes",[],{"title":2815,"path":2816,"order":2021,"children":2817},"FlowFuse MQTT Nodes","\u002Fdocs\u002Fuser\u002Fmqtt-nodes",[],{"title":2819,"path":2820,"order":2021,"children":2821},"FlowFuse Persistent Context","\u002Fdocs\u002Fuser\u002Fpersistent-context",[],{"title":2823,"path":2824,"order":2021,"children":2825},"FlowFuse Project Nodes","\u002Fdocs\u002Fuser\u002Fprojectnodes",[],{"title":2105,"path":2827,"order":2021,"children":2828},"\u002Fdocs\u002Fuser\u002Fff-tables",[],{"title":2830,"path":2831,"order":2021,"children":2832},"Groups","\u002Fdocs\u002Fuser\u002Fdevice-groups",[],{"title":2834,"path":2835,"order":2021,"children":2836},"High Availability mode","\u002Fdocs\u002Fuser\u002Fhigh-availability",[],{"title":2838,"path":2839,"order":2021,"children":2840},"HTTP Access Tokens","\u002Fdocs\u002Fuser\u002Fhttp-access-tokens",[],{"title":2842,"path":2843,"order":2021,"children":2844},"Instance Settings","\u002Fdocs\u002Fuser\u002Finstance-settings",[],{"title":2846,"path":2847,"order":2021,"children":2848},"Logging","\u002Fdocs\u002Fuser\u002Flogs",[],{"title":2365,"path":2850,"order":2021,"children":2851},"\u002Fdocs\u002Fuser\u002Frole-based-access-control",[],{"title":2853,"path":2854,"order":2021,"children":2855},"Shared Team Library","\u002Fdocs\u002Fuser\u002Fshared-library",[],{"title":2857,"path":2858,"order":2021,"children":2859},"Snapshots","\u002Fdocs\u002Fuser\u002Fsnapshots",[],{"title":563,"path":2861,"order":2021,"children":2862},"\u002Fdocs\u002Fuser\u002Fteambroker",[],{"title":2864,"path":2865,"order":2021,"children":2866},"Teams","\u002Fdocs\u002Fuser\u002Fteam",[],{"title":2868,"path":2869,"order":2021,"children":2870},"User Settings","\u002Fdocs\u002Fuser\u002Fuser-settings",[],1786717926334]