[
  {
    "path": ".github/workflows/gh-pages.yml",
    "content": "name: Build GH pages and deploy if on master\n\non:\n  push:\n    branches:\n    - master\n  pull_request:\n\njobs:\n  build-deploy:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5  # v4\n    - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020  # v4\n      with:\n        node-version-file: '.nvmrc'\n\n    - name: Build\n      run: npm ci && npm run build\n\n    - name: Deploy\n      uses: peaceiris/actions-gh-pages@855ba067441803d21928825cee354a257c1ca173  # v2.5.0\n      if: ${{ github.ref == 'refs/heads/master' }}\n      env:\n        ACTIONS_DEPLOY_KEY: ${{ secrets.ACTIONS_DEPLOY_KEY }}\n        PUBLISH_BRANCH: gh-pages\n        PUBLISH_DIR: ./build\n"
  },
  {
    "path": ".github/workflows/stale.yml",
    "content": "name: 'Close stale issues and PRs'\non:\n  schedule:\n    - cron: '30 1 * * *'\n\njobs:\n  stale:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84  # v8\n        with:\n          stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.'\n          stale-pr-message: 'This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.'\n          stale-issue-label: 'stale'\n          stale-pr-label: 'stale'\n          exempt-issue-labels: 'confirmed,help-needed'\n          exempt-pr-labels: 'confirmed'\n          days-before-issue-stale: 60\n          days-before-pr-stale: 90\n          days-before-issue-close: 10\n          days-before-pr-close: 10\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n\nnode_modules\ntranslated_docs\nbuild\nyarn.lock\n.docusaurus\n.vscode\n.idea\n\n*~\n*.sw?\n"
  },
  {
    "path": ".nvmrc",
    "content": "20\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# The Jitsi Handbook\n\nThis is The Jitsi Handbook. Your one-stop shop for Jitsi documentation. It's powered by [Docusaurus](https://docusaurus.io/).\nThe documentation website can be found [here](https://jitsi.github.io/handbook/).\n\n## Building the site\n\nThe site is built automatically with every push thanks to a [GH Actions](https://github.com/jitsi/handbook/blob/master/.github/workflows/gh-pages.yml).\n\n**NOTE:** You need to have Node.Js(>=16) installed in your system to build this site. NodeJs can be downloaded from [here](https://nodejs.org/en/download/)\n\nIf you want to build it locally, follow these simple steps:\n\n1. Clone the repository\n\n```shell\ngit clone https://github.com/jitsi/handbook.git\n```\n\n2. Move to the folder where the repository is cloned\n\n```shell\ncd handbook\n```\n\n3. Install the dependencies\n\n```shell\nnpm install\n```\n\n4. Start the website\n\n```shell\nnpm start\n```\n\nThe website will be running at http://127.0.0.1:3000/handbook/\n\nYou can now edit the files in the `docs` folder and the site will reflect the changes immediately thanks to\nlive reloading.\n\n## Contributing\n\nWe appreciate all contributions to this repository. Please make a Pull Request, no matter how small, all contributions\nare valuable!\n\nPlease follow the given [Contributing Guidelines](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-contributing) before submitting a pull request.\n"
  },
  {
    "path": "docs/architecture.md",
    "content": "---\nid: architecture\ntitle: Architecture\n---\n\nIn this section, a global overview of the Jitsi infrastructure is provided. If you just started contributing to the project, we highly recommend reading this section thoroughly.\n\n\n## Components\nJitsi comprises a [collection of projects](https://jitsi.org/projects/):\n\n* [Jitsi Meet](https://jitsi.org/jitsi-meet) - WebRTC compatible JavaScript application that uses Jitsi Videobridge to provide high-quality, scalable video conferences. Build upon React and React Native.\n* [Jitsi Videobridge (JVB)](https://jitsi.org/jitsi-videobridge) - WebRTC compatible server designed to route video streams amongst participants in a conference.\n* [Jitsi Conference Focus (jicofo)](https://github.com/jitsi/jicofo) - server-side focus component used in Jitsi Meet conferences that manages media sessions and acts as a load balancer between each of the participants and the videobridge.\n* [Jitsi Gateway to SIP (jigasi)](https://github.com/jitsi/jigasi) - server-side application that allows regular SIP clients to join Jitsi Meet conferences\n* [Jitsi Broadcasting Infrastructure (jibri)](https://github.com/jitsi/jibri) - set of tools for recording and/or streaming a Jitsi Meet conference that works by launching a Chrome instance rendered in a virtual framebuffer and capturing and encoding the output with ffmpeg.\n\nExternal Software used by Jitsi:\n* [Prosody](https://prosody.im/) - XMPP server used for signalling\n\n\n## Architecture Diagram\nThe individual connections between the previously described components, as well as their external integrations are described in the figure below.\n\n![](https://raw.githubusercontent.com/jitsi/handbook/master/docs/assets/ArchitectureDiagram.png)\n\nThe external connections can be categorized into two main groups. Firstly, the connections between clients that request a video or audio connection are performed through remote requests and data streams. The second category of external connections is those to external services that help store recordings, stream recordings, stream videos, or help with creating meetings.\n\n## Code Map\nIn this section, we will look at the main parts of the codebase and see what they can be used for.\n\n**./react/features**\nThis folder is where it is best to start writing your code, as it contains most of the app components that are used in the apps on Android and iOS, as well as on the web version. This source folder is split up into all the different features that Jitsi has to offer, such as authentication, chat interaction, keyboard shortcuts, screenshot capture, remote control, and virtual background. Each of these features has a folder in this map, which is then again split up to keep a hierarchy and consistency throughout the code.\n\nEach feature folder consists of a subfolder called components, in this folder all of the React, or React Native for mobile, components are expressed. Usually, in this folder there will be a separation between native and web components, however, in some cases, the same component could be used for both Android, iOS, and web browsers, in which case there is no separation made.\n\nAs stated before, the codebase mostly consists of React and React Native, which is the React version for mobile applications. Most of the features make use of the so-called class component by React [^class-comp], however, some new features start to use the new way to write functional components by using hooks[^func-comp].\n\nThe application makes use of React Redux as well, this is used as a general state store to keep track of important parameters that are used throughout the application. More on React Redux can be found here [^react-redux].\n\nMost features also contain a file called `middleware.js`. This file acts as a bridge between the component and the functionality of the rest of the application.\n\n**./modules/external-api**\nIn this folder, the external API can be found. This API can be used in various events like participants joining/leaving the meeting, changes in avatars or chat, as well as errors in using the microphone or camera.\n\n**./android and ./ios**\nBoth of these folders contain the basics of the Android and iOS apps respectively. However, the code for the application itself and its components can be found in the **react/features** folder, which is explained earlier in this section.\n\n**./conference.js**\nThis file can be found at the root of the project and contains the foundation of any interaction between a user and a conference room. This consists of setting up a connection to it, joining the meeting room, muting and unmuting, but also functions to gather information about the participants that are in the room.\n\n**./lang**\nThis folder contains all the different translations that are present in Jitsi Meet. The translations can be found in the code with each of the keys in the translation maps that can be found in the `main-[language].json` files.\n\n**./css**\nThis folder contains all the CSS that is used in the project. The files (mostly .scss files[^scss]) are split up into features like the React features that they are used in.\n\n## Testing\nThe main form of testing code changes is done through torture tests, next to this the code is tested manually.\n\nThe torture tests are located in a separate repository, [Jitsi Meet Torture](https://github.com/jitsi/jitsi-meet-torture). The project contains end-to-end tests for several key functions such as peer-to-peer and invites. The testing can be done for iOS, Android, and the web, which are all the platforms that Jitsi Meet can be used on. The testing is done automatically for pull requests by project members, where it is used in combination with the continuous integration by a Jenkins instance running the tests, testing on the [meet.jit.si](https://meet.jit.si) instance. Other members can run the tests locally. The test results can be viewed on an automatically generated web page.\n\nManual testing is performed while doing code reviews, however, there are also testing releases that can be freely downloaded and deployed or can be used on the [beta test server](https://beta.meet.jit.si/).\n"
  },
  {
    "path": "docs/assets/ArchitectureDiagram.drawio",
    "content": "<mxfile host=\"app.diagrams.net\" agent=\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\" version=\"28.1.0\">\n  <diagram name=\"Page-1\" id=\"bncNPVXqShW8ecF_GqgS\">\n    <mxGraphModel dx=\"946\" dy=\"583\" grid=\"1\" gridSize=\"10\" guides=\"1\" tooltips=\"1\" connect=\"1\" arrows=\"1\" fold=\"1\" page=\"1\" pageScale=\"1\" pageWidth=\"850\" pageHeight=\"1100\" math=\"0\" shadow=\"0\">\n      <root>\n        <mxCell id=\"0\" />\n        <mxCell id=\"1\" parent=\"0\" />\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-1\" value=\"\" style=\"rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"210\" y=\"140\" width=\"530\" height=\"450\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-2\" value=\"\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=arc;orthogonalLoop=1;jettySize=auto;html=1;startArrow=classic;startFill=1;endArrow=classic;endFill=1;\" edge=\"1\" parent=\"1\" source=\"aDnG364j5N6lEboaqi8u-4\" target=\"aDnG364j5N6lEboaqi8u-39\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"310\" y=\"380\" />\n              <mxPoint x=\"310\" y=\"380\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-3\" value=\"Exchange files\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];\" vertex=\"1\" connectable=\"0\" parent=\"aDnG364j5N6lEboaqi8u-2\">\n          <mxGeometry x=\"-0.2429\" y=\"1\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"1\" y=\"-13.33\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-4\" value=\"jitsi meet&lt;br&gt;&lt;font style=&quot;font-size: 10px&quot;&gt;Files for the webinterface&lt;/font&gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"250\" y=\"400\" width=\"120\" height=\"60\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-5\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=arc;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.75;entryY=0;entryDx=0;entryDy=0;startArrow=classic;startFill=1;endArrow=classic;endFill=1;\" edge=\"1\" parent=\"1\" source=\"aDnG364j5N6lEboaqi8u-7\" target=\"aDnG364j5N6lEboaqi8u-28\">\n          <mxGeometry relative=\"1\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-6\" value=\"XMPP connection\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];\" vertex=\"1\" connectable=\"0\" parent=\"aDnG364j5N6lEboaqi8u-5\">\n          <mxGeometry x=\"0.528\" y=\"-1\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"-69\" y=\"-107\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-7\" value=\"jitsi videobridge (JVB)&lt;br style=&quot;font-size: 10px&quot;&gt;&lt;font size=&quot;1&quot;&gt;Video streams&lt;/font&gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"250\" y=\"190\" width=\"120\" height=\"60\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-8\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=arc;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;startArrow=none;startFill=0;endArrow=classic;endFill=1;\" edge=\"1\" parent=\"1\" source=\"aDnG364j5N6lEboaqi8u-12\" target=\"aDnG364j5N6lEboaqi8u-28\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"660\" y=\"370\" as=\"sourcePoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-9\" value=\"Setup connections\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];\" vertex=\"1\" connectable=\"0\" parent=\"aDnG364j5N6lEboaqi8u-8\">\n          <mxGeometry x=\"-0.0519\" y=\"-1\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"-46\" y=\"1\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-10\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;startArrow=classic;startFill=1;endArrow=none;endFill=0;\" edge=\"1\" parent=\"1\" source=\"aDnG364j5N6lEboaqi8u-12\" target=\"aDnG364j5N6lEboaqi8u-7\">\n          <mxGeometry relative=\"1\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-11\" value=\"Statistics\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];\" vertex=\"1\" connectable=\"0\" parent=\"aDnG364j5N6lEboaqi8u-10\">\n          <mxGeometry x=\"-0.7049\" y=\"1\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"-15\" y=\"-1\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-12\" value=\"jicofo&lt;br&gt;&lt;font style=&quot;font-size: 10px&quot;&gt;Load balancer and connection broker&amp;nbsp;&lt;/font&gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"600\" y=\"280\" width=\"120\" height=\"60\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-13\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;startArrow=classic;startFill=1;\" edge=\"1\" parent=\"1\" source=\"aDnG364j5N6lEboaqi8u-19\" target=\"aDnG364j5N6lEboaqi8u-7\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"490\" y=\"200\" />\n              <mxPoint x=\"490\" y=\"170\" />\n              <mxPoint x=\"310\" y=\"170\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-14\" value=\"Streaming\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];\" vertex=\"1\" connectable=\"0\" parent=\"aDnG364j5N6lEboaqi8u-13\">\n          <mxGeometry x=\"0.0632\" y=\"1\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"-24\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-15\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=arc;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;startArrow=none;startFill=0;endArrow=classic;endFill=1;\" edge=\"1\" parent=\"1\" source=\"aDnG364j5N6lEboaqi8u-19\" target=\"aDnG364j5N6lEboaqi8u-45\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"650\" y=\"170\" />\n              <mxPoint x=\"570\" y=\"170\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-16\" value=\"Remote file transfer\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];\" vertex=\"1\" connectable=\"0\" parent=\"aDnG364j5N6lEboaqi8u-15\">\n          <mxGeometry x=\"0.6456\" y=\"2\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"2\" y=\"-3.33\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-17\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=arc;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;startArrow=none;startFill=0;endArrow=classic;endFill=1;\" edge=\"1\" parent=\"1\" source=\"aDnG364j5N6lEboaqi8u-19\" target=\"aDnG364j5N6lEboaqi8u-44\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"650\" y=\"170\" />\n              <mxPoint x=\"710\" y=\"170\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-18\" value=\"Remote streaming\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];\" vertex=\"1\" connectable=\"0\" parent=\"aDnG364j5N6lEboaqi8u-17\">\n          <mxGeometry x=\"0.5941\" relative=\"1\" as=\"geometry\">\n            <mxPoint y=\"-6.67\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-19\" value=\"jibri&lt;br style=&quot;font-size: 7px&quot;&gt;&lt;font size=&quot;1&quot;&gt;Recording/streaming&lt;/font&gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"600\" y=\"190\" width=\"120\" height=\"60\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-20\" value=\"\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=classic;startFill=1;\" edge=\"1\" parent=\"1\" source=\"aDnG364j5N6lEboaqi8u-22\" target=\"aDnG364j5N6lEboaqi8u-25\">\n          <mxGeometry relative=\"1\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-21\" value=\"Stream call\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];\" vertex=\"1\" connectable=\"0\" parent=\"aDnG364j5N6lEboaqi8u-20\">\n          <mxGeometry x=\"-0.3037\" y=\"-1\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"1.67\" y=\"-1\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-22\" value=\"SIP server&lt;br style=&quot;font-size: 10px&quot;&gt;&lt;font size=&quot;1&quot;&gt;IP cellphone server&lt;/font&gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"20\" y=\"480\" width=\"120\" height=\"60\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-23\" value=\"\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=classic;startFill=1;\" edge=\"1\" parent=\"1\" source=\"aDnG364j5N6lEboaqi8u-25\" target=\"aDnG364j5N6lEboaqi8u-28\">\n          <mxGeometry relative=\"1\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-24\" value=\"Stream call\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];\" vertex=\"1\" connectable=\"0\" parent=\"aDnG364j5N6lEboaqi8u-23\">\n          <mxGeometry x=\"-0.1083\" y=\"-1\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"-16.67\" y=\"-1\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-25\" value=\"jigasi&lt;br style=&quot;font-size: 10px&quot;&gt;&lt;font size=&quot;1&quot;&gt;Phone bridge&lt;/font&gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"250\" y=\"480\" width=\"120\" height=\"60\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-26\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;startArrow=classic;startFill=1;\" edge=\"1\" parent=\"1\" source=\"aDnG364j5N6lEboaqi8u-28\" target=\"aDnG364j5N6lEboaqi8u-39\">\n          <mxGeometry relative=\"1\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-27\" value=\"XML messaging&lt;br&gt;Websocket/Bosh\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];\" vertex=\"1\" connectable=\"0\" parent=\"aDnG364j5N6lEboaqi8u-26\">\n          <mxGeometry x=\"-0.6683\" y=\"2\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"-43\" y=\"-66.67\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-28\" value=\"Prosody&lt;br style=&quot;font-size: 10px&quot;&gt;&lt;font size=&quot;1&quot;&gt;XMPP server&lt;/font&gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"410\" y=\"400\" width=\"120\" height=\"60\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-29\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;startArrow=classic;startFill=1;\" edge=\"1\" parent=\"1\" source=\"aDnG364j5N6lEboaqi8u-33\" target=\"aDnG364j5N6lEboaqi8u-39\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"80\" y=\"300\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-30\" value=\"Remote request\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];\" vertex=\"1\" connectable=\"0\" parent=\"aDnG364j5N6lEboaqi8u-29\">\n          <mxGeometry x=\"0.1467\" y=\"1\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"14.17\" y=\"1\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-31\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;startArrow=classic;startFill=1;\" edge=\"1\" parent=\"1\" source=\"aDnG364j5N6lEboaqi8u-33\" target=\"aDnG364j5N6lEboaqi8u-7\">\n          <mxGeometry relative=\"1\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-32\" value=\"Stream\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];\" vertex=\"1\" connectable=\"0\" parent=\"aDnG364j5N6lEboaqi8u-31\">\n          <mxGeometry x=\"-0.2296\" y=\"1\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"-5\" y=\"1\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-33\" value=\"web browser&lt;br&gt;&lt;font style=&quot;font-size: 10px&quot;&gt;Client&lt;/font&gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"20\" y=\"190\" width=\"120\" height=\"60\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-34\" value=\"\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=classic;startFill=1;\" edge=\"1\" parent=\"1\" source=\"aDnG364j5N6lEboaqi8u-36\" target=\"aDnG364j5N6lEboaqi8u-22\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"80\" y=\"460\" />\n              <mxPoint x=\"80\" y=\"460\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-35\" value=\"Call\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];\" vertex=\"1\" connectable=\"0\" parent=\"aDnG364j5N6lEboaqi8u-34\">\n          <mxGeometry x=\"0.1952\" y=\"-1\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"1\" y=\"-5.83\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-36\" value=\"cellphone&lt;br style=&quot;font-size: 10px&quot;&gt;&lt;font size=&quot;1&quot;&gt;Client&lt;/font&gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"20\" y=\"360\" width=\"120\" height=\"60\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-37\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=arc;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.25;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;startArrow=none;startFill=0;\" edge=\"1\" parent=\"1\" source=\"aDnG364j5N6lEboaqi8u-39\" target=\"aDnG364j5N6lEboaqi8u-43\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"210\" y=\"285\" />\n              <mxPoint x=\"210\" y=\"130\" />\n              <mxPoint x=\"270\" y=\"130\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-38\" value=\"Remote API-call\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];\" vertex=\"1\" connectable=\"0\" parent=\"aDnG364j5N6lEboaqi8u-37\">\n          <mxGeometry x=\"0.0449\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"60\" y=\"-55.83\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-39\" value=\"web server&lt;br style=&quot;font-size: 10px&quot;&gt;&lt;font size=&quot;1&quot;&gt;Cloud/hosting server&lt;/font&gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"250\" y=\"270\" width=\"120\" height=\"60\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-40\" value=\"jitsi components\" style=\"text;strokeColor=none;fillColor=none;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"510\" y=\"480\" width=\"220\" height=\"40\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-41\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;jumpStyle=arc;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;startFill=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;\" edge=\"1\" parent=\"1\" source=\"aDnG364j5N6lEboaqi8u-43\" target=\"aDnG364j5N6lEboaqi8u-33\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"100\" y=\"180\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"80\" y=\"50\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-42\" value=\"Stream\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];\" vertex=\"1\" connectable=\"0\" parent=\"aDnG364j5N6lEboaqi8u-41\">\n          <mxGeometry x=\"0.1\" y=\"-1\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"1\" y=\"41.67\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-43\" value=\"Youtube&lt;br style=&quot;font-size: 7px&quot;&gt;&lt;font size=&quot;1&quot;&gt;Embed videos&lt;/font&gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"210\" y=\"20\" width=\"120\" height=\"60\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-44\" value=\"Youtube&lt;br style=&quot;font-size: 7px&quot;&gt;&lt;font size=&quot;1&quot;&gt;Streaming&lt;/font&gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"650\" y=\"20\" width=\"120\" height=\"60\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-45\" value=\"Dropbox&lt;br style=&quot;font-size: 7px&quot;&gt;&lt;font size=&quot;1&quot;&gt;Streaming storage&lt;/font&gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"510\" y=\"20\" width=\"120\" height=\"60\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-46\" value=\"\" style=\"endArrow=classic;startArrow=classic;html=1;rounded=0;exitX=0.993;exitY=0.873;exitDx=0;exitDy=0;strokeColor=default;dashed=1;entryX=0.663;entryY=0.98;entryDx=0;entryDy=0;entryPerimeter=0;exitPerimeter=0;\" edge=\"1\" parent=\"1\" source=\"aDnG364j5N6lEboaqi8u-28\" target=\"aDnG364j5N6lEboaqi8u-12\">\n          <mxGeometry width=\"50\" height=\"50\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"400\" y=\"420\" as=\"sourcePoint\" />\n            <mxPoint x=\"670\" y=\"340\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"680\" y=\"450\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"aDnG364j5N6lEboaqi8u-47\" value=\"Presence updating\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];\" vertex=\"1\" connectable=\"0\" parent=\"1\">\n          <mxGeometry x=\"610\" y=\"460\" as=\"geometry\">\n            <mxPoint x=\"-4\" y=\"-3\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n      </root>\n    </mxGraphModel>\n  </diagram>\n</mxfile>\n"
  },
  {
    "path": "docs/community/intro.md",
    "content": "---\nid: community-intro\ntitle: Our community\n---\n\nJitsi Meet and all accompanying Jitsi projects are maintained by a community of\npassionate individuals and corporations.\n\nIf you have questions, need help, or want to make suggestions, please join our community\n[here](https://community.jitsi.org/).\n\n<hr />\n\nimport DocCardList from '@theme/DocCardList';\n\n<DocCardList />\n"
  },
  {
    "path": "docs/community/third-party-software.md",
    "content": "---\nid: third-party-software\ntitle: Third-Party Software\n---\n\nThis page contains links to projects around Jitsi Meet, which are not maintained\nby the Jitsi team.\n\nPlease keep this list in alphabetical order.\n\n:::warning\nAs these packages are not maintained by the Jitsi team, please ask their\nrespective forums / issue trackers for help if you find issues.\n:::\n\n## Cketti's Jitsi Hacks\n\nSome extra features using injected scripts.\n\nhttps://jitsi-hacks.cketti.eu/\n\n## Eparto Virtual Phone\n\nThis is a Chrome extension that allow users to use their browser as a virtual\nphone and to make Jitsi-based calls without opening any web site.\n\nChrome web store:\n[Eparto virtual phone extension](https://chromewebstore.google.com/detail/eparto-virtual-phone/njihflnogjnjnmflicfongbnehhpkhmj)\n\nGitHub: https://github.com/emrahcom/eparto-virtual-phone\n\n## Flutter plugin\n\nPlugin for Flutter.\n\nhttps://pub.dev/packages/jitsi_meet\n\n## Galaxy\n\nGalaxy is a web application for Jitsi admins and users to organize their Jitsi\nmeetings, meeting schedules, and attendees. It supports\n[JaaS](https://jaas.8x8.vc/) and self-hosted Jitsi deployments.\n\nGitHub: https://github.com/emrahcom/galaxy\n\nDemo: https://eparto.net\n\n## Galaxy-kc\n\nThis is the version of `Galaxy` that uses `Keycloak` as the identity provider.\n\nhttps://github.com/emrahcom/galaxy-kc\n\n## GStreamer pipeline integration\n\nIntegrate Jitsi Meet conferences with GStreamer pipelines.\n\nhttps://github.com/avstack/gst-meet\n\n## GStreamer plugin in C++\n\nJitsi Meet GStreamer plugin\n\nhttps://github.com/mojyack/gstjitsimeet\n\n## Jitok: Jitsi Token generator\n\nHelper web tool and API for generating Jitsi Meet compatible JWT.\n\nGitHub: https://github.com/jitsi-contrib/jitok\n\nDemo: https://jitok.emrah.com/\n\nDiscussion: https://community.jitsi.org/t/jitok-jitsi-token-generator/94683\n\n## Jitsi-Admin\n\nAn open-source platform to organize your meetings. Includes all functions we\nknow from the big conference-tools\n\n- Plan your Meeting\n- Secure your Meeting with user login credentials\n- Create a Report of each user visiting your conference\n- Create an appointment poll and transfer it into a conference with one click\n- Dockerised for easy installation\n\nGithub: https://github.com/H2-invent/jitsi-admin\n\nDemo: https://jitsi-admin.de\n\nDocker:\nhttps://github.com/H2-invent/jitsi-admin/wiki/Install-jitsi-admin-in-docker\n\n## JitsiMeet-AutoStart\n\nA lightweight, single-page utility for unattended systems that need to automatically join a Jitsi meeting with the camera enabled. Perfect for kiosks, monitoring setups, or any scenario where a system should automatically connect and display video without user interaction. No deployment required.\n\nGithub: https://github.com/GammaPi/JitsiMeet-AutoStart\n\n## Jitsi Config Differ\n\nWeb app to compare reference configs between different Jitsi releases. This aims\nto help users identify potential changes in config keys and default values when\nupgrading their deployment.\n\nhttps://shawnchin.github.io/jitsi-config-differ/\n\nGithub: https://github.com/shawnchin/jitsi-config-differ\n\n## Jitsi URL Generator\n\nA simple tool to illustrate how URL params can be composed to customize Jitsi.\nIt only exposes a small fraction of what is possible, but should hopefully help\nbuild familiarity which users can then apply to other config values in the\nwhitelist.\n\nhttps://shawnchin.github.io/jitsi-url-generator/\n\nGithub: https://github.com/shawnchin/jitsi-url-generator\n\n## KeyCloak adapter\n\nAllow Jitsi to use Keycloak as an identity and OIDC provider.\n\nhttps://github.com/nordeck/jitsi-keycloak-adapter\n\n## KeyCloak integration\n\nIntegration of KeyCloak for authentication.\n\nhttps://github.com/D3473R/jitsi-keycloak\n\n## Outlook Plugin\n\nPlugin for Adding a \"Schedule Jitsi Meeting\" Button to Microsoft Outlook.\n\nGitHub: https://github.com/timetheoretical/jitsi-meet-outlook\n\n## Outlook Pluigin\n\nPlugin for Adding a \"Schedule Jitsi Meeting\" Button to Microsoft Outlook.\nWritten according to Microsoft's modern architecture.\n\nGitHub: https://github.com/diggsweden/jitsi-outlook\n\n## Prosody Plugins\n\nCollection of community-contributed prosody plugins that can be added to\nself-hosted Jitsi deployments.\n\nhttps://github.com/jitsi-contrib/prosody-plugins\n\n- **event_sync**: Sends HTTP POST to external API when occupant or room events\n  are triggered.\n- **frozen_nick**: Prevents users from changing their display name if JWT auth\n  is used and name is provided in token context.\n- **jibri_autostart**: Automatically starts recording when the moderator enters\n  the room.\n- **lobby_autostart**: Automatically enables lobby for all rooms.\n- **per_room_max_occupants**: Set different max occupants based on room name and\n  subdomain.\n- **secure_domain_lobby_bypass**: Allows secure domain authenticated users to\n  bypass lobby.\n- **time_restricted**: Sets a time limit on rooms and terminates the conference\n  when the time is up.\n- **token_affiliation**: Promotes users to moderators based on affiliation\n  property in token (JWT).\n- **token_lobby_bypass**: Enables some users to bypass lobby based on a flag in\n  token (JWT).\n- **token_lobby_ondemand**: Activates lobby based on a flag in token (JWT).\n- **token_owner_party**: Prevents unauthorized users from create a room and\n  terminates the conference when the owner leaves.\n\n## SAML to Jitsi JWT Authentification\n\nIntergration of SAML authentification with Shibboleth for a Jitsi Meet JWT\ngenerator.\n\nGithub: https://github.com/Renater/Jitsi-SAML2JWT\n\n## Unity plugin\n\nPlugin for using lib-jitsi-meet in a Unity environment (WebGL).\n\nhttps://github.com/avstack/jitsi-meet-unity-demo\n\nPlugin for using lib-jitsi-meet in a Unity environment (Android and iOS).\n\nhttps://github.com/SariskaIO/Sariska-Media-Unity-Demo\n\n## Generic OIDC and SAML adapter\n\nAdd support for OIDC and SAML to Jitsi with JWT and anonymous domain activated.\nAuthenticaten for the meeting host, allowing guests to join without requiring\nauthentication.\n\nGithub: https://github.com/aadpM2hhdixoJm3u/jitsi-OIDC-SAML-adapter\n\nGithub: https://github.com/aadpM2hhdixoJm3u/jitsi-OIDC-adapter\n"
  },
  {
    "path": "docs/dev-guide/android-sdk.md",
    "content": "---\nid: dev-guide-android-sdk\ntitle: Android SDK\n---\n\nThe Jitsi Meet Android SDK provides the same user experience as the Jitsi Meet app,\nin a customizable way which you can embed in your apps.\n\n:::important\nAndroid 7.0 (API level 24) or higher is required.\n:::\n\n## Sample applications using the SDK\n\nIf you want to see how easy integrating the Jitsi Meet SDK into a native application is, take a look at the\n[sample applications repository](https://github.com/jitsi/jitsi-meet-sdk-samples#android).\n\n## Build your own, or use a pre-build SDK artifacts/binaries\n\nJitsi conveniently provides a pre-build SDK artifacts/binaries in its Maven repository. When you do not require any\nmodification to the SDK itself or any of its dependencies, it's suggested to use the pre-build SDK. This avoids the\ncomplexity of building and installing your own SDK artifacts/binaries.\n\n### Use pre-build SDK artifacts/binaries\n\nIn your project, add the Maven repository\n`https://github.com/jitsi/jitsi-maven-repository/raw/master/releases` and the\ndependency `org.jitsi.react:jitsi-meet-sdk` into your `build.gradle` files.\n\nThe repository typically goes into the `build.gradle` file in the root of your project:\n\n```gradle title=\"build.gradle\"\nallprojects {\n    repositories {\n        maven {\n            url \"https://github.com/jitsi/jitsi-maven-repository/raw/master/releases\"\n        }\n        google()\n        mavenCentral()\n        maven { url 'https://www.jitpack.io' }\n    }\n}\n```\n\nIn recent versions of Android Studios, `allprojects{}` might not be found in `build.gradle`. In that case, the repository goes into the `settings.gradle` file in the root of your project:\n\n```gradle title=\"settings.gradle\"\ndependencyResolutionManagement {\n    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n    repositories {\n        google()\n        mavenCentral()\n        maven {\n            url \"https://github.com/jitsi/jitsi-maven-repository/raw/master/releases\"\n        }\n        maven {\n            url \"https://maven.google.com\"\n        }\n    }\n}\n```\n\nDependency definitions belong in the individual module `build.gradle` files:\n\n```gradle\ndependencies {\n    // (other dependencies)\n    implementation ('org.jitsi.react:jitsi-meet-sdk:+') { transitive = true }\n}\n```\n\n:::warning\nMake sure you pin your dependency by checking the [releases page](https://github.com/jitsi/jitsi-meet-release-notes/blob/master/CHANGELOG-MOBILE-SDKS.md).\n:::\n\n### Build and use your own SDK artifacts/binaries\n\n<details>\n<summary>Show building instructions</summary>\n\nStart by making sure that your development environment [is set up correctly](/docs/category/mobile).\n\n:::note A Note on Dependencies\nApart from the SDK, Jitsi also publishes a binary Maven artifact for some of the SDK dependencies (that are not otherwise publicly available) to the Jitsi Maven repository. When you're planning to use a SDK that is built from source, you'll likely use a version of the source code that is newer (or at least _different_) than the version of the source that was used to create the binary SDK artifact. As a consequence, the dependencies that your project will need, might also be different from those that are published in the Jitsi Maven repository. This might lead to build problems, caused by dependencies that are unavailable.\n:::\n\nIf you want to use a SDK that is built from source, you will likely benefit from composing a local Maven repository that contains these dependencies. The text below describes how you create a repository that includes both the SDK as well as these dependencies. For illustration purposes, we'll define the location of this local Maven repository as `/tmp/repo`\n\nIn source code form, the Android SDK dependencies are locked/pinned by `package.json` and `package-lock.json` of the Jitsi Meet project. To obtain the data, execute NPM in the jitsi-meet project directory:\n\n```shell\n    npm install\n```\n\nThis will pull in the dependencies in either binary format, or in source code format, somewhere under /node_modules/\n\nThird-party React Native _modules_, which Jitsi Meet SDK for Android depends on, are download by NPM in source code \nor binary form. These need to be assembled into Maven artifacts, and then published to your local Maven repository.\nA script is provided to facilitate this. From the root of the jitsi-meet project repository, run:\n\n```shell\n    ./android/scripts/release-sdk.sh /tmp/repo\n```\n\nThis will build and publish the SDK, and all of its dependencies to the specified Maven repository (`/tmp/repo`) in\nthis example.\n\nYou're now ready to use the artifacts. In _your_ project, add the Maven repository that you used above (`/tmp/repo`) into your top-level `build.gradle` file:\n\n```gradle\n    allprojects {\n        repositories {\n            maven { url \"file:/tmp/repo\" }\n            google()\n            mavenCentral()\n            maven { url 'https://www.jitpack.io' }\n        }\n    }\n```\n\nYou can use your local repository to replace the Jitsi repository (`maven { url \"https://github.com/jitsi/jitsi-maven-repository/raw/master/releases\" }`) when you published _all_ subprojects. If you didn't do that, you'll have to add both repositories. Make sure your local repository is listed first!\n\nThen, define the dependency `org.jitsi.react:jitsi-meet-sdk` into the `build.gradle` file of your module:\n\n```java\n    implementation ('org.jitsi.react:jitsi-meet-sdk:+') { transitive = true }\n```\n\nNote that there should not be a need to explicitly add the other dependencies, as they will be pulled in as transitive\ndependencies of `jitsi-meet-sdk`.\n\n</details>\n\n## Using the API\n\nJitsi Meet SDK is an Android library which embodies the whole Jitsi Meet\nexperience and makes it reusable by third-party apps.\n\nFirst, add Java 1.8 compatibility support to your project by adding the\nfollowing lines into your `build.gradle` file:\n\n```gradle\ncompileOptions {\n    sourceCompatibility JavaVersion.VERSION_1_8\n    targetCompatibility JavaVersion.VERSION_1_8\n}\n```\n\nTo get started, just launch `JitsiMeetActivity` pointing to the room you want:\n\n```java\n// Somewhere early in your app.\nJitsiMeetConferenceOptions defaultOptions\n        = new JitsiMeetConferenceOptions.Builder()\n    .setServerURL(serverURL)\n    // When using JaaS, set the obtained JWT here\n    //.setToken(\"MyJWT\")\n    // Different features flags can be set\n    // .setFeatureFlag(\"toolbox.enabled\", false)\n    // .setFeatureFlag(\"filmstrip.enabled\", false)\n    .setFeatureFlag(\"welcomepage.enabled\", false)\n    .build();\nJitsiMeet.setDefaultConferenceOptions(defaultOptions);\n// ...\n// Build options object for joining the conference. The SDK will merge the default\n// one we set earlier and this one when joining.\nJitsiMeetConferenceOptions options\n        = new JitsiMeetConferenceOptions.Builder()\n    .setRoom(roomName)\n    // Settings for audio and video\n    //.setAudioMuted(true)\n    //.setVideoMuted(true)\n    .build();\n// Launch the new activity with the given options. The launch() method takes care\n// of creating the required Intent and passing the options.\nJitsiMeetActivity.launch(this, options);\n```\n\nAlternatively, you can use the `org.jitsi.meet.sdk.JitsiMeetView` class which\nextends `android.view.View`.\n\nNote that this should only be needed when `JitsiMeetActivity` cannot be used for\nsome reason. Extending `JitsiMeetView` requires manual wiring of the view to\nthe activity, using a lot of boilerplate code. Using the Activity instead of the\nView is strongly recommended.\n\n<details>\n<summary>Show example</summary>\n\n```java\npackage org.jitsi.example;\n\nimport android.os.Bundle;\nimport android.support.v4.app.FragmentActivity;\n\nimport org.jitsi.meet.sdk.JitsiMeetView;\nimport org.jitsi.meet.sdk.ReactActivityLifecycleCallbacks;\n\n// Example\n//\npublic class MainActivity extends FragmentActivity implements JitsiMeetActivityInterface {\n    private JitsiMeetView view;\n\n    @Override\n    protected void onActivityResult(\n            int requestCode,\n            int resultCode,\n            Intent data) {\n        JitsiMeetActivityDelegate.onActivityResult(\n                this, requestCode, resultCode, data);\n    }\n\n    @Override\n    public void onBackPressed() {\n        JitsiMeetActivityDelegate.onBackPressed();\n    }\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        view = new JitsiMeetView(this);\n        JitsiMeetConferenceOptions options = new JitsiMeetConferenceOptions.Builder()\n            .setRoom(\"https://meet.jit.si/test123\")\n            .build();\n        view.join(options);\n\n        setContentView(view);\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n\n        view.dispose();\n        view = null;\n\n        JitsiMeetActivityDelegate.onHostDestroy(this);\n    }\n\n    @Override\n    public void onNewIntent(Intent intent) {\n        JitsiMeetActivityDelegate.onNewIntent(intent);\n    }\n\n    @Override\n    public void onRequestPermissionsResult(\n            final int requestCode,\n            final String[] permissions,\n            final int[] grantResults) {\n        JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n\n        JitsiMeetActivityDelegate.onHostResume(this);\n    }\n\n    @Override\n    protected void onStop() {\n        super.onStop();\n\n        JitsiMeetActivityDelegate.onHostPause(this);\n    }\n}\n```\n\n</details>\n\n### JitsiMeetActivity\n\nThis class encapsulates a high level API in the form of an Android `FragmentActivity`\nwhich displays a single `JitsiMeetView`. You can pass a URL as a `ACTION_VIEW`\non the Intent when starting it and it will join the conference, and will be\nautomatically terminated (finish() will be called on the activity) when the\nconference ends or fails.\n\n### JitsiMeetView\n\nThe `JitsiMeetView` class is the core of Jitsi Meet SDK. It's designed to\ndisplay a Jitsi Meet conference (or a welcome page).\n\n#### join(options)\n\nJoins the conference specified by the given `JitsiMeetConferenceOptions`.\n\n#### dispose()\n\nReleases all resources associated with this view. This method MUST be called\nwhen the Activity holding this view is going to be destroyed, usually in the\n`onDestroy()` method.\n\n### JitsiMeetConferenceOptions\n\nThis object encapsulates all the options that can be tweaked when joining\na conference.\n\nExample:\n\n```java\nprivate static @NonNull ArrayList<Bundle> getCustomToolbarButtons() {\n    ArrayList<Bundle> customToolbarButtons = new ArrayList<>();\n\n    Bundle firstCustomButton = new Bundle();\n    Bundle secondCustomButton = new Bundle();\n    Bundle thirdCustomButton = new Bundle();\n    Bundle fourthCustomButton = new Bundle();\n    Bundle fifthCustomButton = new Bundle();\n\n    firstCustomButton.putString(\"icon\", \"ICON_URL\");\n    firstCustomButton.putString(\"id\", \"CUSTOM_BTN_ID\");\n    \n    secondCustomButton.putString(\"backgroundColor\", \"CUSTOM_BTN_BACKGROUND_COLOR\");\n    secondCustomButton.putString(\"icon\", \"ICON_URL\");\n    secondCustomButton.putString(\"id\", \"CUSTOM_BTN_ID\");\n\n    customToolbarButtons.add(firstCustomButton);\n    customToolbarButtons.add(secondCustomButton);\n\n    return customToolbarButtons;\n}\n\n// If you want your custom button/s to appear inside the toolbar, \n// you will need to set your toolbar buttons too and always include \"overflowmenu\", \"hangup\".\n// All the buttons that, because of the screen size, won't fit the toolbar, will be automatically moved to the overflow menu.\nprivate String[] getToolbarButtons() {\n    return new String[]{\"CUSTOM_BTN_ID\", \"CUSTOM_BTN_ID\", \"microphone\", \"overflowmenu\", \"hangup\"};\n}\n        \nJitsiMeetConferenceOptions options = new JitsiMeetConferenceOptions.Builder()\n    .setServerURL(new URL(\"https://meet.jit.si\"))\n    .setRoom(\"MonthlyEndorsementsRebuildConsequently\")\n    .setAudioMuted(false)\n    .setVideoMuted(false)\n    .setAudioOnly(false)\n    .setWelcomePageEnabled(false)\n    .setConfigOverride(\"requireDisplayName\", true)\n    .setConfigOverride(\"customToolbarButtons\", getCustomToolbarButtons())\n    .setConfigOverride(\"toolbarButtons\", getToolbarButtons())\n    .build();\n```\n\nSee the `JitsiMeetConferenceOptions` implementation for all available options.\n\n### JitsiMeetActivityDelegate\n\nThis class handles the interaction between `JitsiMeetView` and its enclosing\n`Activity`. Generally this shouldn't be consumed by users, because they'd be\nusing `JitsiMeetActivity` instead, which is already completely integrated.\n\nAll its methods are static.\n\n#### onActivityResult(...)\n\nHelper method to handle results of auxiliary activities launched by the SDK.\nShould be called from the activity method of the same name.\n\n#### onBackPressed()\n\nHelper method which should be called from the activity's `onBackPressed` method.\nIf this function returns `true`, it means the action was handled and thus no\nextra processing is required; otherwise the app should call the parent's\n`onBackPressed` method.\n\n#### onHostDestroy(...)\n\nHelper method which should be called from the activity's `onDestroy` method.\n\n#### onHostResume(...)\n\nHelper method which should be called from the activity's `onResume` or `onStop`\nmethod.\n\n#### onHostStop(...)\n\nHelper method which should be called from the activity's `onSstop` method.\n\n#### onNewIntent(...)\n\nHelper method for integrating the *deep linking* functionality. If your app's\nactivity is launched in \"singleTask\" mode this method should be called from the\nactivity's `onNewIntent` method.\n\n#### onRequestPermissionsResult(...)\n\nHelper method to handle permission requests inside the SDK. It should be called\nfrom the activity method of the same name.\n\n#### onUserLeaveHint()\n\nHelper method for integrating automatic Picture-in-Picture. It should be called\nfrom the activity's `onUserLeaveHint` method.\n\nThis is a static method.\n\n### Listening for broadcasted events\n\nThe SDK broadcasts several events that the users can listen for.\n\n```java\n    IntentFilter intentFilter = new IntentFilter();\n    intentFilter.addAction(BroadcastEvent.Type.CONFERENCE_JOINED.getAction());\n    LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, intentFilter);\n ```  \n        \nPlease see `JitsiMeetActivity`, which registers for all the events and can serve as an example.\n\n#### Supported events\n\n##### CONFERENCE_JOINED\n\nBroadcasted when a conference was joined. `data` contains the following information:\n\n- `url`: the conference URL\n\n##### CONFERENCE_TERMINATED\n\nBroadcasted when the active conference ends, be it because of user choice or because of a failure. `data` contains the\nfollowing information:\n\n- `url`: the conference URL\n- `error`: missing if the conference finished gracefully, otherwise contains the error message\n\n##### CONFERENCE_WILL_JOIN\n\nBroadcasted before a conference is joined. `data` contains the following information:\n\n- `url`: the conference URL\n\n##### AUDIO_MUTED_CHANGED\n\nBroadcasted when the local participant's audio is muted or unmuted. `data` contains the following information:\n\n- `muted`: a boolean indicating whether the audio is muted or not.\n\n##### PARTICIPANT_JOINED\n\nBroadcasted when a participant has joined the conference. `data` contains the following information:\n\n- `email`: the email of the participant. It may not be set if the remote participant didn't set one.\n- `name`: the name of the participant.\n- `role`: the role of the participant.\n- `participantId`: the id of the participant.\n\n##### PARTICIPANT_LEFT\n\nCalled when a participant has left the conference. `data` contains the following information:\n\n- `participantId`: the id of the participant that left.\n\n##### ENDPOINT_TEXT_MESSAGE_RECEIVED\n\nBroadcasted when an endpoint text message is received. The `data` HashMap contains a `senderId` key with the\nparticipantId of the sender and a `message` key with the content.\n\n##### SCREEN_SHARE_TOGGLED\n\nBroadcasted when a participant starts or stops sharing his screen. `data` contains the following information:\n\n- `participantId`: Id of the participant that started or stopped sharing his screen.\n- `sharing`: True if the participant is sharing his screen, false otherwise.\n\n##### PARTICIPANTS_INFO_RETRIEVED\n\nBroadcasted when a RETRIEVE_PARTICIPANTS_INFO action is called. The `data` HashMap contains a `participantsInfo` key\nwith a list of participants information and a `requestId` key with the ID that was sent in the\nRETRIEVE_PARTICIPANTS_INFO action.\n\n##### CHAT_MESSAGE_RECEIVED\n\nBroadcasted when a chat text message is received. `data` contains the following information:\n\n- `senderId`: the id of the participant that sent the message.\n- `message`: the content of the message.\n- `isPrivate`: true if the message is private, false otherwise.\n- `timestamp`: the (optional) timestamp of the message.\n\n##### CHAT_TOGGLED\n\nBroadcasted when the chat dialog is opened or closed. `data` contains the following information:\n\n- `isOpen`: true if the chat dialog is open, false otherwise.\n\n##### VIDEO_MUTED_CHANGED\n\nBroadcasted when the local participant's video is muted or unmuted. `data` contains the following information:\n\n- `muted`: an integer indicating whether the video is muted or not. 0 means unmuted, and 4 means muted.\n\n##### READY_TO_CLOSE\n\nThe SDK is ready to be closed / dismissed.\n\n##### CUSTOM_BUTTON_PRESSED\n\nBroadcasted when a custom button is pressed. `data` contains the following information:\n\n- `id`: the id of the pressed custom button.\n- `text`: the label of the pressed custom button.\n\n##### CONFERENCE_UNIQUE_ID_SET\n\nBroadcasted when an meeting unique id has been set. `data` contains the following information:\n\n- `sessionId`: the unique meeting id.\n\n### Broadcasting Actions\n\nThe SDK listens for broadcasted actions from the users and reacts accordingly.\n\n```java\n    Intent muteBroadcastIntent = new Intent(BroadcastAction.Type.SET_AUDIO_MUTED.getAction());\n    muteBroadcastIntent.putExtra(\"muted\", muted);\n    LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(muteBroadcastIntent);\n ```\n\nThe intents can be built manually (as shown above) or through the methods in `BroadcastIntentHelper`.\n\nPlease see `JitsiMeetOngoingConferenceService` for more examples of sending actions.\n\n#### Supported actions\n\n##### SET_AUDIO_MUTED\nSets the state of the localParticipant audio muted according to the `muted` parameter.\nExpects a `muted` key on the intent extra with a boolean value.\n\n##### SET_VIDEO_MUTED\nSets the state of the localParticipant video muted according to the `muted` parameter.\nExpects a `muted` key on the intent extra with a boolean value.\n\n##### HANG_UP\nThe localParticipant leaves the current conference.\nDoes not expect any extra value.\n\n##### SEND_ENDPOINT_TEXT_MESSAGE\nSends a message via the data channel to one particular participant or all of them.\nExpects a `to` key on the intent extra with the ID of the participant to which the message \nis meant and a `message` key with a string value, the actual content of the message. \nIf the `to` key is not present or its value is empty, the message will be sent \nto all the participants in the conference.\n\nTo get the participantId, the `PARTICIPANT_JOINED` event should be listened for,\nwhich `data` includes the id and this should be stored somehow.\n\n##### TOGGLE_SCREEN_SHARE\nSets the state of the localParticipant screen share according to the `enabled` parameter.\nExpects an `enabled` key on the intent extra with a boolean value.\n\n##### RETRIEVE_PARTICIPANTS_INFO\nSignals the SDK to retrieve a list with the participant's information. The SDK will emit a PARTICIPANTS_INFO_RETRIEVED event.\nExpects a `requestId` key on the intent extra with a string value, this parameter will be present on the PARTICIPANTS_INFO_RETRIEVED event.\n\n##### OPEN_CHAT\nOpens the chat dialog. If a `to` key is present with a valid participantId, the private chat for that particular participant will be opened.\n\n##### CLOSE_CHAT\nCloses the chat dialog.\nDoes not expect any extra value.\n\n##### SEND_CHAT_MESSAGE\nSends a chat message, either a private one if a `to` key is present with a valid participantId and to everybody otherwise.\nExpect a `message` key with a string value.\n\n##### SHOW_NOTIFICATION\nShow a notification that can be configured based on `appearance`, `description`, `timeout`, `title` and an `uid`.\n\n##### HIDE_NOTIFICATION\nHides a notification according to its `uid`.\n\n##### START_RECORDING\nStarts the recording by setting up a `mode`, either as a `file` or a `stream`, which can also have a `dropboxToken`, `shouldShare`\nyou can provide a `rtmpStreamKey`, `rtmBroadcastID`, `youtubeStreamKey`, `youtubeBroadcastID`, other `extraMetadata`. You can also enable\n`transcription` through this action.\n\n##### STOP_RECORDING\nStops the recording based on `mode` and also can stop `transcription` if it was enabled.\n\n##### OVERWRITE_CONFIG\nOverwrites `config` during the meeting.\n\n##### SEND_CAMERA_FACING_MODE_MESSAGE\nSends a message `to` with the `facingMode` via data channel. \n\n## ProGuard rules\n\nWhen using the SDK on a project some proguard rules have to be added to avoid necessary code being stripped. Add the following to your project's\nrules file: https://github.com/jitsi/jitsi-meet/blob/master/android/app/proguard-rules.pro\n\n## Picture-in-Picture\n\n`JitsiMeetView` will automatically adjust its UI when presented in a\nPicture-in-Picture style scenario, in a rectangle too small to accommodate its\n\"full\" UI.\n\n## Dropbox integration\n\nTo set up the Dropbox integration, follow these steps:\n\n1. Add the following to the app's AndroidManifest.xml and change `<APP_KEY>` to\nyour Dropbox app key:\n\n```xml\n<activity\n    android:configChanges=\"keyboard|orientation\"\n    android:launchMode=\"singleTask\"\n    android:name=\"com.dropbox.core.android.AuthActivity\">\n  <intent-filter>\n    <action android:name=\"android.intent.action.VIEW\" />\n    <category android:name=\"android.intent.category.BROWSABLE\" />\n    <category android:name=\"android.intent.category.DEFAULT\" />\n    <data android:scheme=\"db-<APP_KEY>\" />\n  </intent-filter>\n</activity>\n```\n\n2. Add the following to the app's strings.xml and change `<APP_KEY>` to your\nDropbox app key:\n\n```xml\n<string name=\"dropbox_app_key\"><APP_KEY></string>\n```\n"
  },
  {
    "path": "docs/dev-guide/configuration.md",
    "content": "---\nid: dev-guide-configuration\ntitle: Configuration\n---\n\nThis page describes available configuration options for Jitsi Meet. These are either set in `config.js` on the server\nside or overridden in the app.\n\n:::note\nOptions marked with 🚫 are not overwritable through `configOverwrite`\n:::\n\n:::warning\nThis page is a work in progress. Not all options are described here yet.\n:::\n\n## API\n\n### apiLogLevels\n\ntype: `Array`\n\nLogs that should go be passed through the 'log' event if a handler is defined for it\n\nDefault: **unset**\n\n```javascript\napiLogLevels: ['warn', 'log', 'error', 'info', 'debug']\n```\n\n### buttonsWithNotifyClick\n\ntype: `Array`\n\nToolbar buttons which have their click/tap event exposed through the API on `toolbarButtonClicked`. Passing a string for the button key will prevent execution of the click/tap routine; passing an object with `key` and `preventExecution` flag on false will not prevent execution of the click/tap routine. Below array with mixed mode for passing the buttons.\n\nDefault: **unset**\n\n```javascript\nbuttonsWithNotifyClick: [\n    'camera',\n    {\n        key: 'chat',\n        preventExecution: false\n    },\n    {\n        key: 'closedcaptions',\n        preventExecution: true\n    },\n    'desktop',\n    'download',\n    'embedmeeting',\n    'etherpad',\n    'feedback',\n    'filmstrip',\n    'fullscreen',\n    'hangup',\n    'help',\n    {\n        key: 'invite',\n        preventExecution: false\n    },\n    'livestreaming',\n    'microphone',\n    'mute-everyone',\n    'mute-video-everyone',\n    'participants-pane',\n    'profile',\n    {\n        key: 'raisehand',\n        preventExecution: true\n    },\n    'recording',\n    'security',\n    'select-background',\n    'settings',\n    'shareaudio',\n    'sharedvideo',\n    'shortcuts',\n    'stats',\n    'tileview',\n    'toggle-camera',\n    'videoquality',\n    // The add passcode button from the security dialog.\n    {\n        key: 'add-passcode',\n        preventExecution: false\n    },\n    '__end'\n]\n```\n\n### customParticipantMenuButtons\n\ntype: `Array<{ icon: string; id: string; text: string; }>`\n\nDefault: **unset**\n\nA list of custom buttons that can be added to the Participant Context Menu. Each will have an icon, that can be either a base64 encoded image or the path to an image, a unique id, and a text that will be displayed alongside the icon in the menu. This custom button will trigger the `participantMenuButtonClick` event that will have the id set to the button as the `key` and the `participantId`, representing the id of the participant for which the button was clicked.\n\n```javascript\ncustomParticipantMenuButtons: [\n    {\n        icon: 'data:image/svg+xml;base64,...',\n        id: 'custom-button',\n        text: 'Custom Button'\n    }\n]\n```\n\n### customToolbarButtons\n\ntype: `Array<{ icon: string; id: string; text: string; }>`\n\nDefault: **unset**\n\nA list of custom buttons that can be added to the Toolbar. Each will have an icon, that can be either a base64 encoded image or the path to an image, a unique id, and a text that will be displayed alongside the icon in the menu. This custom button will trigger the `toolbarButtonClicked` event that will the id set to the button as the `key`.\n\n```javascript\ncustomToolbarButtons: [\n    {\n        icon: 'data:image/svg+xml;base64,...',\n        id: 'custom-toolbar-button',\n        text: 'Custom Toolbar Button'\n    }\n]\n```\n\n### mouseMoveCallbackInterval\n\ntype: `Number`\n\nDefault interval (milliseconds) for triggering `mouseMoved` iframe API event.\n\nDefault: `1000`\n\n```javascript\nmouseMoveCallbackInterval: 1000\n```\n\n### participantMenuButtonsWithNotifyClick\n\ntype: `Array`\n\nParticipant context menu buttons which have their click/tap event exposed through the API on `participantMenuButtonClick`. Passing a string for the button key will prevent execution of the click/tap routine; passing an object with `key` and `preventExecution` flag on false will not prevent execution of the click/tap routine. Below array with mixed mode for passing the buttons.\n\nDefault: **unset**\n\n```javascript\nparticipantMenuButtonsWithNotifyClick: [\n    'allow-video',\n    {\n        key: 'ask-unmute',\n        preventExecution: false\n    },\n    'conn-status',\n    'flip-local-video',\n    'grant-moderator',\n    {\n        key: 'kick',\n        preventExecution: true\n    },\n    {\n        key: 'hide-self-view',\n        preventExecution: false\n    },\n    'mute',\n    'mute-others',\n    'mute-others-video',\n    'mute-video',\n    'pinToStage',\n    'privateMessage',\n    {\n        key: 'remote-control',\n        preventExecution: false\n    },\n    'send-participant-to-room',\n    'verify',\n]\n```\n\n### useHostPageLocalStorage\n\ntype: `Boolean`\n\nThis property is related to the use case when Jitsi Meet is used via the IFrame API. When the property is true\nJitsi Meet will use the local storage of the host page instead of its own. This option is useful if the browser\nis not persisting the local storage inside the iframe.\n\nDefault: **unset**\n\n```javascript\nuseHostPageLocalStorage: true\n```\n\n## Audio\n\n### audioLevelsInterval\n\ntype: `Number`\n\nThe interval (milliseconds) at which the audio levels are calculated.\n\nDefault: `200`\n\n```javascript\naudioLevelsInterval: 200\n```\n\n### audioQuality\n\ntype: `Object`\n\nSpecify audio quality stereo and opusMaxAverageBitrate values in order to enable HD audio.\nBeware, by doing so, you are disabling echo cancellation, noise suppression and AGC.\n\nDefault: **unset**\n\n```javascript\naudioQuality: {\n    stereo: false,\n    opusMaxAverageBitrate: null // Value to fit the 6000 to 510000 range.\n}\n```\n\n### disableAudioLevels\n\ntype: `Boolean`\n\nDisable measuring of audio levels.\n\nDefault: `false`\n\n```javascript\ndisableAudioLevels: false\n```\n\n### ~~disableSpeakerStatsSearch~~\n\ntype: `Boolean`\n\nSpecifies whether there will be a search field in speaker stats or not.\n\n__DEPRECATED__ Use `speakerStats.disableSearch` instead.\n\nDefault: false\n\n```javascript\ndisableSpeakerStatsSearch: false\n```\n\n### disabledSounds\n\ntype: `Array`\n\nThe sounds passed in this array will be disabled.\n\nDefault: **unset**\n\n```javascript\ndisabledSounds: [\n    // 'ASKED_TO_UNMUTE_SOUND'\n    // 'E2EE_OFF_SOUND'\n    // 'E2EE_ON_SOUND'\n    // 'INCOMING_MSG_SOUND'\n    // 'KNOCKING_PARTICIPANT_SOUND'\n    // 'LIVE_STREAMING_OFF_SOUND'\n    // 'LIVE_STREAMING_ON_SOUND'\n    // 'NO_AUDIO_SIGNAL_SOUND'\n    // 'NOISY_AUDIO_INPUT_SOUND'\n    // 'OUTGOING_CALL_EXPIRED_SOUND'\n    // 'OUTGOING_CALL_REJECTED_SOUND'\n    // 'OUTGOING_CALL_RINGING_SOUND'\n    // 'OUTGOING_CALL_START_SOUND'\n    // 'PARTICIPANT_JOINED_SOUND'\n    // 'PARTICIPANT_LEFT_SOUND'\n    // 'RAISE_HAND_SOUND'\n    // 'REACTION_SOUND'\n    // 'RECORDING_OFF_SOUND'\n    // '_ON_SOUND'\n    // 'TALK_WHILE_MUTED_SOUND'\n]\n```\n\n### enableNoAudioDetection\n\ntype: `Boolean`\n\nEnabling this will run the lib-jitsi-meet no audio detection module which\nwill notify the user if the current selected microphone has no audio\ninput and will suggest another valid device if one is present.\n\nDefault: `true`\n\n```javascript\nenableNoAudioDetection: true\n```\n\n### enableNoisyMicDetection\n\ntype: `Boolean`\n\nEnabling this will run the lib-jitsi-meet noise detection module which will\nnotify the user if there is noise, other than voice, coming from the current\nselected microphone. The purpose it to let the user know that the input could\nbe potentially unpleasant for other meeting participants.\n\nDefault: `true`\n\n```javascript\nenableNoisyMicDetection: true\n```\n\n### speakerStats\n\ntype: `Object`\n\nOptions related to the speaker stats feature.\n\nProperties: \n\n* `disabled` - Specifies whether the speaker stats is enable or not.\n* `disableSearch` - Specifies whether there will be a search field in speaker stats or not.\n* `order` - Specifies whether participants in speaker stats should be ordered or not, and with what priority.\n\nDefault:\n\n```javascript\nspeakerStats: {\n    disabled: false,\n    disableSearch: false,\n    order: [\n        'role', // Moderators on top.\n        'name', // Alphabetically by name.\n        'hasLeft', // The ones that have left in the bottom.\n    ], // the order of the array elements determines priority.\n}\n```\n\n### ~~speakerStatsOrder~~\n\ntype: `Array`\n\nSpecifies whether participants in speaker stats should be ordered or not, and with what priority.\n\n__DEPRECATED__ Use `speakerStats.order` instead.\n\nDefault:\n ```javascript\n    speakerStatsOrder: [\n        'role', // Moderators on top.\n        'name', // Alphabetically by name.\n        'hasLeft', // The ones that have left in the bottom.\n    ], // the order of the array elements determines priority.\n```\n\n### startAudioMuted\n\ntype: `Number`\n\nEvery participant after the Nth will start audio muted.\n\nDefault: **unset**\n\n```javascript\nstartAudioMuted: 10\n```\n\n### startAudioOnly\n\ntype: `Boolean`\n\nStart the conference in audio only mode (no video is being received nor sent).\n\nDefault: **unset**\n\n```javascript\nstartAudioOnly: false\n```\n\n### startSilent\n\ntype: `Boolean`\n\nEnabling it (with #params) will disable local audio output of remote\nparticipants and to enable it back a reload is needed.\n\nDefault: **unset**\n\n```javascript\nstartSilent: false\n```\n\n### startWithAudioMuted\n\ntype: `Boolean`\n\nStart calls with audio muted. This option is only applied locally.\n\nDefault: **unset**\n\n```javascript\nstartWithAudioMuted: false\n```\n\n## Breakout rooms\n\n### breakoutRooms\n\ntype: `Object`\n\nOptions related to the breakout rooms feature.\n\nDefault: **unset**\n\nProperties:\n* `hideAddRoomButton` - Hides the add breakout room button. This replaces `hideAddRoomButton`.\n* `hideAutoAssignButton` - Hides the auto assign participants button.\n* `hideJoinRoomButton` - Hides the join breakout room button.\n* `hideModeratorSettingsTab` - Hides the button to open the moderator settings tab.\n* `hideMoreActionsButton` - Hides the more actions button.\n* `hideMuteAllButton` - Hides the mute all button.\n\n```javascript\nbreakoutRooms: {\n    hideAddRoomButton: false,\n    hideAutoAssignButton: false,\n    hideJoinRoomButton: false\n}\n```\n\n### ~~hideAddRoomButton~~\n\ntype: `Boolean`\n\n__DEPRECATED__ Use `breakoutRooms.hideAddRoomButton` instead.\n\nHides add breakout room button.\n\nDefault: `false`\n\n```javascript\nhideAddRoomButton: false\n```\n\n## Analytics\n\n### enableDisplayNameInStats\n\ntype: `Boolean`\n\nEnables sending participants' display names to analytics services.\n\n```javascript\nenableDisplayNameInStats: false\n```\n\n### enableEmailInStats\n\ntype: `Boolean`\n\nEnables sending participants' emails (if available) to analytics services.\n\n```javascript\nenableEmailInStats: false\n```\n\n### feedbackPercentage\n\ntype: `Number`\n\nControls the percentage of automatic feedback shown to participants when analytics is enabled.\nThe default value is 100%. If set to 0, no automatic feedback will be requested.\n\n```javascript\nfeedbackPercentage: 100\n```\n\n## Transcriptions\n\n### autoCaptionOnRecord \n\n__DEPRECATED__ Use `transcription.autoTranscribeOnRecord` instead.\n\n### preferredTranscribingLanguage\n\n__DEPRECATED__ Use `transcription.preferredLanguage` instead.\n\n### transcribeWithAppLanguage\n\n__DEPRECATED__ Use `transcription.useAppLanguage` instead.\n\n### transcribingEnabled\n\n__DEPRECATED__ Use `transcription.enabled` instead.\n\n### transcription\n\ntype: `Object`\n\nTranscription related options.\n\nProperties:\n\n* `enabled` - Enable transcription (in interface_config, subtitles and buttons can be configured). Default `false`.\n* `translationLanguages` - Translation languages. Available languages can be found in ./lang/translation-languages.json.\n* `useAppLanguage` - If `true` the transcriber will use the application language. The application language is either explicitly set by participants in their settings or automatically detected based on the environment, e.g. if the app is opened in a Chrome instance which is using French as its default language then transcriptions for that participant will be in french. Default: `true`.\n* `preferredLanguage` - Transcriber language. This settings will only work if `useAppLanguage` is explicitly set to `false`. Available languages can be found [here](https://github.com/jitsi/jitsi-meet/blob/master/react/features/transcribing/transcriber-langs.json). Default: `'en-US'`.\n* `autoTranscribeOnRecord` - Enables automatic turning on transcribing when recording is started. Default: `true`.\n\n```javascript\ntranscription: {\n    enabled: true,\n    translationLanguages: ['en-US', 'es'],\n    useAppLanguage: false,\n    preferredLanguage: 'en-US',\n    autoTranscribeOnRecord: true\n}\n```\n\n## Connection\n\n### bosh*\n\ntype: `String`\n\nThe BOSH URL.\n\n```javascript\nbosh: '//jitsi-meet.example.com/http-bind'\n```\n\n### disableRtx\n\ntype: `Boolean`\n\nDisables or enables RTX (RFC 4588).\n\nDefault: `false`\n\n```javascript\ndisableRtx: true\n```\n\n### disableSimulcast\n\ntype: `Boolean`\n\nEnable / disable simulcast support.\n\nDefault: `false`\n\n```javascript\ndisableSimulcast: true\n```\n\n### e2ee\n\ntype: `Object`\n\nConfigure End-to-End Encryption.\n\n```javascript\ne2ee: {\n    labels: {\n        labelTooltip: 'Tooltip',\n        description: 'Description',\n        label: 'E2EE',\n        warning: 'Warning'\n    },\n    externallyManagedKey: false\n}\n```\n\n### e2eping\n\ntype: `Object`\n\nOptions related to end-to-end (participant to participant) ping.\n\nProperties:\n* `enabled` - Whether end-to-end pings should be enabled.\n* `numRequests` - The number of responses to wait for.\n* `maxConferenceSize` - The max conference size in which e2e pings will be sent.\n* `maxMessagesPerSecond` - The maximum number of e2e ping messages per second for the whole conference to aim for.\n    This is used to contol the pacing of messages in order to reduce the load on the backend.\n\n```javascript\ne2eping: {\n    enabled: false,\n    numRequests: 5,\n    maxConferenceSize: 200,\n    maxMessagesPerSecond: 250\n}\n```\n\n### enableEncodedTransformSupport\n\ntype: `Boolean`\n\nEnable support for encoded transform in supported browsers. This allows\nE2EE to work in Safari if the corresponding flag is enabled in the browser.\n**Experimental**.\n\n```javascript\nenableEncodedTransformSupport: false\n```\n\n### enableForcedReload 🚫\n\ntype: `Boolean`\n\nEnables forced reload of the client when the call is migrated as a result of\nthe bridge going down.\n\n```javascript\nenableForcedReload: true\n```\n\n### gatherStats\n\ntype: `Boolean`\n\nWhether to enable stats collection or not in the `TraceablePeerConnection`.\nThis can be useful for debugging purposes (post-processing/analysis of\nthe WebRTC stats) as it is done in the jitsi-meet-torture bandwidth\nestimation tests.\n\n```javascript\ngatherStats: false\n```\n\n### hosts\n\ntype: `Object`\n\n⚠️ **Note:** Since commit `97146ed`, the `hosts` configuration can **no longer be overridden** via `configOverwrite`.  \nIt must be defined only in the main `config.js` served by your deployment.\n\nThis option previously supported dynamic overrides (e.g., for custom domains or hidden domains),  \nbut that functionality has been removed from the client for security and consistency reasons.\n\nIf you need to separate users into different domains (for example, to hide participants from each other),  \nuse the **Visitors / Lobby / JWT roles** features instead of host overrides.\n\n---\n\n#### Properties\n\n- `domain` — XMPP domain  \n- `anonymousdomain` — Domain for guest / unauthenticated users  \n- `authdomain` — Domain for authenticated users (defaults to `domain`)  \n- `focus` — Focus component domain (defaults to `focus.<domain>`)  \n- `muc` — XMPP MUC domain\n\n#### Example\n\n```javascript\nhosts: {\n    domain: 'jitsi-meet.example.com',\n    anonymousdomain: 'guest.example.com',\n    authdomain: 'jitsi-meet.example.com',\n    focus: 'focus.jitsi-meet.example.com',\n    muc: 'conference.jitsi-meet.example.com'\n}\n```\n### p2p\n\ntype: `Object`\n\nPeer-To-Peer mode: used (if enabled) when there are just 2 participants.\n\nProperties:\n* `enabled` - Enables peer to peer mode. When enabled the system will try to\n    establish a direct connection when there are exactly 2 participants\n    in the room. If that succeeds the conference will stop sending data\n    through the JVB and use the peer to peer connection instead. When a\n    3rd participant joins the conference will be moved back to the JVB\n    connection.\n* `iceTransportPolicy` - Sets the ICE transport policy for the p2p connection. At the time\n    of this writing the list of possible values are `all` and `relay`,\n    but that is subject to change in the future. The enum is defined in\n    the [WebRTC standard](https://www.w3.org/TR/webrtc/#rtcicetransportpolicy-enum).\n    If not set, the effective value is `all`.\n* `codecPreferenceOrder` - Provides a way to set the codec preference on desktop based endpoints.\n* `mobileCodecPreferenceOrder` - Provides a way to set the codec preference on mobile devices, both on RN and mobile browser based endpoints.\n* `backToP2PDelay` - How long we're going to wait, before going back to P2P after the 3rd\n    participant has left the conference (to filter out page reload).\n* `stunServers` - The STUN servers that will be used in the peer to peer connections.\n\n```javascript\np2p: {\n    enabled: true,\n    enableUnifiedOnChrome: false,\n    iceTransportPolicy: 'all',\n    backToP2PDelay: 5,\n    stunServers: [\n        { urls: 'stun:jitsi-meet.example.com:3478' },\n        { urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }\n    ]\n}\n```\n\n### pcStatsInterval\n\ntype: `Number`\n\nThe interval at which PeerConnection.getStats() is called.\n\nDefault: `10000`\n\n```javascript\npcStatsInterval: 50000\n```\n\n### peopleSearchQueryTypes\n\ntype: `Array`\n\nThe entity types which are queriable when inviting people in a room. Valid values are \"phone\", \"room\", \"sip\", \"user\", \"videosipgw\" and \"email\". Authentication for Jitsi entity types is done by passing a jwt, authentication for external entity types (e. g. email) is done by passing an alternative token (e. g. peopleSearchTokenLocation).\n\nDefault: `[]`\n\n```javascript\npeopleSearchQueryTypes: [\"user\", \"email\"]\n```\n\n### peopleSearchUrl\n\ntype: `String`\n\nDirectory endpoint which is called for invite dialog autocomplete. Expected response format is an array of objects. Each object should be formatted as follows:\n\n```javascript\n{\n    id: int,\n    type: string, # the entity type (phone, room, user, email etc.),\n    name: string, # the entity display name\n    avatar?: string, # full URL to the entity picture, not mandatory\n    number?: string, # required for phone numbers\n}\n```\n\nDefault: `\"\"`\n\n```javascript\npeopleSearchUrl: \"https://myservice.com/api/people\"\n```\n\n### inviteServiceUrl\n\ntype: `String`\n\nEndpoint which is called to send invitation requests. The request is made in POST and contains as a POST body an array of objects formatted the same as the peopleSearchUrl response body.\n\nDefault: `\"\"`\n\n```javascript\ninviteServiceUrl: \"https://myservice.com/api/invite\"\n```\n\n### peopleSearchTokenLocation\n\ntype: `String`\n\nUseful for authentication against directories holding entities which don't exist in Prosody (e. g. email). This indicates the localStorage key where the alternate authentication token value is to be found. This alternate token will be used if the JWT value is not set. It will be sent in the Authorization: Bearer header, as the JWT would have been.\n\n\nDefault: `\"\"`\n\n```javascript\npeopleSearchTokenLocation: \"service_token\"\n```\n\n### useTurnUdp\n\ntype: `Boolean`\n\nUse TURN/UDP servers for the jitsi-videobridge connection (by default\nwe filter out TURN/UDP because it is usually not needed since the\nbridge itself is reachable via UDP)\n\n```javascript\nuseTurnUdp: false\n```\n\n### webrtcIceTcpDisable\n\ntype: `Boolean`\n\nDisables ICE/TCP by filtering out local and remote TCP candidates in signalling.\n\n```javascript\nwebrtcIceTcpDisable: false\n```\n\n### webrtcIceUdpDisable\n\ntype: `Boolean`\n\nDisables ICE/UDP by filtering out local and remote UDP candidates in signalling.\n\n```javascript\nwebrtcIceUdpDisable: false\n```\n\n### websocket 🚫\n\ntype: `String`\n\nWebsocket URL\n\n```javascript\nwebsocket: 'wss://jitsi-meet.example.com/xmpp-websocket'\n```\n\n## Etherpad\n\n### etherpad_base\n\ntype: `String`\n\nIf set, it adds a \"Open shared document\" link to the bottom right menu that\nwill open an etherpad document.\n\n```javascript\netherpad_base: 'https://your-etherpad-installati.on/p/'\n```\n\n### openSharedDocumentOnJoin\n\ntype: `Boolean`\n\nIf etherpad integration is enabled, setting this to `true` will\nautomatically open the etherpad when a participant joins.  This\ndoes not affect the mobile app since opening an etherpad\nobscures the conference controls -- it's better to let users\nchoose to open the pad on their own in that case.\n\n```javascript\nopenSharedDocumentOnJoin: false\n```\n\n## Filmstrip\n\n### disableFilmstripAutohiding\n\ntype: `Boolean`\n\nPrevents the filmstrip from autohiding when screen width is under a certain threshold\n\nDefault: `false`\n\n```javascript\ndisableFilmstripAutohiding: true\n```\n\n### filmstrip\n\ntype: `Object`\n\nOptions related to the filmstrip.\n\nDefault: **unset**\n\nProperties:\n* `disableResizable` - Disables user resizable filmstrip. This also allows configuration of the filmstrip\n    (width, tiles aspect ratios) through the interfaceConfig options.\n* `disableStageFilmstrip` - Disables the stage filmstrip (displaying multiple\n    participants on stage besides the vertical filmstrip)\n\n```javascript\nfilmstrip: {\n    disableResizable: true,\n    disableStageFilmstrip: false\n}\n```\n\n### disableCameraTintForeground\n\ntype: `Boolean`\n\nDefault: **unset**\n\nIf true disable the camera tint foreground on the active speaker in the filmstrip\n\n```javascript\ndisableCameraTintForeground: true\n```\n\n## Face Landmarks\n\n### faceLandmarks\n\ntype: `Object`\n\nOptions related to the face landmarks features.\n\nProperties:\n* `enableFaceCentering` - Enables centering faces within a video by sharing face coordinates.\n* `enableFaceExpressionsDetection` - Enables detecting face expressions from video.\n* `enableDisplayFaceExpressions` - Enables displaying face expressions in speaker stats.\n* `enableRTCStats` - Enables anonymized stats collection for face landmarks.\n* `faceCenteringThreshold` - Minimum required face movement percentage threshold for sending new face centering coordinates data.\n* `captureInterval` - Milliseconds for processing a new image capture in order to detect face landmarks.\n\n```javascript\nfaceLandmarks: {\n        enableFaceCentering: false,\n        enableFaceExpressionsDetection: false,\n        enableDisplayFaceExpressions: false,\n        enableRTCStats: false,\n        faceCenteringThreshold: 20,\n        captureInterval: 1000\n},\n```\n\n## Giphy\n\n### giphy\n\ntype: `Object`\n\nSetup for the Giphy integration.\n\nProperties:\n* `enabled` - Whether the feature is enabled or not.\n* `sdkKey` - SDK API Key from Giphy.\n* `displayMode` - Display mode can be one of:\n    - `tile` - show the GIF on the tile of the participant that sent it.\n    - `chat` - show the GIF as a message in chat.\n    - `all` - all of the above. This is the default option.\n* `tileTime` - How long the GIF should be displayed on the tile (in milliseconds).\n* `rating` - Limit results by audience rating: \n    - `g` - broadly accepted as appropriate in a public environment. This is the default option.\n    - `pg` - commonly witnessed in a public environment, but not as broadly accepted as appropriate.\n    - `pg-13` - typically not seen unless sought out, but still commonly witnessed.\n    - `r` - typically not seen unless sought out, and could be considered alarming if witnessed.\n\n```javascript\ngiphy: {\n    enabled: true,\n    sdkKey: 'example-key',\n    displayMode: 'tile',\n    tileTime: 7000,\n    rating: 'pg'\n}\n```\n\n## Gravatar\n\n### gravatar\n\ntype: `Object`\n\nSetup for Gravatar-compatible services.\n\nProperties:\n* `baseUrl` 🚫 - Base URL for a Gravatar-compatible service. Defaults to Gravatar.\n* `disabled` - True if Gravatar should be disabled.\n\n```javascript\ngravatar: {\n    baseUrl: 'https://www.gravatar.com/avatar/',\n    disabled: false\n}\n```\n\n### ~~gravatarBaseURL~~ 🚫\n\ntype: `String`\n\n__DEPRECATED__ Use `gravatar.baseUrl` instead.\n\nBase URL for a Gravatar-compatible service.\n\nDefault: 'https://www.gravatar.com/avatar/'\n\n```javascript\ngravatarBaseURL: 'https://www.gravatar.com/avatar/'\n```\n\n## LastN\n\n### channelLastN\n\ntype: `Number`\n\nDefault value for the channel \"last N\" attribute. -1 for unlimited.\n\n```javascript\nchannelLastN: -1\n```\n\n### startLastN\n\ntype: `Number`\n\nProvides a way for the lastN value to be controlled through the UI.\nWhen startLastN is present, conference starts with a last-n value of startLastN and channelLastN\nvalue will be used when the quality level is selected using \"Manage Video Quality\" slider.\n\n```javascript\nstartLastN: 1\n```\n\n## Lobby\n\n### ~~autoKnockLobby~~\n\ntype: `Boolean`\n\n__DEPRECATED__ Use `lobby.autoKnock` instead.\n\nIf Lobby is enabled starts knocking automatically.\n\n```javascript\nautoKnockLobby: false\n```\n\n### ~~enableLobbyChat~~\n\ntype: `Boolean`\n\n__DEPRECATED__ Use `lobby.enableChat` instead.\n\nEnable lobby chat.\n\n```javascript\nenableLobbyChat: false\n```\n\n### ~~hideLobbyButton~~\n\ntype: `Boolean`\n\n__DEPRECATED__ Use `securityUi.hideLobbyButton` instead.\n\nHide the lobby button.\n\n```javascript\nhideLobbyButton: false\n```\n\n### lobby\n\ntype: `Object`\n\nOptions related to the lobby screen.\n\nDefault: **unset**\n\nProperties:\n* `autoKnock` - If the lobby is enabled, it starts knocking automatically. Replaces `autoKnockLobby`.\n* `enableChat` - Enables the lobby chat. Replaces `enableLobbyChat`.\n\n```javascript\nlobby: {\n    autoKnock: true,\n    enableChat: false\n}\n```\n\n## Moderator\n\n### disableModeratorIndicator\n\ntype: `Boolean`\n\nHides the moderator indicators.\n\nDefault: `false`\n\n```javascript\ndisableModeratorIndicator: true\n```\n\n### disableReactionsModeration\n\ntype: `Boolean`\n\nDisables the moderation of reactions feature.\n\nDefault: `false`\n\n```javascript\ndisableReactionsModeration: true\n```\n\n### disableRemoteMute\n\ntype: `Boolean`\n\nDisables muting operations of remote participants.\n\nDefault: `false`\n\n```javascript\ndisableRemoteMute: true\n```\n\n## Notifications\n\n### notifications\n\ntype: `Array`\n\nUse this array to configure which notifications will be shown to the user.\nThe items correspond to the title or description key of that notification.\nSome of these notifications also depend on some other internal logic to be displayed or not,\nso adding them here will not ensure they will always be displayed.\n\nA falsy value for this prop will result in having all notifications enabled (e.g null, undefined, false).\n\n```javascript\nnotifications: []\n```\n\n### disabledNotifications\n\ntype: `Array`\n\nList of notifications to be disabled. Works in tandem with the above setting.\n\n```javascript\ndisabledNotifications: [\n    'notify.chatMessages', // shown when receiving chat messages while the chat window is closed\n    'notify.grantedTo', // shown when moderator rights were granted to a participant\n]\n```\n\n## Participants Pane\n\n### participantsPane\n\ntype: `Object`\n\nOptions related to the participants pane.\n\nDefault: **unset**\n\nProperties:\n* `hideModeratorSettingsTab` - Hides the button to open the moderator settings tab.\n* `hideMoreActionsButton` - Hides the more actions button.\n* `hideMuteAllButton` - Hides the mute all button.\n\n```javascript\nparticipantsPane: {\n    hideModeratorSettingsTab: false,\n    hideMoreActionsButton: false,\n    hideMuteAllButton: false\n}\n```\n\n## Recording\n\n### dropbox\n\ntype: `Object`\n\nEnable the dropbox integration.\n\nProperties:\n* `appKey` - Your APP Key.\n* `redirectURI` - A URL to redirect the user to, after authenticating by default uses\n\n```javascript\ndropbox: {\n    appKey: 'DROPBOX_APP_KEY',\n    redirectURI: 'https://jitsi-meet.example.com/subfolder/static/oauth.html'\n}\n```\n\n### fileRecordingsEnabled\n\ntype: `Boolean`\n\nWhether to enable file recording or not.\n\n```javascript\nfileRecordingsEnabled: false\n```\n\n### fileRecordingsServiceEnabled 🚫\n\ntype: `Boolean`\n\nWhen integrations like dropbox are enabled only that will be shown,\nby enabling fileRecordingsServiceEnabled, we show both the integrations\nand the generic recording service (its configuration and storage type\ndepends on jibri configuration)\n\n```javascript\nfileRecordingsServiceEnabled: true\n```\n\n### fileRecordingsServiceSharingEnabled 🚫\n\ntype: `Boolean`\n\nWhether to show the possibility to share file recording with other people\n(e.g. meeting participants), based on the actual implementation\non the backend.\n\n```javascript\nfileRecordingsServiceSharingEnabled: false\n```\n\n### hideRecordingLabel\n\ntype: `Boolean`\n\nSet recording label to auto hide instead of staying always on screen.\n\nDefault: `false`\n\n```javascript\nhideRecordingLabel: true\n```\n\n### localRecording\n\ntype: `Object`\n\nSet local recording configuration.\n\nProperties:\n* `disable` - Whether to disable the feature or not.\n* `notifyAllParticipants` - Whether to notify all the participants when a local recording is started.\n\n```javascript\nlocalRecording: {\n    disable: false,\n    notifyAllParticipants: true\n}\n```\n\n### recordingLimit 🚫\n\ntype: `Object`\n\nOptions for the recording limit notification.\n\nProperties:\n* `limit` - The recording limit in minutes. Note: This number appears in the notification text\n    but doesn't enforce the actual recording time limit. This should be configured in jibri!\n* `appName` = The name of the app with unlimited recordings.\n* `appURL` - The URL of the app with unlimited recordings.\n\n```javascript\nrecordingLimit: {\n    limit: 60,\n    appName: 'Unlimited recordings APP',\n    appURL: 'https://unlimited.recordings.app.com/'\n}\n```\n\n### recordings\n\ntype: `Object`\n\nOptions for the recordings features.\n\nProperties:\n* `recordAudioAndVideo` - If true (default) recording audio and video is selected by default in the recording dialog.\n* `suggestRecording` - If true, shows a notification at the start of the meeting with a call to action button to start recording (for users who can do so).\n* `showPrejoinWarning` - If true, shows a warning label in the prejoin screen to point out the possibility that the call you're joining might be recorded.\n* `showRecordingLink` - If true, the notification for recording start will display a link to download the cloud recording.\n\n```javascript\nrecordings: {\n    recordAudioAndVideo: true,\n    suggestRecording: false,\n    showPrejoinWarning: true,\n    showRecordingLink: true\n}\n```\n\n## Screen Sharing\n\n### desktopSharingFrameRate\n\ntype: `Object`\n\nOptional desktop sharing frame rate options\n\nDefault: `{\n    min: 5,\n    max: 5\n}`\n\n```javascript\ndesktopSharingFrameRate: {\n    min: 3,\n    max: 10\n}\n```\n\n### disableScreensharingVirtualBackground\n\ntype: `Boolean`\n\nDisables using screensharing as virtual background.\n\n```javascript\ndisableScreensharingVirtualBackground: false\n```\n\n### screenshotCapture\n\ntype: `Object`\n\nOptions for the screenshot capture feature.\n\nProperties:\n* `enabled` - Enables the feature\n* `mode` - The mode for the screenshot capture feature. Can be either 'recording' - screensharing screenshots\n    are taken only when the recording is also on, or 'always' - screensharing screenshots are always taken.\n\n```javascript\nscreenshotCapture: {\n    enabled: true,\n    mode: 'recording'\n}\n```\n\n## Security UI\n### securityUi\n\ntype: `Object`\n\nOptions regarding the security-related UI elements.\n\nDefault: **unset**\n\nProperties:\n* `hideLobbyButton` - Hides the lobby button. Replaces `hideLobbyButton`.\n* `disableLobbyPassword` - Hides the possibility to set and enter a lobby password.\n\n```javascript\nsecurityUi: {\n    hideLobbyButton: true,\n    disableLobbyPassword: false\n}\n```\n\n## Testing\n### testing\n\ntype: `Object`\n\nExperimental features.\n\nDefault: **unset**\n\nProperties:\n* `assumeBandwidth` - Allows the setting of a custom bandwidth value from the UI.\n* `disableE2EE` - Disables the End to End Encryption feature. Useful for debugging issues related to insertable streams.\n* `mobileXmppWsThreshold` - Enables XMPP WebSocket (as opposed to BOSH) for the given amount of users.\n* `p2pTestMode` - P2P test mode disables automatic switching to P2P when there are 2 participants in the conference.\n* `testMode` - Enables the test specific features consumed by jitsi-meet-torture.\n* `noAutoPlayVideo` - Disables the auto-play behavior of *all* newly created video element. This is useful when the client runs on a host with limited resources.\n\n```javascript\ntesting: {\n    assumeBandwidth: true,\n    disableE2EE: false,\n    mobileXmppWsThreshold: 10, // enable XMPP WebSockets on mobile for 10% of the users\n    p2pTestMode: false,\n    testMode: false,\n    noAutoPlayVideo: false\n}\n```\n\n## Video\n\n### constraints\n\ntype: `Object`\n\nW3C spec-compliant video constraints to use for video capture. Currently\nused by browsers that return true from lib-jitsi-meet's\n`util#browser#usesNewGumFlow`. The constraints are independent of\nthis config's resolution value. Defaults to requesting an ideal\nresolution of 720p.\n\n```javascript\nconstraints: {\n    video: {\n        height: {\n            ideal: 720,\n            max: 720,\n            min: 240\n        }\n    }\n}\n```\n\n### disableAddingBackgroundImages\n\ntype: `Boolean`\n\nWhen true the user cannot add more images to be used as a virtual background.\nOnly the default ones will be available.\n\n```javascript\ndisableAddingBackgroundImages: true\n```\n\n### disableH264\n\ntype: `Boolean`\n\nIf set to true, disable the H.264 video codec by stripping it out of the SDP.\n\n```javascript\ndisableH264: true\n```\n\n### disableLocalVideoFlip\n\ntype: `Boolean`\n\nDisable the Flip video option from the context menu for local video.\n\n```javascript\ndisableLocalVideoFlip: true\n```\n\n### disableSelfView\n\ntype: `Boolean`\n\nDisables self-view tile. (hides it from tile view and filmstrip)\n\n```javascript\ndisableSelfView: true\n```\n\n### doNotFlipLocalVideo\n\ntype: `Boolean`\n\nA property used to unset the default flip state of the local video.\nWhen it is set to `true`, the local(self) video will not be mirrored anymore.\n\n```javascript\ndoNotFlipLocalVideo: true\n```\n\n### maxFullResolutionParticipants\n\ntype: `Number`\n\nHow many participants while in the tile view mode, before the receiving video quality is reduced from HD to SD?\nUse `-1` to disable.\n\n```javascript\nmaxFullResolutionParticipants: 5\n```\n\n### resolution\n\ntype: `Number`\n\nSets the preferred resolution (height) for local video\n\nDefault: `720`\n\n```javascript\nresolution: 1080\n```\n\n### startVideoMuted\n\ntype: `Number`\n\nEvery participant after the Nth will start the video muted.\n\n```javascript\nstartVideoMuted: 5\n```\n\n### startWithVideoMuted\n\ntype: `Boolean`\n\nStart calls with video muted. Only applied locally.\n\n```javascript\nstartWithVideoMuted: true\n```\n\n### videoQuality\n\ntype: `Object`\n\nSpecify the settings for video quality optimizations on the client.\n\nProperties:\n* `codecPreferenceOrder` - Provides a way to set the codec preference on desktop-based endpoints.\n```javascript\ncodecPreferenceOrder: [ 'AV1', 'VP9', 'VP8', 'H264' ],\n```\n* `mobileCodecPreferenceOrder` - Provides a way to set the codec preference on mobile devices, both on RN and mobile browser-based endpoints.\n```javascript\ncodecPreferenceOrder: [ 'VP8', 'H264', 'VP9' ],\n```\nCodec specific settings for scalability modes and max bitrates.\n```javascript\nav1: {\n    maxBitratesVideo: {\n        low: 100000,\n        standard: 300000,\n        high: 1000000,\n        fullHd: 2000000,\n        ultraHd: 4000000,\n        ssHigh: 2500000\n    },\n    scalabilityModeEnabled: true,\n    useSimulcast: false,\n    useKSVC: true\n},\nh264: {\n    maxBitratesVideo: {\n        low: 200000,\n        standard: 500000,\n        high: 1500000,\n        fullHd: 3000000,\n        ultraHd: 6000000,\n        ssHigh: 2500000\n    },\n    scalabilityModeEnabled: true\n},\nvp8: {\n    maxBitratesVideo: {\n        low: 200000,\n        standard: 500000,\n        high: 1500000,\n        fullHd: 3000000,\n        ultraHd: 6000000,\n        ssHigh: 2500000\n    },\n    scalabilityModeEnabled: false\n},\nvp9: {\n    maxBitratesVideo: {\n        low: 100000,\n        standard: 300000,\n        high: 1200000,\n        fullHd: 2500000,\n        ultraHd: 5000000,\n        ssHigh: 2500000\n    },\n    scalabilityModeEnabled: true,\n    useSimulcast: false,\n    useKSVC: true\n},\n```\n* `minHeightForQualityLvl` - The options can be used to override default thresholds of video thumbnail heights corresponding to\n    the video quality levels used in the application. At the time of this writing, the allowed levels are:\n    *    `low` - for the low-quality level (180p at the time of this writing)\n    *    `standard` - for the medium quality level (360p)\n    *    `high` - for the high-quality level (720p)\n\n    The keys should be positive numbers which represent the minimal thumbnail height for the quality level.\n    With the default config value below the application will use 'low' quality until the thumbnails are\n    at least 360 pixels tall. If the thumbnail height reaches 720 pixels then the application will switch to\n    the high quality.\n\n## Whiteboard\n\n### whiteboard\n\ntype: `Object`\n\nOptions related to the Excalidraw whiteboard integration.\n\nDefault: **unset**\n\nProperties:\n* `enabled` - Whether the feature is enabled or not.\n* `collabServerBaseUrl` - The [server](https://github.com/jitsi/excalidraw-backend) used to support whiteboard collaboration.\n* `userLimit` - The user access limit to the whiteboard, introduced as a means to control the performance.\n* `limitUrl` - The url for more info about the whiteboard and its usage limitations.\n\n```javascript\nwhiteboard: {\n    enabled: true,\n    collabServerBaseUrl: 'https://excalidraw-backend.example.com',\n    userLimit: 25,\n    limitUrl: 'https://example.com/blog/whiteboard-limits'\n}\n"
  },
  {
    "path": "docs/dev-guide/contributing.md",
    "content": "---\nid: dev-guide-contributing\ntitle: Contributing Guidelines\n---\n\n# 🤝 How to Contribute \nWe greatly appreciate your willingness to contribute ❤️ Before you start working however, please take a moment to read and follow this brief guide.\n\n# 📥 Reporting Issues and Asking Questions \n\n- We prefer issues to be discussed first in the [community forum](https://community.jitsi.org/) and when confirmed, then an issue can be opened in the issue tracker of the appropriate project on GitHub.\n\n- Feel free to report ***any bugs, ask for new features or anything else*** you need help with. When opening an issue, please provide as much information as possible.\n\n- Please ask any general and implementation specific questions on the [community forum](https://community.jitsi.org/) for support.\n\n### 🪲 Bug Reports and Other Issues\n\nFor bugs please follow these steps:\n\n- **Provide Detailed Information:**\n  Include versions of Jitsi Meet, Jicofo, and JVB.\n\n- **Description of the Issue:**\n  Clearly explain the problem encountered.\n\n- **Reproduction Steps:**\n  Provide step-by-step instructions to recreate the issue.\n\n- **Expected Behavior:**\n  Describe the expected outcome when using the software.\n\n- **Actual Behavior:**\n  Explain what actually happened, including any error messages.\n\n### 💟 Feature Requests\n\nIf you have an idea for a new feature or something you'd like to see improved in Jitsi, here's how you can let us know:\n\n- **Describe the feature:** Specify the desired functionality.\n- **Provide examples:** Share similar features from other apps.\n- **Explain importance:** Justify the feature's relevance.\n- **Considerations:** Assess potential challenges.\n- **Additional details:** Include specific preferences.\n\n# Code Contributions \n\n- Visit the issue tracker ([Jitsi Meet's for example](https://github.com/jitsi/jitsi-meet/issues)) to find a list of open issues that need attention.\n\n- Discovered a bug or have a feature request and know how to fix it? Excellent! Keep reading 🔍\n\n- The [Developer Guide](/docs/category/developer-guide) will help you to setup a development environment to start working on the Jitsi Meet applications.\n\n## ✏️ Contributor License Agreement \nWhile the Jitsi projects are released under the\n[Apache License 2.0](https://github.com/jitsi/jitsi-meet/blob/master/LICENSE), the copyright\nholder and principal creator is [8x8](https://www.8x8.com/). To\nensure that we can continue making these projects available under an Open Source license,\nwe need you to sign our Apache-based contributor\nlicense agreement as either a [corporation](https://jitsi.org/ccla) or an\n[individual](https://jitsi.org/icla). If you cannot accept the terms laid out\nin the agreement, unfortunately, we cannot accept your contribution.\n\n## 🔁 Creating Pull Requests \n- Fork the repository to your GitHub account.\n- Create a new branch for your changes, based on the master branch. Choose a descriptive name for your branch.\n- Make **one** logical change per pull request to keep things organized.\n- Keep your commit history clean and concise. If necessary, squash multiple commits into one.\n- Rebase your branch onto the latest changes in the master branch before submitting the pull request. **Never** merge master, always rebase.\n\n### 📝 Commit messages\nJitsi projects follow the [Conventional Commits](https://www.conventionalcommits.org) spec, while making the scope\nmandatory.\n\nThat is, we use `feat(feature name) add some functionality` as opposed to `feat: add some functionality`. As projects\ngrow large, scoping down changes is helpful.\n\nThis is a non-exhaustive list of types of commits:\n\n```\n[\n  'build',\n  'chore',\n  'ci',\n  'docs',\n  'feat',\n  'fix',\n  'perf',\n  'refactor',\n  'revert',\n  'style',\n  'test'\n];\n```\n\nAs for what constitutes a \"feature\", that can vary across projects. \"Subsystem\" is another valid analogy.\nIn Jitsi Meet, for example, the feature name can be the feature where you made the changes: `react/features/<this>`.\nIn lib-jitsi-meet, the module: `modules/<this>`.\n\nUse your judgement and look into the commit history when in doubt.\n\n## ❗️For 8x8 employees\n- Don't link any internal resources such as Jira issues, this is an Open Source project\n\n## 📝 Coding Style\n\n### Comments\n\n* Comments documenting the source code are required.\n\n  * Comments from which documentation is automatically generated are **not**\n    subject to case-by-case decisions. Such comments are used, for example, on\n    types and their members. Examples of tools which automatically generate\n    documentation from such comments include JSDoc, Javadoc, Doxygen.\n\n  * Comments that are not automatically processed are strongly encouraged. They\n    are subject to case-by-case decisions. Such comments are often observed in\n    function bodies.\n\n* Comments should be formatted as proper English sentences. Such formatting pays\n  attention to, for example, capitalization and punctuation.\n\n### Duplication\n\n* Don't copy-paste source code. Reuse it. Be careful not to create bad abstractions just to reuse a small chunk of code, however.\n\n### Naming\n\n* An abstraction should have one name within the project and across multiple\n  projects. For example:\n\n  * The instance of lib-jitsi-meet's `JitsiConnection` type should be named\n    `connection` or `jitsiConnection` in jitsi-meet, not `client`.\n\n  * The class `ReducerRegistry` should be defined in ReducerRegistry.js and its\n    imports in other files should use the same name. Don't define the class\n    `Registry` in ReducerRegistry.js and then import it as `Reducers` in other\n    files.\n\n* The names of global constants (including ES6 module-global constants) should\n  be written in uppercase with underscores to separate words. For example,\n  `BACKGROUND_COLOR`.\n\n* The underscore character at the beginning of a name signals that the\n  respective variable, function, or property is non-public i.e. private, protected,\n  or internal. In contrast, the lack of an underscore at the beginning of a name\n  signals public API.\n\n### TypeScript\n\n#### Feature layout\n\nWhen adding a new feature, this would be the usual layout.\n\n```\nreact/features/sample/\n├── actionTypes.ts\n├── actions.ts\n├── components\n│   ├── AnotherComponent.tsx\n│   └── OneComponent.tsx\n├── middleware.ts\n└── reducer.ts\n```\n\nAll new features must be written in TypeScript. When working on an old feature,\nconverting the JavaScript files to TypeScript is encouraged.\n\nThe middleware must be imported in `react/features/app/` specifically\nin `middlewares.any`, `middlewares.native.js` or `middlewares.web.js` where appropriate.\nLikewise for the reducer.\n\nIn general, we want to avoid `index` files. We prefer using the full path for imports.\nHowever, there are cases where a common file (used by both web and native, eg. `actions.ts`)\nneeds to import from components (from `/native` or from `/web`, depending on the platform the build is for).\nIn this case, we create two `index` files in `components/`: `index.native.ts` and `index.web.ts` and export\njust the component we need. The common file should then be imported from `components/index`.\n\nThis has not always been the case and the entire codebase hasn't been migrated to\nthis model but new features should follow this new layout.\n\nWhen working on an old feature, adding the necessary changes to migrate to the new\nmodel is encouraged.\n\n#### Avoiding bundle bloat\n\nWhen adding a new feature it may trigger a build failure due to the increased bundle size. We have safeguards in place to avoid bundles growing disproportionately. While there are legitimate reasons for increasing the limits, please analyze the bundle first to make sure no unintended dependencies have been included, causing the increase in size.\n\nFirst, make a production build with bundle-analysis enabled:\n\n```\nnpx webpack -p --analyze-bundle\n```\n\nThen open the interactive bundle analyzer tool:\n\n```\nnpx webpack-bundle-analyzer build/app-stats.json\n```\n\n### Kotlin\n\n- For Kotlin code we use the [standard convention](https://kotlinlang.org/docs/coding-conventions.html) and limit line length to 120 characters. We use `ktlint` to enforce formatting.\n\n## 🪟 Windows Development Tips\n\nGit on Windows defaults to checking out files with `CRLF` (Carriage Return & Line Feed) line endings. The Jitsi projects are primarily developed in Linux/macOS environments using `LF` line endings. If not configured correctly, Windows contributors will encounter lint failures and cause unnecessary diff noise in pull requests.\n\n### 1. Global Git Configuration\n\nTo prevent issues, it is highly recommended to configure Git to automatically handle line conversions:\n\n```bash\ngit config --global core.autocrlf input\n```\n\nThis tells Git to automatically convert `CRLF` endings to `LF` upon commit, ensuring you do not accidentally push Windows line endings into the repository.\n\n### 2. .gitattributes Configuration\n\nMany Jitsi repositories use a `.gitattributes` file to explicitly enforce `LF` line endings across the repository. If you use an editor like VS Code or IntelliJ, ensure you have an **EditorConfig** plugin installed (if not built-in) so your editor respects the repository's native line ending preferences. \n\n### 3. Renormalizing an Existing Checkout\n\nIf you cloned the repository *before* fixing your Git configuration and are currently experiencing lint errors or noisy diffs, you can forcefully re-normalize the line endings in your local repository checkout:\n\n```bash\ngit add --renormalize .\ngit commit -m \"docs: fix line endings\"\n```\n## ✅ Code Reviews\n\n- **Submit Your Contribution:** After completing your work, submit your contribution.\n- **Draft PRs for Discussion:** Consider opening a draft PR early to discuss your approach with the team before fully implementing it. Draft PRs facilitate early collaboration, ensuring efficient progress.\n- **Assign Reviewers:** Appropriate reviewers are assigned based on the affected code base and expertise required for changes.\n- **Review Process:** Reviewers will carefully examine your code, checking for adherence to coding standards, correctness, performance and potential issues.\n- **Feedback and Iteration:** If any issues or suggestions are identified during the review, you'll receive feedback from the reviewers. Address any comments or concerns raised and make necessary revisions to your code.\n- **Automated tests:** Once the PR is in a good state, a team member will trigger the automated tests. The PR needs to merge cleanly on top of master, and test failures or issues discovered at this stage will need to be addressed before the PR is approved for merging.\n- **Approval:** Once the code meets the required standards, passes the review, and tests, it will be approved for merging into the main codebase.\n\n## 🎉 Issue Closing \n\n- You can close issues automatically with keywords in pull requests and commit messages. For more information, see \"[Linking a pull request to an issue.](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword#linking-a-pull-request-to-an-issue-using-a-keyword)\"\n"
  },
  {
    "path": "docs/dev-guide/electron-sdk.md",
    "content": "---\nid: dev-guide-electron-sdk\ntitle: Electron SDK\n---\n\nThe Jitsi Meet Electron SDK provides a toolkit for adding Jitsi Meet into electron applications with additional features for a better desktop experience.\n\nSupported Electron versions: >= 16.\n\n## Sample Application\n\nThe Jitsi Meet Electron Application is created using the Electron SDK and makes use of all its available features. The source code is available here: [jitsi-meet-electron application repository](https://github.com/jitsi/jitsi-meet-electron).\n\n## Installation\n\nInstall from npm:\n\n    npm install @jitsi/electron-sdk\n\nNote: This package contains native code on Windows for the remote control module. Binary prebuilds are packaged with prebuildify as part of the npm package.\n\n## Usage\n\n### Screen Sharing\n\n**Requirements**:\nThe screen sharing utility requires iframe HTML Element that will load Jitsi Meet.\n\n**Enable the screen sharing:**\n\nIn the **render** electron process of the window where Jitsi Meet is displayed:\n\n```js\nconst {\n    setupScreenSharingRender\n} = require(\"@jitsi/electron-sdk\");\n\n// api - The Jitsi Meet iframe api object.\nsetupScreenSharingRender(api);\n```\nIn the **main** electron process:\n\n```js\nconst {\n    setupScreenSharingMain\n} = require(\"@jitsi/electron-sdk\");\n\n// jitsiMeetWindow - The BrowserWindow instance of the window where Jitsi Meet is loaded.\n// appName - Application name which will be displayed inside the content sharing tracking window\n// i.e. [appName] is sharing your screen.\n// osxBundleId - Mac Application bundleId for which screen capturer permissions will be reset if user denied them.  \nsetupScreenSharingMain(mainWindow, appName, osxBundleId);\n```\n\n**Note**:\nAn example using screensharing in Electron without the SDK is available here: [screensharing example without the SDK](https://github.com/gabiborlea/jitsi-meet-electron-example).\n\n### Remote Control\n\n**Requirements**:\nThe remote control utility requires an iframe HTML Element that will load Jitsi Meet.\n\n**Enable the remote control:**\n\nIn the **render** electron process of the window where Jitsi Meet is displayed:\n\n```js\nconst {\n    RemoteControl\n} = require(\"@jitsi/electron-sdk\");\n\n// iframe - the Jitsi Meet iframe\nconst remoteControl = new RemoteControl(iframe);\n```\n\nTo disable the remote control:\n```js\nremoteControl.dispose();\n```\n\nNOTE: The `dispose` method will be called automatically when the Jitsi Meet iframe unloads.\n\nIn the **main** electron process:\n\n```js\nconst {\n    RemoteControlMain\n} = require(\"@jitsi/electron-sdk\");\n\n// jitsiMeetWindow - The BrowserWindow instance of the window where Jitsi Meet is loaded.\nconst remoteControl = new RemoteControlMain(mainWindow);\n```\n\n### Always On Top\nDisplays a small window with the currently active speaker video when the main Jitsi Meet window is not focused.\n\n**Requirements**:\n1. Jitsi Meet should be initialized through our [iframe API](https://github.com/jitsi/jitsi-meet/blob/master/doc/api.md)\n2. The `BrowserWindow` instance where Jitsi Meet is displayed should use the [Chrome's window.open implementation](https://github.com/electron/electron/blob/master/docs/api/window-open.md#using-chromes-windowopen-implementation) (set `nativeWindowOpen` option of `BrowserWindow`'s constructor to `true`).\n3. If you have a custom handler for opening windows you have to filter the always-on-top window. You can do this by its `frameName` argument which will be set to `AlwaysOnTop`.\n\n**Enable the aways on top:**\n\nIn the **main** electron process:\n```js\nconst {\n    setupAlwaysOnTopMain\n} = require(\"@jitsi/electron-sdk\");\n\n// jitsiMeetWindow - The BrowserWindow instance\n// of the window where Jitsi Meet is loaded.\nsetupAlwaysOnTopMain(jitsiMeetWindow);\n```\n\nIn the **render** electron process of the window where Jitsi Meet is displayed:\n```js\nconst {\n    setupAlwaysOnTopRender\n} = require(\"@jitsi/electron-sdk\");\n\nconst api = new JitsiMeetExternalAPI(...);\nconst alwaysOnTop = setupAlwaysOnTopRender(api);\n\nalwaysOnTop.on('will-close', handleAlwaysOnTopClose);\n```\n\n`setupAlwaysOnTopRender` returns an instance of EventEmitter with the following events:\n\n* _dismissed_ - emitted when the always-on-top window is explicitly dismissed via its close button\n\n* _will-close_ - emitted right before the always-on-top window is going to close\n\n\n### Power Monitor\n\nProvides a way to query Electron for system idle and receive power monitor events.\n\n**enable power monitor:**\nIn the **main** electron process:\n```js\nconst {\n    setupPowerMonitorMain\n} = require(\"@jitsi/electron-sdk\");\n\n// jitsiMeetWindow - The BrowserWindow instance\n// of the window where Jitsi Meet is loaded.\nsetupPowerMonitorMain(jitsiMeetWindow);\n```\n\nIn the **render** electron process of the window where Jitsi Meet is displayed:\n```js\nconst {\n    setupPowerMonitorRender\n} = require(\"@jitsi/electron-sdk\");\n\nconst api = new JitsiMeetExternalAPI(...);\nsetupPowerMonitorRender(api);\n```\n\n### NOTE:\nYou'll need to add 'disable-site-isolation-trials' switch because of [https://github.com/electron/electron/issues/18214](https://github.com/electron/electron/issues/18214):\n```\napp.commandLine.appendSwitch('disable-site-isolation-trials')\n```\n\nFor more information please check out the SDK's repository [https://github.com/jitsi/jitsi-meet-electron-sdk](https://github.com/jitsi/jitsi-meet-electron-sdk).\n\n## Contributing and Local Development\n\nThis section explains how the Electron SDK and the Electron app are structured as separate repositories, how to decide where a fix or feature belongs, and how to develop them together locally without publishing to npm.\n\n### SDK vs Electron App — which layer owns what?\n\nThe Electron desktop experience is split across two repositories:\n\n| Repository | Responsibility |\n|---|---|\n| [`jitsi-meet-electron-sdk`](https://github.com/jitsi/jitsi-meet-electron-sdk) | Native OS integrations: screen sharing, remote control, always-on-top window, power monitor |\n| [`jitsi-meet-electron`](https://github.com/jitsi/jitsi-meet-electron) | The application shell: window management, app menus, auto-updates, packaging and distribution |\n| [`jitsi-meet`](https://github.com/jitsi/jitsi-meet) | The meeting logic itself, rendered inside an iframe inside the Electron shell |\n\nUse this as a quick guide when triaging a bug or planning a feature:\n\n- **Always-on-top / PiP window layout or lifecycle** → fix in `jitsi-meet-electron-sdk`\n- **Remote control or screen sharing capture** → fix in `jitsi-meet-electron-sdk`\n- **App window size, tray icon, menu, auto-update** → fix in `jitsi-meet-electron`\n- **In-meeting UI or conference behaviour** → fix in `jitsi-meet`\n\n:::tip\nIf a bug is visible in the Electron app but not in the web browser, it is almost always the SDK or the Electron shell. Start with `jitsi-meet-electron-sdk`.\n:::\n\n### Local development with a linked SDK\n\nWhen you need to test SDK changes against the Electron app without publishing a new npm release, use `npm link` to symlink your local SDK clone into the app.\n\n#### Step 1 — Clone and build the SDK\n\n```bash\ngit clone https://github.com/jitsi/jitsi-meet-electron-sdk\ncd jitsi-meet-electron-sdk\nnpm install\nnpm run build\n```\n\n#### Step 2 — Register the local SDK globally\n\n```bash\n# Still inside jitsi-meet-electron-sdk/\nnpm link\n```\n\nThis creates a global symlink named `@jitsi/electron-sdk` pointing to your local clone.\n\n#### Step 3 — Link it into the Electron app\n\n```bash\ngit clone https://github.com/jitsi/jitsi-meet-electron\ncd jitsi-meet-electron\nnpm install\nnpm link @jitsi/electron-sdk\n```\n\n#### Step 4 — Start the app\n\n```bash\nnpm start\n```\n\nAny changes you make in `jitsi-meet-electron-sdk/` are reflected immediately in the running app (after a rebuild of the SDK if it uses a compile step).\n\n### Verifying the linked version is active\n\nAfter linking, confirm the symlink is in place:\n\n```bash\n# In the jitsi-meet-electron/ directory\nls -la node_modules/@jitsi/electron-sdk\n```\n\nThe output should show an arrow (`->`) pointing to your local SDK path, for example:\n\n```\nnode_modules/@jitsi/electron-sdk -> /home/user/jitsi-meet-electron-sdk\n```\n\nIf it shows a regular directory instead, re-run `npm link @jitsi/electron-sdk`.\n\n### Restoring the published package\n\nWhen you are done with local development, unlink and restore the npm-published version:\n\n```bash\n# In the jitsi-meet-electron/ directory\nnpm unlink @jitsi/electron-sdk\nnpm install\n```\n\nTo also remove the global symlink created in Step 2:\n\n```bash\n# In the jitsi-meet-electron-sdk/ directory\nnpm unlink\n```\n"
  },
  {
    "path": "docs/dev-guide/flutter-sdk.md",
    "content": "---\nid: dev-guide-flutter-sdk\ntitle: Flutter SDK\n---\n\nThe Jitsi Meet Flutter SDK provides the same user experience as the Jitsi Meet app, in the form of a Flutter plugin so that you can embed and customize Jitsi Meet in your own Flutter app.\n\n## Sample application using the Flutter\n\nIf you want to see how easy integrating the Jitsi Meet Flutter SDK into a Flutter application is, take a look at the<br/>\n[sample applications repository](https://github.com/jitsi/jitsi-meet-sdk-samples#flutter).\n\n## Installation\n\n### Add dependency\n\nAdd the dependency from command-line\n```bash\n$ flutter pub add jitsi_meet_flutter_sdk\n```\n\nThe command above will add this to the `pubspec.yaml` file in your project (you can do this manually):\n```yaml\ndependencies:\n    jitsi_meet_flutter_sdk: ^0.1.7\n```\n\n### Install \n\nInstall the packages from the terminal:\n\n```bash\n$ flutter pub get\n```\n\n### Import files\n\nImport the following files into your dart code:\n\n```dart\nimport 'package:jitsi_meet_flutter_sdk/jitsi_meet_flutter_sdk.dart';\n```\n\n### Usage\n\n#### Join meeting\n\nFirstly, create a `JitsiMeet` object, then call the method `join` from it with a `JitsiMeetConferenceOptions` object\n\n```dart\nvar jitsiMeet = JitsiMeet();\nvar options = JitsiMeetConferenceOptions(room: 'jitsiIsAwesome');\njitsiMeet.join(options);\n```\n\n## Configuration\n\n### iOS\n\nMake sure in `Podfile` from the `ios` directory you set the ios version `15.1 or higher` \n\n```\nplatform :ios, '15.1'\n```\n\nThe plugin requests camera and microphone access, make sure to include the required entries for `NSCameraUsageDescription` and `NSMicrophoneUsageDescription` in your `Info.plist` file from the `ios/Runner` directory.\n\n```xml\n<key>NSCameraUsageDescription</key>\n<string>The app needs access to your camera for meetings.</string>\n<key>NSMicrophoneUsageDescription</key>\n<string>The app needs access to your microphone for meetings.</string>\n```\n\n### Android\n\nGo to `android/app/build.gradle` and make sure that the `minSdkVersion` is set to at least 24`\n\n```gradle\nandroid {\n    ...\n    defaultConfig {\n        ...\n        minSdkVersion 24\n    }\n}\n```\n\n\nThe `application:label` field from the Jitsi Meet Android SDK will conflict with your application's one . Go to `android/app/src/main/AndroidManifest.xml` and add the tools library and `tools:replace=\"android:label\"` to the application tag.\n\n```xml\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" \n    xmlns:tools=\"http://schemas.android.com/tools\">\n    <application\n        tools:replace=\"android:label\"\n        android:label=\"sample_app\"\n        android:name=\"${applicationName}\"\n        android:icon=\"@mipmap/ic_launcher\">\n        ...\n    </application>\n</manifest>\n```\n## Using the API\n\n### JitsiMeet\n\nThe `JitsiMeet` class is the entry point for the SDK. It is used to launch the meeting screen and to send and receive all the events.\n\n1. ####  JitsiMeet()\n    The constructor for the class.\n\n\n2. ####  join(JitsiMeetConferenceOptions options, [JitsiMeetEventListener? listener])\n    Joins a meeting with the given options and optionally a listener is given\n\n    - `options` : meeting options\n    - `listener` : event listener for events triggered by the native SDKs\n\n3. #### hangUp()\n\n    The localParticipant leaves the current meeting.\n\n4. #### setAudioMuted(bool muted)\n\n    Sets the state of the localParticipant audio muted according to the `muted` parameter.\n\n5. #### setVideoMuted(bool muted)\n    Sets the state of the localParticipant video muted according to the `muted` parameter.\n\n6. #### sendEndpointTextMessage(`{String? to, required String message}`)\n    Sends a message via the data channel to one particular participant or all of them. If the `to` param is empty, the message will be sent to all the participants in the conference.\n\n    To get the participantId, the `participantsJoined` event should be listened for, which has as a parameter the `participantId` and this should be stored somehow.\n\n7. #### toggleScreenShare(bool enabled)\n    Sets the state of the localParticipant screen sharing according to the `enabled` parameter.\n\n8. #### openChat([String? to])\n\n    Opens the chat dialog. If `to` contains a valid participantId, the private chat with that particular participant will be opened.\n\n9. #### sendChatMessage(`{String? to, required String message}`)\n\n    Sends a chat message to one particular participant or all of them. If the `to` param is empty, the message will be sent to all the participants in the conference.\n\n    To get the participantId, the `participantsJoined` event should be listened for, which has as a parameter the `participantId` and this should be stored somehow.\n\n10. #### closeChat()\n\n    Closes the chat dialog.\n\n11. #### retrieveParticipantsInfo()\n\n    Sends an event that will trigger the `participantsInfoRetrieved` event which will contain participants' information\n\n\n### JitsiMeetConferenceOptions\n\nThis object encapsulates all the options that can be tweaked when joining a conference.\n\nExample:\n\n```dart\nvar options = JitsiMeetConferenceOptions(\n      serverURL: \"https://meet.jit.si\",\n      room: \"jitsiIsAwesomeWithFlutter\",\n      configOverrides: {\n        \"startWithAudioMuted\": false,\n        \"startWithVideoMuted\": false,\n        \"subject\" : \"Jitsi with Flutter\",\n      },\n      featureFlags: {\n        \"unsaferoomwarning.enabled\": false\n      },\n      userInfo: JitsiMeetUserInfo(\n          displayName: \"Flutter user\",\n          email: \"user@example.com\"\n      ),\n    );\n```\n\n- All the values that can be added to the `configOverrides` can be found [here](https://github.com/jitsi/jitsi-meet/blob/master/config.js).\n\n- All the values that can be added to the `featureFlags` can be found [here](https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.ts).\n\n#### JitsiMeetUserInfo(`{String displayName, String email, String avatar}`)\n\nThe constructor for the JitsiMeetUserInfo.\n\nP.S. the avatar should be an url.\n\n### JitsiMeetEventListener\n\nThis class intends to be used as a listener for events that come from the native sdks. It will receive as arguments the event handlers\n\n#### conferenceJoined(String url)\n\n    Called when a conference was joined.\n    - `url` : the conference URL\n\n#### conferenceTerminated(String url, Object? error)\n\n    Called when the active conference ends, be it because of user choice or because of a failure.\n\n    - `url` : the conference URL\n    - `error` : missing if the conference finished gracefully, otherwise contains the error message\n\n#### conferenceWillJoin(String url)\n\n    Called before a conference is joined.\n\n    - url: the conference URL\n\n#### participantJoined(String? email, String? name, String? role, String? participantId) \n\n    Called when a participant has joined the conference.\n\n    - `email` : the email of the participant. It may not be set if the remote participant didn't set one.\n    - `name` : the name of the participant.\n    - `role` : the role of the participant.\n    - `participantId` : the id of the participant.\n\n#### participantLeft(String? participantId)\n\n    Called when a participant has left the conference.\n\n    - `participantId` : the id of the participant that left.\n\n#### audioMutedChanged(bool muted)\n\n    Called when the local participant's audio is muted or unmuted. \n\n    - `muted` : a boolean indicating whether the audio is muted or not.\n\n#### videoMutedChanged(bool muted)\n\n    Called when the local participant's video is muted or unmuted. \n\n    - `muted` : a boolean indicating whether the video is muted or not.\n\n#### endpointTextMessageReceived(String senderId, String message)\n\n    Called when an endpoint text message is received.\n\n    - `senderId` : the participantId of the sender\n    - `message` : the content.\n\n#### screenShareToggled(String participantId, bool sharing)\n\n    Called when a participant starts or stops sharing his screen.\n\n    - `participantId` : the id of the participant\n    - `sharing` : the state of screen share\n\n#### chatMessageReceived(String senderId, String message, bool isPrivate, String? timestamp)\n\n    Called when a chat text message is received.\n\n    - `senderId` : the ID of the participant that sent the message.\n    - `message` : the content of the message.\n    - `isPrivate` : true if the message is private, false otherwise.\n    - `timestamp` : the (optional) timestamp of the message.\n\n#### chatToggled(bool isOpen)\n\n    Called when the chat dialog is opened or closed.\n\n    - `isOpen` : true if the chat dialog is open, false otherwise.\n\n#### participantsInfoRetrieved(String participantsInfo)\n    Called when the `retrieveParticipantsInfo` action is called\n\n    - `participantsInfo` : a list of participants' information as a string.\n\n#### readyToClose()\n    Called when the SDK is ready to be closed. No meeting is happening at this point.\n\n#### customButtonPressed()\n    Called when a custom button is pressed.\n\n    - `id` : the ID of the button.\n    - `text` : the label of the button.\n\n\n#### Example of listener:\n\n```dart\nvar listener = JitsiMeetEventListener(\n      conferenceJoined: (url) {\n        debugPrint(\"conferenceJoined: url: $url\");\n      },\n\n      participantJoined: (email, name, role, participantId) {\n        debugPrint(\n          \"participantJoined: email: $email, name: $name, role: $role, \"\n              \"participantId: $participantId\",\n        );\n        participants.add(participantId!);\n      },\n\n      chatMessageReceived: (senderId, message, isPrivate) {\n        debugPrint(\n          \"chatMessageReceived: senderId: $senderId, message: $message, \"\n              \"isPrivate: $isPrivate\",\n        );\n      },\n\n      readyToClose: () {\n        debugPrint(\"readyToClose\");\n      },\n    );\n```\n"
  },
  {
    "path": "docs/dev-guide/iframe-commands.md",
    "content": "---\nid: dev-guide-iframe-commands\ntitle: Commands\n---\n\nYou can control the embedded Jitsi Meet conference by calling **`executeCommand`** on the **`JitsiMeetExternalAPI`** object:\n\n```javascript\napi.executeCommand(command, ...arguments);\n```\n\nThe command parameter is a string which contains the command name.\n\nYou can also execute multiple commands using the **`executeCommands`** method:\n\n```javascript\napi.executeCommands(commands);\n```\n\nThe **`commands`** parameter is an object with the names of the commands as keys and the arguments for the commands as values:\n\n```javascript\napi.executeCommands({\n    displayName: [ 'nickname' ],\n    toggleAudio: []\n});\n```\n\nThe following commands are supported:\n\n### displayName\n\nSets the display name of the local participant.\n\n  This command requires one argument to set the new display name.\n\n```javascript\napi.executeCommand('displayName', 'New Nickname');\n```\n\n### password\n\nSets the password for the room.\n\n```javascript\n// set new password for channel\napi.addEventListener('participantRoleChanged', function(event) {\n    if (event.role === \"moderator\") {\n        api.executeCommand('password', 'The Password');\n    }\n});\n// join a protected channel\napi.on('passwordRequired', function ()\n{\n    api.executeCommand('password', 'The Password');\n});\n```\n\n### toggleLobby\n\nToggles the lobby mode on or off.\n\nThis command requires the desired lobby mode state as the argument.\n\n```javascript\napi.addEventListener('participantRoleChanged', function (event) {\n    if(event.role === 'moderator') {\n        api.executeCommand('toggleLobby', true);\n    }\n});\n```\n\n### sendTones\n\nTouch tone playback.\n\nThis command requires the selected touch tone dial pads to play as well as the length of and time gap between tone play as the arguments.\n\n```javascript\napi.executeCommand('sendTones', {\n    tones: string, // The dial pad touch tones to play. For example, '12345#'.\n    duration: number, // Optional. The number of milliseconds each tone should play. The default is 200.\n    pause: number // Optional. The number of milliseconds between each tone. The default is 200.\n});\n```\n\n### startShareVideo\n\nStarts sharing a video\n\nThis command requires the an url pointing to either a youtube video or a video to be streamed from web (e.g an mp4 file)\n\n```javascript\napi.executeCommand('startShareVideo', url);\n```\n\n### stopShareVideo\n\nStops sharing a video (if the user is the one who started the video)\n\nNo arguments are required.\n\n```javascript\napi.executeCommand('stopShareVideo');\n```\n\n### subject\n\nSets the subject of the conference.\n\nThis command requires the new subject to be set as the argument and it will be applied only if the participant has the moderator role or after they receive that role later on.\n\n```javascript\napi.executeCommand('subject', 'New Conference Subject');\n```\n\n### localSubject\n\nSets the local subject of the conference.\n\nThis command requires the new local subject to be set as the argument and it can be applied by all participants regardless of their role.\n\n```javascript\napi.executeCommand('localSubject', 'New Conference Local Subject');\n```\n\n### toggleAudio\n\nMutes / unmutes the audio for the local participant.\n\nNo arguments are required.\n\n```javascript\napi.executeCommand('toggleAudio');\n```\n\n### toggleVideo\n\nMutes / unmutes the video for the local participant.\n\nNo arguments are required.\n\n```javascript\napi.executeCommand('toggleVideo');\n```\n\n### toggleFilmStrip\n\nHide or show the filmstrip.\n\nNo arguments are required.\n\n```javascript\napi.executeCommand('toggleFilmStrip');\n```\n\n### toggleChat\n\nHide or show chat messaging.\n\nNo arguments are required.\n\n```javascript\napi.executeCommand('toggleChat');\n```\n\n### toggleRaiseHand\n\nHide or show the raised hand.\n\nNo arguments are required.\n\n```javascript\napi.executeCommand('toggleRaiseHand')\n```\n\n### toggleShareScreen\n\nStart or stop screen sharing.\n\nNo arguments are required.\n\n```javascript\napi.executeCommand('toggleShareScreen');\n```\n\n### setNoiseSuppressionEnabled\n\nEnable or disable noise suppression on the current audio track.\n\n```javascript\napi.executeCommand('setNoiseSuppressionEnabled', {\n    enabled: boolean // Enable or disable noise suppression.\n});\n```\n\n### toggleSubtitles\n\nStart or stop subtitles.\n\nNo arguments are required.\n\n```javascript\napi.executeCommand('toggleSubtitles');\n```\n\n### toggleTileView\n\nEnter or exit the tile view layout mode.\n\nNo arguments are required.\n\n```javascript\napi.executeCommand('toggleTileView');\n```\n\n### hangup\n\nEnds the call.\n\nNo arguments are required.\n\n```javascript\napi.executeCommand('hangup');\n```\n\n### endConference\n\nEnds the current conference for everyone.\n\nThis command can only be executed by a meeting moderator, and requires End Conference support to be enabled\nfor the deployment.\n\n```javascript\napi.executeCommand('endConference');\n```\n\n### email\n\nChanges the local email address.\n\nThis command requires the new email address as the single argument.\n\n```javascript\napi.executeCommand('email', 'example@example.com');\n```\n\n### sendCameraFacingMode\n\nSends a request to a given participant to set camera facing mode as `user` or `environment`.\n\nThe receiving participant is shown a confirmation dialog. If the `facingMode` param is not sent, the camera will toggle between the two options on subsequent calls.\n\n```javascript\napi.executeCommand('sendCameraFacingMode', 'receiverParticipantId', 'facingMode');\n```\n\n### sendEndpointTextMessage\n\nSends a text message to another participant through the data channels.\n\n```javascript\napi.executeCommand('sendEndpointTextMessage', 'receiverParticipantId', 'text');\n```\n\n### setLargeVideoParticipant\n\nDisplays the participant on the large video display.\n\nThe participant ID, if specified, is displayed on the large video. If no argument is passed, the participant to be displayed on the large  video is automatically selected based on the dominant/pinned speaker settings.\n\nThe second parameter is optional and can be used to specify a `videoType`. When multistream support is enabled by passing this parameter you can specify whether the desktop or the camera video for the specified participant should be selected. The accepted values are `'camera'` and `'desktop'`. The default is `'camera'`. Any invalid values will be ignored and default will be used.\n\n```javascript\napi.executeCommand('setLargeVideoParticipant', 'abcd1234', 'desktop');\n```\n\n### setVideoQuality\n\nSets the send and receive video resolution.\n\nThe resolution height setting is implemented using a single argument.\n\n```javascript\napi.executeCommand('setVideoQuality', 720);\n```\n\n### muteEveryone\n\nMute all meeting participants.\n\nThis command can only be executed by the meeting moderator and can take one argument: `mediaType` - for which media type to mute everyone.\n\n`mediaType` can be either 'audio' (default) or 'video'.\n\n```javascript\napi.executeCommand('muteEveryone', 'video');\n```\n\n### muteRemoteParticipant\n\nMutes a specific remote participant.\n\nThis command can only be executed by the meeting moderator and takes two arguments:\n\n- `participantId` - The ID of the participant to mute (required)\n- `mediaType` - The media type to mute: either `'audio'` (default) or `'video'`\n\n```javascript\n// Mute participant's audio\napi.executeCommand('muteRemoteParticipant', 'participantId123');\n\n// Mute participant's video\napi.executeCommand('muteRemoteParticipant', 'participantId123', 'video');\n```\n\n### startRecording\n\nStarts a local recording, file recording, streaming session or transcription using passed parameters:\n\n  - **RTMP streaming** - Recording mode set to **`stream`** with an **`rtmpStreamKey`**. The **`rtmpBroadcastID`** value is optional.\n\n  - **YouTube streams** - Recording mode set to **`stream`** with an **`youtubeStreamKey`**. The **`youtubeBroadcastID`** value is optional.\n\n  - **Local Recording** - Recording mode set to **`local`**. The **`onlySelf`** value is optional.\n\n  - **Dropbox recording** - Recording mode set to **`file`** with a Dropbox OAuth2 token.\n\n  Additionally, Dropbox saving should be enabled on the Jitsi meet deploy config you are using.\n\n  - **File recording** - Recording mode set to **`file`**. The **`extraMetadata`** value is optional.\n\n  Optionally, **`shouldShare`** should be passed on. No other params are required.\n\n  - **Transcription** - Set the `transcription` option to `true`.\n\n```javascript\napi.executeCommand('startRecording', {\n    mode: string, //recording mode, either `local`, `file` or `stream`.\n    dropboxToken: string, //dropbox oauth2 token.\n    onlySelf: boolean,  //Whether to only record the local streams. Only applies to `local` recording mode.\n    shouldShare: boolean, //whether the recording should be shared with the participants or not. Only applies to certain jitsi meet deploys.\n    rtmpStreamKey: string, //the RTMP stream key.\n    rtmpBroadcastID: string, //the RTMP broadcast ID.\n    youtubeStreamKey: string, //the youtube stream key.\n    youtubeBroadcastID: string, //the youtube broacast ID.\n    extraMetada: Object, // any extra metada for file recording.\n    transcription: boolean, // Whether a transcription should be started. \n});\n```\n\n### stopRecording\n\nStops an ongoing  **`local`**, **`stream`**, **`file`** recording or transcription.\n\nThe mode in which the recording was started must be specified.\n\n```javascript\napi.executeCommand('stopRecording',\n    mode: string, //recording mode to stop, `local`, `stream` or `file`\n    transcription: boolean // whether the transcription should be stopped\n);\n```\n\n### initiatePrivateChat\n\nOpens the chat window and sets the participant with the given participant ID as the messages recipient.\n\n```javascript\napi.executeCommand('initiatePrivateChat',\n    participantID: string\n);\n```\n\n### cancelPrivateChat\n\nRemoves the private chat participant thus it resets the chat window to group chat.\n\n```javascript\napi.executeCommand('cancelPrivateChat');\n```\n\n### kickParticipant\n\nKicks the participant with the given participant ID from the meeting.\n\n```javascript\napi.executeCommand('kickParticipant',\n    participantID: string\n);\n```\n\n### grantModerator\n\nGrants moderator rights to the participant with the given ID.\n\n```javascript\napi.executeCommand('grantModerator',\n    participantID: string\n);\n```\n\n### overwriteConfig\n\nOverwrite config.js props with values from the config object passed on to the command.\n\n```javascript\napi.executeCommand('overwriteConfig',\n    config: Object\n);\n```\nFor example:\n```javascript\napi.executeCommand('overwriteConfig',\n    {\n      toolbarButtons: ['chat']\n    }\n);\n```\nwill overwrite the `toolbarButtons` config value with `[chat]`, resulting in UI only showing the `chat` button.\n\n### sendChatMessage\n\nSends a chat message either to a specific participant or as a group chat message.\n\n```javascript\napi.executeCommand('sendChatMessage',\n    message: string, //the text message\n    to: string, // the receiving participant ID or empty string/undefined for group chat.\n    ignorePrivacy: boolean // true if the privacy notification should be ignored. Defaulted to false.\n);\n```\n\n### setFollowMe\n\nAllows moderators to toggle the follow me functionality\n\n```javascript\napi.executeCommand('setFollowMe',\n    value: boolean, // set to true if participants should be following you, false otherwise\n    recorderOnly: boolean // Whether the recorder will be the only one following you. The default is false.\n);\n```\n\n### setSubtitles\n\nEnables or disables the subtitles.\n\n```javascript\napi.executeCommand('setSubtitles',\n    enabled: boolean,\n    displaySubtitles: boolean = true,\n    language: string | null = 'en'\n);\n```\n\n### setTileView\n\nEnables or disables the tileview mode.\n\n```javascript\napi.executeCommand('setTileView',\n    enabled: boolean\n);\n```\n\n### answerKnockingParticipant\n\nApproves or rejects the knocking participant in the lobby.\n\n```javascript\napi.executeCommand('answerKnockingParticipant',\n    id: string, // the participant id\n    approved: boolean\n);\n```\n\n### toggleCamera\n\nSets the camera facing mode as `user` or `environment` on mobile web. If the `facingMode` param is not sent, a toggle between back and front camera happens on subsequent calls.\n\n```javascript\napi.executeCommand('toggleCamera', 'facingMode');\n```\n\n### toggleCameraMirror\n\nToggles the mirroring of the local video.\n\n```javascript\napi.executeCommand('toggleCameraMirror');\n```\n\n### toggleVirtualBackgroundDialog\n\nToggles the virtual background selection dialog.\n\n```javascript\napi.executeCommand('toggleVirtualBackgroundDialog');\n```\n\n### pinParticipant\n\nPins a conference participant.\n\n```javascript\napi.executeCommand('pinParticipant',\n    id?: string // The ID of the conference participant to pin or null to unpin all\n);\n```\n\n### setParticipantVolume\n\nChange volume of the participant with the given participant ID.\n\n```javascript\napi.executeCommand('setParticipantVolume',\n    participantID: string,\n    volume: number // number between 0 and 1\n);\n```\n\n### toggleParticipantsPane\n\nChanges the visibility status of the participants pane.\n\n```javascript\napi.executeCommand('toggleParticipantsPane',\n    enabled: boolean // The visibility status of the participants pane.\n);\n```\n\n### toggleModeration\n\nChanges moderation status of the given media type.\n\nThis command requires two arguments: `enable` - whether to enable it or not, and `mediaType` - the media type for which to change moderation.\n\n```javascript\napi.executeCommand('toggleModeration',\n    enable: Boolean,\n    mediaType: String // can be 'audio' (default) or 'video'\n);\n```\n\n### askToUnmute\n\nAsks the participant with the given ID to unmute.\nIf audio moderation is on it also approves the participant for audio.\n\n```javascript\napi.executeCommand('askToUnmute',\n    participantId: String\n);\n```\n\n### approveVideo\n\nIf video moderation is on it approves the participant with the given ID for video.\n\n```javascript\napi.executeCommand('approveVideo',\n    participantId: String\n);\n```\n\n### rejectParticipant\n\nRejects the participant with the given ID from moderation of the given media type.\n\n```javascript\napi.executeCommand('rejectParticipant',\n    participantId: String,\n    mediaType: String // can be 'audio' (default) or 'video'\n);\n```\n\n### addBreakoutRoom\n\nCreates a breakout room.\n\nThis command can only be executed by the meeting moderator.\n\n```javascript\napi.executeCommand('addBreakoutRoom',\n    name: String // Optional. The name or subject of the new room.\n);\n```\n\n### autoAssignToBreakoutRooms\n\nAuto-assigns the participants to breakout rooms.\n\nThis command can only be executed by the meeting moderator.\n\n```javascript\napi.executeCommand('autoAssignToBreakoutRooms');\n```\n\n### closeBreakoutRoom\n\nCloses the breakout room and sends participants to the main room.\n\nThis command can only be executed by the meeting moderator.\n\n```javascript\napi.executeCommand('closeBreakoutRoom',\n    roomId: String // The id of the room to close.\n);\n```\n\n### joinBreakoutRoom\n\nJoins a breakout room. If the argument is omitted, joins the main room.\n\n```javascript\napi.executeCommand('joinBreakoutRoom',\n    roomId: String // Optional. The id of the room to join.\n);\n```\n\n### removeBreakoutRoom\n\nRemoves the breakout room.\n\nThis command can only be executed by the meeting moderator.\n\n```javascript\napi.executeCommand('removeBreakoutRoom',\n    breakoutRoomJid: String // The jid of the breakout room to remove.\n);\n```\n\n### resizeFilmStrip\n\nResizes the filmstrip.\n\n```javascript\napi.executeCommand('resizeFilmStrip', {\n    width: number // The desired filmstrip width\n});\n```\n\n### resizeLargeVideo\n\nResizes the large video container based on the dimensions provided.\n\n```javascript\napi.executeCommand('resizeLargeVideo',\n    width: number, // The desired width\n    height: number // The desired height\n);\n```\n\n### sendParticipantToRoom\n\nSends a participant to a room.\n\nThis command can only be executed by the meeting moderator.\n\n```javascript\napi.executeCommand('sendParticipantToRoom',\n    participantId: String, // The id of the participant.\n    roomId: String // The id of the room.\n);\n```\n\n### overwriteNames\n\nOverwrites the names of the given participants to the given names. (locally for the participant that send the command)\n\n```javascript\napi.executeCommand('overwriteNames', [{\n        id: String, // The id of the participant.\n        name: String // The new name.\n    }]\n);\n```\n\n### showNotification\n\nShows a custom notification. This affects only the local user.\n\nIf `uid` is provided, the notification will replace existing notification with the same `uid`. The `uid` can also be\npassed to the `hideNotification` command to programmatically hide the notification.\n\nIf `customActions` is provided, when triggered, the actions will fire a [customNotificationActionTriggered](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-iframe-events#customnotificationactiontriggered) event with their corresponding uuid\n\n```javascript\napi.executeCommand('showNotification', {\n  title: String, // Title of the notification.\n  description: String, // Content of the notification.\n  customActions: Object(label: String, uuid: String)[], // Optional. Define custom actions to be displayed on the notification\n  uid: String, // Optional. Unique identifier for the notification.\n  type: String, // Optional. Can be 'normal', 'success', 'warning' or 'error'. Defaults to 'normal'.\n  timeout: String // optional. Can be 'short', 'medium', 'long', or 'sticky'. Defaults to 'short'.\n});\n```\n\n### hideNotification\n\nHides the notification which has the given `uid`.\n\n```javascript\napi.executeCommand('hideNotification',\n    uid: String // Unique identifier for the notification to be removed.\n);\n```\n\n### toggleWhiteboard\n\nToggles the whiteboard to open, repeated toggling hidden the whiteboard\n\n```javascript\napi.executeCommand('toggleWhiteboard');\n```\n\n### setAssumedBandwidthBps\n\nSets the assumed bandwidth bps.\n\n```javascript\napi.executeCommand('setAssumedBandwidthBps',\n    assumedBandwidthBps: number // Required. The value to set as assumed bandwidth expressed in bps.\n);\n```\n\n### setBlurredBackground\n\nSets or removes the blurred virtual background to the user camera.\n\n```javascript\napi.executeCommand('setBlurredBackground',\n\tblurType: String // Required. Blur type to apply. Accepted values are 'slight-blur', 'blur' or 'none'.\n);\n```\n\n### setAudioOnly\n\nEnables or disables the audio only mode.\n\n```javascript\napi.executeCommand('setAudioOnly',\n    enable: boolean // Required. true for enable and false for disable \n);\n```\n\n### setVirtualBackground\n\nSet your virtual background with a base64 image.\n\n```javascript\napi.executeCommand('setVirtualBackground',\n    enabled: boolean, // Required. Enable or disable the virtual background.\n    backgroundImage: string // Required. Base64 image string, eg. \"data:image/png;base64, iVBOR...\".\n);\n```\n"
  },
  {
    "path": "docs/dev-guide/iframe-events.md",
    "content": "---\nid: dev-guide-iframe-events\ntitle: Events\n---\n\nThe `JitsiMeetExternalAPI` object implements the [EventEmitter] API for emitting and listening for events.\n\nYou can add event listeners to the embedded Jitsi Meet using the **`addListener`** method:\n\n```javascript\napi.addListener(event, listener);\n```\n\nIf you want to remove a listener you can use the **`removeListener`** method:\n\n```javascript\napi.removeListener(event, listener);\n```\n\nThe **`event`** parameter is a string object with the name of the event.\n\nThe **`listener`** parameter is a function object with one argument that creates a notification when the event occurs along with related event data.\n\nThe following events are currently supported:\n\n### cameraError\n\nProvides event notifications about Jitsi Meet having failed to access the meeting camera.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    type: string, // A constant representing the overall type of the error.\n    message: string // Additional information about the error.\n}\n```\n\n### avatarChanged\n\nProvides event notifications about changes to a participant's avatar.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    id: string, // the id of the participant that changed his avatar.\n    avatarURL: string // the new avatar URL.\n}\n```\n\n### audioAvailabilityChanged\n\nProvides event notifications about changes to audio availability status.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    available: boolean // new available status - boolean\n}\n```\n\n### audioMuteStatusChanged\n\nProvides event notifications about changes to audio mute status.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    muted: boolean // new muted status - boolean\n}\n```\n\n### breakoutRoomsUpdated\n\nProvides notifications about breakout rooms changes.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    [roomId]: {\n        id: string,\n        jid: string,\n        name: string,\n        isMainRoom: true | undefined,\n        participants: {\n            [participantJid]: {\n                displayName: string,\n                jid: string,\n                role: string\n            }\n        }\n    },\n    ...\n}\n```\n\n\n### browserSupport\n\nProvides event notifications about the current browser support.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    supported: boolean\n}\n```\n\n### contentSharingParticipantsChanged\n\nProvides real-time list of currently screen sharing participant ID's.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    data: [\"particId1\", \"particId2\", ...]\n}\n```\n\n### customNotificationActionTriggered\n\nCallback that triggers for custom actions defined for the [showNotification](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-iframe-commands/#shownotification) command\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    data: {\n        id: string // uuid of the triggered action\n    }\n}\n```\n\n### dataChannelOpened\n\nIndicates the data channel is open and thus messages can be sent over it.\n\n### endpointTextMessageReceived\n\nProvides event notifications about a text messages received through data channels.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    senderInfo: {\n        jid: string, // the jid of the sender\n        id: string // the participant id of the sender\n    },\n    eventData: {\n        name: string, // the name of the datachannel event: `endpoint-text-message`\n        text: string // the received text from the sender\n    }\n}\n```\n\n### nonParticipantMessageReceived\n\nProvides event notifications about a messages sent by a non-participant, e.g. a custom prosody message.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n        id: string, // the id of the message, may be null\n        message: string // the message received\n}\n```\n\n### faceLandmarkDetected\n\nProvides event notifications when a face landmark is detected\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    faceBox: {\n        left: number, // face bounding box distance as percentage from the left video edge\n        right: number // face bounding box distance as percentage from the right video edge\n        width: number // face bounding box width as percentage of the total video width\n    }, // this might be undefined if config.faceLandmarks.faceCenteringThreshold is not passed\n    faceExpression: string // check https://github.com/jitsi/jitsi-meet/blob/master/react/features/face-landmarks/constants.js#L3 for available values\n}\n```\n\n### errorOccurred\n\nProvides event notifications about an error which has occurred.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    details: Object?, // additional error details\n    message: string?, // the error message\n    name: string, // the coded name of the error\n    type: string, // error type/source, one of : 'CONFIG', 'CONNECTION', 'CONFERENCE'\n    isFatal: boolean // whether this is a fatal error which triggered a reconnect overlay or not\n}\n```\n\n### knockingParticipant\n\nProvides event notifications about a knocking participant in the lobby.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    participant: {\n        // the id and name of the participant that is currently knocking in the lobby\n        id: string,\n        name: string\n    }\n}\n```\n\n### largeVideoChanged\n\nProvides event notifications about changes in the large video display.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    id: string // id of the participant that is now on large video in the stage view.\n}\n```\n\n### log\n\nProvides log event notifications with the log level being one of the values specified in the [config.js] file in the **`apiLogLevels`** property (if not specified the event does not fire).\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    logLevel: string, // A constant representing the log type (info, error, debug, warn).\n    args: string // Additional log information.\n}\n```\n\n### micError\n\nProvides event notifications about Jitsi Meet issues with mic access.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    type: string, // A constant representing the overall type of the error.\n    message: string // Additional information about the error.\n}\n```\n\n### screenSharingStatusChanged\n\nProvides event notifications about either turning on or off local user screen sharing.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    on: boolean, //whether screen sharing is on\n    details: {\n\n        // From where the screen sharing is capturing, if known. Values which are\n        // passed include 'window', 'screen', 'proxy', 'device'. The value undefined\n        // will be passed if the source type is unknown or screen share is off.\n        sourceType: string|undefined\n    }\n}\n```\n\n### dominantSpeakerChanged\n\nProvides event notifications about dominant speaker changes.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    id: string //participantId of the new dominant speaker\n}\n```\n\n### raiseHandUpdated\n\nProvides event notifications about the participant raising/lowering the hand.\n\nThe listener will receive an object with the following structure:\n\n```javascript\n{\n    id: string,         // participantId of the user who raises/lowers the hand\n    handRaised: number  // 0 when hand is lowered and the hand raised timestamp when raised.\n}\n```\n\n### tileViewChanged\n\nProvides event notifications about entrance or exit from the tile view layout mode.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    enabled: boolean, // whether tile view is not displayed or not\n}\n```\n\n### chatUpdated\n\nProvides event notifications about chat state being updated.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    isOpen: boolean, // Whether the chat panel is open or not\n    unreadCount: number // The unread messages counter\n}\n```\n\n### incomingMessage\n\nProvides event notifications about incoming chat messages.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    from: string, // the id of the user that sent the message\n    nick: string, // the nickname of the user that sent the message\n    privateMessage: boolean, // whether this is a private or group message\n    message: string // the text of the message\n    stamp: string // the message timestamp as string (ISO-8601)\n}\n```\n\n### mouseEnter\n\nProvides event notifications when mouse enters the iframe.\nThe listener receives an object with the following structure based on [MouseEvent]:\n\n```javascript\n{\n    event: {\n        clientX,\n        clientY,\n        movementX,\n        movementY,\n        offsetX,\n        offsetY,\n        pageX,\n        pageY,\n        x,\n        y,\n        screenX,\n        screenY\n    }\n}\n```\n\n### mouseLeave\n\nProvides event notifications when mouse leaves the iframe.\nThe listener receives an object with the following structure based on [MouseEvent]:\n\n```javascript\n{\n    event: {\n        clientX,\n        clientY,\n        movementX,\n        movementY,\n        offsetX,\n        offsetY,\n        pageX,\n        pageY,\n        x,\n        y,\n        screenX,\n        screenY\n    }\n}\n```\n\n### mouseMove\n\nProvides event notifications when mouse moves inside the iframe.\nTis event is triggered on an interval which can be configured by overriding the config.js mouseMoveCallbackInterval property.\n\nThe listener receives an object with the following structure based on [MouseEvent]:\n\n```javascript\n{\n    event: {\n        clientX,\n        clientY,\n        movementX,\n        movementY,\n        offsetX,\n        offsetY,\n        pageX,\n        pageY,\n        x,\n        y,\n        screenX,\n        screenY\n    }\n}\n```\n\n### participantMenuButtonClick\n\nProvides event notifications about a participant context menu button being clicked.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    key: string, // the pressed button's key. The key is as defined in `toolbarButtons` config,\n    participantId: string, // the id of the participant for which the button was clicked,\n    preventExecution: boolean // whether the execution of the button click was prevented or not\n}\n```\n\n### toolbarButtonClicked\n\nProvides event notifications about a toolbar button being clicked and whether the click routine was executed or not.\nTo enable this notification you need to add the button to [`buttonsWithNotifyClick` config](/handbook/docs/dev-guide/dev-guide-configuration#buttonswithnotifyclick). \n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    key: string, // the pressed button's key. The key is as defined in `toolbarButtons` config,\n    preventExecution: boolean // whether the click routine execution was prevented or not.\n}\n```\n\n### outgoingMessage\n\nProvides event notifications about outgoing chat messages.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    message: string, // the text of the message\n    privateMessage: boolean // whether this is a private or group message\n}\n```\n\n### displayNameChange\n\nProvides event notifications about display name changes.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    id: string, // the id of the participant that changed their display name\n    displayname: string // the new display name\n}\n```\n\n### deviceListChanged\n\nProvides event notifications about device list changes.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    devices: Object // the new list of available devices.\n}\n```\n\n**NOTE:** The **`device`** object has the same format as the **`getAvailableDevices`** result format.\n\n### emailChange\n\nProvides event notifications about email changes.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    id: string, // the id of the participant that changed his email\n    email: string // the new email\n}\n```\n\n### feedbackSubmitted\n\nProvides event notifications about conference feedback submissions:\n\n```javascript\n{\n    error: string // The error which occurred during submission, if any.\n}\n```\n\n### fileDeleted\n\nProvides event notifications when a file is deleted from the meeting.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    fileId: string // The ID of the deleted file\n}\n```\n\n### fileUploaded\n\nProvides event notifications when a file is uploaded to the meeting.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    file: {\n      authorParticipantId: string,   // ID of participant who uploaded the file\n      authorParticipantJid: string,  // Full JID of participant who uploaded the file\n      authorParticipantName: string, // Display name of participant who uploaded the file\n      conferenceFullName: string,    // Full conference JID\n      fileId: string,                // Unique ID of the file\n      fileName: string,              // Name of the file\n      fileSize: number,              // Size of the file in bytes\n      fileType: string,              // File extension/type\n      timestamp: number              // Upload timestamp in milliseconds\n    }\n}\n```\n\n### filmstripDisplayChanged\n\nProvides event visibility notifications for the filmstrip that is being updated:\n\n```javascript\n{\n    visible: boolean // Whether or not the filmstrip is displayed or hidden.\n}\n```\n\n### toolbarVisibilityChanged\n\nProvides event notifications when the in-page Jitsi Meet toolbar (toolbox) is shown or hidden.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    visible: boolean // Whether the toolbar is currently visible.\n}\n```\n\n### moderationStatusChanged\n\nProvides event notifications about changes to moderation status.\n\n```javascript\n{\n    mediaType: string, // The media type for which moderation changed.\n    enabled: boolean // Whether or not moderation changed to enabled.\n}\n```\n\n### moderationParticipantApproved\n\nProvides event notifications about participants approvals for moderation.\n\n```javascript\n{\n    id: string, // The ID of the participant that got approved.\n    mediaType: string // The media type for which the participant was approved.\n}\n```\n\n### moderationParticipantRejected\n\nProvides event notifications about participants rejections for moderation.\n\n```javascript\n{\n    id: string, // The ID of the participant that got rejected.\n    mediaType: string // The media type for which the participant was rejected.\n}\n```\n\n### notificationTriggered\n\nProvides event notifications when an application notification occurs.\n\n```javascript\n{\n    title: string, // The notification title.\n    description: string // The notification description.\n}\n```\n\n### participantJoined\n\nProvides event notifications about new participants who join the room.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    id: string, // the id of the participant\n    displayName: string, // the display name of the participant\n    userContext: {\n        id: string // the JWT id of the participant\n    }\n}\n```\n\n### participantKickedOut\n\nProvides event notifications about participants being removed from the room.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    kicked: {\n        id: string, // the id of the participant removed from the room\n        local: boolean // whether or not the participant is the local particiapnt\n    },\n    kicker: {\n        id: string // the id of the participant who kicked out the other participant\n    }\n}\n```\n\n### participantLeft\n\nProvides event notifications about participants that leave the meeting room.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    id: string // the id of the participant\n}\n```\n\n### participantRoleChanged\n\nProvides event notifications that fire when the local user role has changed (e.g., none, moderator, participant).\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    id: string // the id of the participant\n    role: string // the new role of the participant\n}\n```\n\n### participantsPaneToggled\n\nProvides event notifications that fire when the participants pane status changes.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    open: boolean // whether the pane is open or not\n}\n```\n\n### participantMuted\n\nProvides event notifications about participant mute state changes.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    participantId: string, // the id of the participant whose mute state changed\n    isMuted: boolean, // whether the participant is now muted\n    mediaType: string // the media type: 'audio' or 'video'\n}\n```\n\n### passwordRequired\n\nProvides event notifications that fire when participants fail to join a password protected room.\n\n### videoConferenceJoined\n\nProvides event notifications that fire when the local user has joined the video conference.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    roomName: string, // the room name of the conference\n    id: string, // the id of the local participant\n    displayName: string, // the display name of the local participant\n    avatarURL: string, // the avatar URL of the local participant\n    breakoutRoom: boolean, // whether the current room is a breakout room\n    visitor: boolean // whether the current user is a visitor\n}\n```\n\n### videoConferenceLeft\n\nProvides event notifications that fire when the local user has left the video conference.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    roomName: string // the room name of the conference\n}\n```\n\n### conferenceCreatedTimestamp\n\nProvides notification of the start time of the video conference.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    timestamp: timestamp // time the conference started\n}\n```\n\n### videoAvailabilityChanged\n\nProvides event notifications about video availability status changes.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    available: boolean // new available status - boolean\n}\n```\n\n### videoMuteStatusChanged\n\nProvides event notifications about video mute status changes.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    muted: boolean // new muted status - boolean\n}\n```\n\n### videoQualityChanged\n\nProvides event notifications about changes to video quality settings.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    videoQuality: number // the height of the resolution related to the new video quality setting.\n}\n```\n\n### readyToClose\n\nProvides event notifications that fire when Jitsi Meet is ready to be closed (i.e., hangup operations are completed).\n\n### recordingLinkAvailable\n\nProvides event notifications about recording link becoming available.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    link: string, // the recording link\n    ttl: number // the time to live of the recording link\n}\n```\n\n### recordingStatusChanged\n\nProvides event notifications about recording status changes.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    on: boolean // new recording status - boolean,\n    mode: string // recording mode, `local`, `stream` or `file`,\n    error: string | undefined // error type if recording fails, undefined otherwise\n    transcription: boolean // whether a transcription is active or not\n}\n```\n\n### subjectChange\n\nProvides event notifications regarding the change of subject for a conference.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    subject: string // the new subject\n}\n```\n\n### suspendDetected\n\nProvides notifications about detecting suspended events in the host computer.\n\n### peerConnectionFailure\n\nNotify the external application that a PeerConnection lost connectivity. This event is fired only if\na PC `failed` but connectivity to the rtcstats server is still maintained signaling that there is a\nproblem establishing a link between the app and the JVB server or the remote peer in case of P2P.\nWill only fire if rtcstats is enabled.\n\n```javascript\n{\n    // Type of PC, Peer2Peer or JVB connection.\n    isP2P: boolean,\n\n    // Was this connection previously connected. If it was it could mean\n    // that connectivity was disrupted, if not it most likely means that the app could not reach\n    // the JVB server, or the other peer in case of P2P.\n    wasConnected: boolean\n}\n```\n\n### transcribingStatusChanged\n\nProvides event notifications about status changes in the transcribing process.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    on: boolean,\n}\n```\n\n### transcriptionChunkReceived\n\nProvides event notifications about new transcription chunks being available.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    // Transcription language\n    language: string,\n\n    // ID for this chunk.\n    messageID: string,\n\n    // participant info\n    participant: {\n        avatarUrl: string,\n        id: string\n        name: string,\n    },\n\n    // If the transcription is final, the text will be here.\n    final: string,\n\n    // If the transcription is not final but has high accuracy the text will be here.\n    stable: string,\n\n    // If the transcription is not final but has low accuracy the text will be here.\n    unstable: string,\n}\n```\n\n### whiteboardStatusChanged\n\nProvides event notifications about changes to the whiteboard.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    status: string // new whiteboard status\n}\n```\n\n### p2pStatusChanged\n\nProvides event notifications about changes to the connection type.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    isP2p: boolean|null // whether the new connection type is P2P\n}\n```\n\n\n### audioOnlyChanged\n\nProvides event notifications about changes to the audio only mode status.\n\nThe listener receives an object with the following structure:\n\n```javascript\n{\n    audioOnlyChanged: boolean // whether the audio only is enabled or disabled.\n}\n```\n\n[config.js]: https://github.com/jitsi/jitsi-meet/blob/master/config.js\n[interface_config.js]: https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js\n[EventEmitter]: https://nodejs.org/api/events.html\n[MouseEvent]: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent\n"
  },
  {
    "path": "docs/dev-guide/iframe-functions.md",
    "content": "---\nid: dev-guide-iframe-functions\ntitle: Functions\n---\n\nUse the following API functions to control your embedded Jitsi Meet Conference.\n\n### captureCameraPicture\n\nMobile browsers only. Captures a high quality picture using the device's camera. All parameters are optional.\n\n```javascript\napi.captureCameraPicture(\n        cameraFacingMode, // the facing mode: environment/user. Defaults to environment.\n        descriptionText, // a custom description text to replace the default text on the consent dialog.\n        titleText // a custom title to replace the default title on the consent dialog.\n    ).then(data => {\n    // data is an Object with only one param, either dataURL on success or error on failure.\n    // - dataURL is the base64 string of the taken picture\n    // - error is a string, a verbose explanation of the problem\n    // data.dataURL = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAA...\"\n});\n```\n\n### captureLargeVideoScreenshot\n\nCaptures a screenshot for the participant in the large video view (on stage).\n\n```javascript\napi.captureLargeVideoScreenshot().then(data => {\n    // data is an Object with only one param, dataURL\n    // data.dataURL = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAA...\"\n});\n```\n\n### getAvailableDevices\n\nRetrieves a list of available devices.\n\n```javascript\napi.getAvailableDevices().then(devices => {\n    // devices = {\n    //     audioInput: [{\n    //         deviceId: 'ID'\n    //         groupId: 'grpID'\n    //         kind: 'audioinput'\n    //         label: 'label'\n    //     },....],\n    //     audioOutput: [{\n    //         deviceId: 'ID'\n    //         groupId: 'grpID'\n    //         kind: 'audioOutput'\n    //         label: 'label'\n    //     },....],\n    //     videoInput: [{\n    //         deviceId: 'ID'\n    //         groupId: 'grpID'\n    //         kind: 'videoInput'\n    //         label: 'label'\n    //     },....]\n    // }\n    ...\n});\n```\n\n### getContentSharingParticipants\n\nReturns a promise which resolves with an array of currently sharing participants ID's.\n\n```javascript\napi.getContentSharingParticipants().then(res => {\n    //res.sharingParticipantIds = [particId1, particId2, ...]\n});\n```\n\n### getCurrentDevices\n\nRetrieves a list of currently selected devices.\n\n```javascript\napi.getCurrentDevices().then(devices => {\n    // devices = {\n    //     audioInput: {\n    //         deviceId: 'ID'\n    //         groupId: 'grpID'\n    //         kind: 'videoInput'\n    //         label: 'label'\n    //     },\n    //     audioOutput: {\n    //         deviceId: 'ID'\n    //         groupId: 'grpID'\n    //         kind: 'videoInput'\n    //         label: 'label'\n    //     },\n    //     videoInput: {\n    //         deviceId: 'ID'\n    //         groupId: 'grpID'\n    //         kind: 'videoInput'\n    //         label: 'label'\n    //     }\n    // }\n    ...\n});\n```\n\n### getDeploymentInfo\n\nRetrieves an object containing information about the deployment.\n\n```javascript\napi.getDeploymentInfo().then(deploymentInfo => {\n    // deploymentInfo = {\n    //     region: 'deployment-region',\n    //     shard: 'deployment-shard',\n    //     ...\n    // }\n    ...\n});\n```\n\n### getLivestreamUrl\n\nRetrieves an object containing information about livestreamUrl of the current live stream.\n\n```javascript\napi.getLivestreamUrl().then(livestreamData => {\n    // livestreamData = {\n    //     livestreamUrl: 'livestreamUrl'\n    // }\n    ...\n});\n```\n\n### getParticipantsInfo\n\n__DEPRECATED__ Use `getRoomsInfo` instead.\n\nReturns an array containing participant information such as ID, display name, avatar URL, and email.\n\n```javascript\napi.getParticipantsInfo();\n```\n\n### getRoomsInfo\n\nReturns an array of available rooms and details of it:\n- `isMainRoom` (true,false), `id`, `jid`\n- participants: `Participant[]`\n    - `id`\n    - `jid`\n    - `role`\n    - `displayName`\n    - `userContext`\n\n```javascript\napi.getRoomsInfo().then(rooms => {\n    ... // see response example structure\n})\n```\n\nResponse example structure:\n\n```json\n{\n  \"rooms\": [\n    {\n      \"isMainRoom\": true,\n      \"id\": \"room_name@conference.jitsi\",\n      \"jid\": \"room_name@conference.jitsi/aaaaaa\",\n      \"participants\": [\n        {\n          \"jid\": \"room_name@conference.jitsi/bbbbbb\",\n          \"role\": \"participant\",\n          \"displayName\": \"p1\",\n          \"id\": \"bbbbbb\",\n          \"userContext\": {\n            \"id\": \"google-oauth2|12345678901234567890\"\n          }\n        },\n        {\n          \"jid\": \"room_name@conference.jitsi/cccccc\",\n          \"role\": \"participant\",\n          \"displayName\": \"p2\",\n          \"id\": \"cccccc\",\n          \"userContext\": {\n            \"id\": \"400e45d607256777df4e0f3a6a447901\"\n          }\n        }\n      ]\n    },\n    {\n    \"isMainRoom\": false,\n    \"id\": \"aaaaaa-bbb-cccc-dddd-qwertyuiopas\",\n    \"jid\": \"aaaaaa-bbb-cccc-dddd-qwertyuiopas@breakout.jitsi\",\n    \"participants\": [{\n        \"jid\": \"aaaaaa-cccc-dddd-eeee-qwertyuiopas@jitsi/abcd1234\",\n        \"role\": \"moderator\",\n        \"displayName\": \"Participant name\",\n        \"avatarUrl\": \"\",\n        \"id\": \"abcd1234\",\n        \"userContext\": {\n            \"id\": \"73317803a589aaa027132696dd77ac34\"\n        }\n    }]\n    },\n  ]\n}\n```\n\n### getSessionId\n\nReturns the meting's unique Id (`sessionId`). \nPlease note that the `sessionId` is not available when in prejoin screen and it's not guaranteed to be available immediately after joining - in which cases it will be empty.\n\n```javascript\napi.getSessionId().then(sessionId => {\n    //sessionId: string\n    ...\n});\n```\n\n### getSharedDocumentUrl\n\nReturns the meeting's unique etherpad shared document url (`sharedDocumentUrl`). \nPlease note that the `sharedDocumentUrl` is not available when in prejoin screen and it's not guaranteed to be available immediately after joining - in which cases it will be empty.\n\n```javascript\napi.getSharedDocumentUrl().then(sharedDocumentUrl => {\n    //sharedDocumentUrl: string\n    ...\n});\n```\n\n### getVideoQuality\n\nReturns the current video quality setting.\n\n```javascript\napi.getVideoQuality();\n```\n\n### getSupportedCommands\n\nReturns array of commands supported by `api.executeCommand(command, ...arguments)`;\n\n```javascript\napi.getSupportedCommands();\n```\n\n### getSupportedEvents\n\nReturns array of events supported by `api.addListener(event, listener)`;\n\n```javascript\napi.getSupportedEvents();\n```\n\n### isDeviceChangeAvailable\n\nResolves to true if the device change is available and to false if not.\n\n```javascript\n// The accepted deviceType values are - 'output', 'input' or undefined.\napi.isDeviceChangeAvailable(deviceType).then(isDeviceChangeAvailable => {\n    ...\n});\n```\n\n### isDeviceListAvailable\n\nResolves to true if the device list is available and to false if not.\n\n```javascript\napi.isDeviceListAvailable().then(isDeviceListAvailable => {\n    ...\n});\n```\n\n### isMultipleAudioInputSupported\n\nResolves to true if multiple audio input is supported and to false if not.\n\n```javascript\napi.isMultipleAudioInputSupported().then(isMultipleAudioInputSupported => {\n    ...\n});\n```\n\n### pinParticipant\n\nSelects the participant ID to be the pinned participant in order to always receive video for this participant.\n\nThe second parameter is optional and can be used to specify a `videoType`. When multistream support is enabled by passing this parameter you can specify whether the desktop or the camera video for the specified participant should be pinned. The accepted values are `'camera'` and `'desktop'`. The default is `'camera'`. Any invalid values will be ignored and default will be used.\n\n```javascript\napi.pinParticipant(participantId, videoType);\n```\n\n### resizeLargeVideo\n\nResizes the large video container per the provided dimensions.\n\n```javascript\napi.resizeLargeVideo(width, height);\n```\n\n### setAudioInputDevice\n\nSets the audio input device to the one with the passed label or ID.\n\n```javascript\napi.setAudioInputDevice(deviceLabel, deviceId);\n```\n\n### setAudioOutputDevice\n\nSets the audio output device to the one with the passed label or ID.\n\n```javascript\napi.setAudioOutputDevice(deviceLabel, deviceId);\n```\n\n### setLargeVideoParticipant\n\nDisplays the participant with the given participant ID on the large video.\n\nIf no participant ID is given, a participant is picked based on the dominant, pinned speaker settings.\n\n```javascript\napi.setLargeVideoParticipant(participantId);\n```\n\n### setVideoInputDevice\n\nSets the video input device to the one with the passed label or ID.\n\n```javascript\napi.setVideoInputDevice(deviceLabel, deviceId);\n```\n\n### setVirtualBackground\n\nSet your virtual background with a base64 image.\n\n```javascript\n/**\n * @param {boolean} enabled - Enable or disable the virtual background.\n * @param {string} backgroundImage - Base64 image string, eg. \"data:image/png;base64, iVBOR...\".\n */\napi.setVirtualBackground(enabled, backgroundImage);\n```\n\n### startRecording\n\nStarts a file recording or streaming session. See the `startRecording` command for more details.\n\n```javascript\napi.startRecording(options);\n```\n\n### stopRecording\n\nStops an ongoing file recording, streaming session or transcription. See the `stopRecording` command for more details.\n\n```javascript\napi.stopRecording(mode, transcription);\n```\n\n### getNumberOfParticipants\n\nReturns the number of conference participants:\n\n```javascript\nconst numberOfParticipants = api.getNumberOfParticipants();\n```\n\n### getAvatarURL\n\n__DEPRECATED__ Use `getRoomsInfo` instead.\n\nReturns a participant's avatar URL:\n\n```javascript\nconst avatarURL = api.getAvatarURL(participantId);\n```\n\n### getDisplayName\n\nReturns a participant's display name:\n\n```javascript\nconst displayName = api.getDisplayName(participantId);\n```\n\n### getEmail\n\nReturns a participant's email:\n\n```javascript\nconst email = api.getEmail(participantId);\n```\n\n### getIFrame\n\nReturns the IFrame HTML element which is used to load the Jitsi Meet conference:\n\n```javascript\nconst iframe = api.getIFrame();\n```\n\n### isAudioDisabled\n\nReturns a Promise which resolves to the current audio disabled state:\n\n```javascript\napi.isAudioDisabled().then(disabled => {\n    ...\n});\n```\n\n### isAudioMuted\n\nReturns a Promise which resolves to the current audio muted state:\n\n```javascript\napi.isAudioMuted().then(muted => {\n    ...\n});\n```\n\n### isVideoMuted\n\nReturns a Promise which resolves to the current video muted state:\n\n```javascript\napi.isVideoMuted().then(muted => {\n    ...\n});\n```\n\n### isAudioAvailable\n\nReturns a Promise which resolves to the current audio availability state:\n\n```javascript\napi.isAudioAvailable().then(available => {\n    ...\n});\n```\n\n### isVideoAvailable\n\nReturns a Promise which resolves to the current video availability state:\n\n```javascript\napi.isVideoAvailable().then(available => {\n    ...\n});\n```\n\n### isVisitor\n\nReturns a whether the current user is a visitor or not.\n\n```javascript\nconst isVisitor = api.isVisitor();\n```\n\n### isModerationOn\n\nReturns a Promise which resolves to the current moderation state of the given media type.\n\n`mediaType` can be either `audio` (default) or `video`.\n\n```javascript\napi.isModerationOn(mediaType).then(isModerationOn => {\n    ...\n});\n```\n\n### isP2pActive\n\nReturns a Promise which resolves to a Boolean or null, when there is no conference.\n\n```javascript\napi.isP2pActive().then(isP2p => {\n    ...\n});\n```\n\n### isParticipantForceMuted\n\nReturns a Promise which resolves to the current force mute state of the given participant for the given media type.\n\n`mediaType` can be either `audio` (default) or `video`.\n\nForce muted - moderation is on and participant is not allowed to unmute the given media type.\n\n```javascript\napi.isParticipantForceMuted(participantId, mediaType).then(isForceMuted => {\n    ...\n});\n```\n\n### isParticipantsPaneOpen\n\nReturns a Promise which resolves with the current participants pane state.\n\n```javascript\napi.isParticipantsPaneOpen().then(state => {\n    ...\n});\n```\n\n### isStartSilent\n\nReturns a Promise which resolves with whether meeting was started in view only.\n\n```javascript\napi.isStartSilent().then(startSilent => {\n    ...\n});\n```\n\n### listBreakoutRooms\n\nReturns a Promise which resolves with the map of breakout rooms.\n\n```javascript\napi.listBreakoutRooms().then(breakoutRooms => {\n    ...\n});\n```\n\n### invite\n\nInvite the given array of participants to the meeting:\n\n```javascript\napi.invite([ {...}, {...}, {...} ]).then(() => {\n    // success\n}).catch(() => {\n    // failure\n});\n```\n**NOTE:** The invitee format in the array depends on the invite service used in the deployment.\n\nPSTN invite objects have the following structure:\n\n```javascript\n{\n    type: 'phone',\n    number: <string> // the phone number in E.164 format  (ex. +31201234567)\n}\n```\n\nSIP invite objects have the following structure:\n\n```javascript\n{\n    type: 'sip',\n    address: <string> // the sip address\n}\n```\n\n### dispose\n\nRemoves the embedded Jitsi Meet conference:\n\n```javascript\napi.dispose();\n```\n\n**NOTE:** Jitsi recommends removing the conference before the page is unloaded.\n"
  },
  {
    "path": "docs/dev-guide/iframe.md",
    "content": "---\nid: dev-guide-iframe\ntitle: IFrame API\n---\n\nEmbedding the Jitsi Meet API into your site or app enables you to host and provide secure video meetings with your colleagues, teams, and stakeholders. The Meet API provides a full complement of comprehensive meeting features.\n\nYour Jitsi meetings can be hosted and attended using any device while keeping your data and privacy protected. You can reach your meeting participants anywhere in the world eliminating the need for travel and the associated inconvenience.\n\nThe IFrame API enables you to embed Jitsi Meet functionality into your meeting application so you can experience the full functionality of the globally distributed and highly available deployment available with [meet.jit.si](https://meet.jit.si/).\n\nYou can also embed and integrate the globally distributed and highly available deployment on the [meet.jit.si](https://meet.jit.si/) platform itself. \n\n:::note NOTE\nJaaS customers, please make sure you also read [this](https://developer.8x8.com/jaas/docs/iframe-api-overview)!\n:::\n\n:::tip\nIf you use React in your web application you might want to use our [React SDK](dev-guide-react-sdk) instead.\n:::\n\n## Integration\n\nTo enable the Jitsi Meet API in your application you must use one of the following JavaScript (JS) Jitsi Meet API library scripts and integrate it into your application:\n\nFor self-hosting in your domain:\n\n```javascript\n<script src='https://<your-domain>/external_api.js'></script>\n```\n\nmeet.jit.si:\n```javascript\n<script src='https://meet.jit.si/external_api.js'></script>\n\n```\n\n## Mobile support\n\nThe iframe API works on mobile browsers the same way as it does on desktop browsers.\n\n### Opening meetings in the Jitsi Meet app\n\nIn order to open meetings with the Jitsi Meet app you can use our custom URL scheme as follows:\n\n(let's assume the meeting is https://meet.jit.si/test123)\n\n* Android: `intent://meet.jit.si/test123#Intent;scheme=org.jitsi.meet;package=org.jitsi.meet;end`\n* iOS: `org.jitsi.meet://meet.jit.si/test123`\n\nThis works with custom servers too, just replace `meet.jit.si` with your custom server URL.\n\n## Creating the Jitsi Meet API object\n\nAfter you have integrated the Meet API library, you must then create the Jitsi Meet API object.\n\nThe Meet API object takes the following form:\n\n**`api = new JitsiMeetExternalAPI(domain, options)`**\n\nThe API object constructor uses the following options:\n\n* `domain`: The domain used to build the conference URL (e.g., **`meet.jit.si`**).\n* `options`: The object with properties. \n\n  IFrame arguments include:\n  \n    * `roomName`: The name of the room to join.\n\n    * `width`: _Optional._ The created IFrame width.\n    \n      The width argument has the following characteristics:\n    \n      - A numerical value indicates the width in pixel units.\n    \n      - If a string is specified the format is a number followed by **`px`**, **`em`**, **`pt`**, or **`%`**.\n    \n    * `height`: _Optional._ The height for the created IFrame. \n    \n      The height argument has the following characteristics: \n    \n      - A numerical value indicates the height in pixel units.\n    \n      - If a string is specified the format is a number followed by **`px`**, **`em`**, **`pt`**, or **`%`**. \n    \n    * `parentNode`: The HTML DOM Element where the IFrame is added as a child.\n    \n    * `configOverwrite`: _Optional._ The JS object with overrides for options defined in the [config.js] file.\n    \n    * `interfaceConfigOverwrite`: _Optional._ The JS object with overrides for options defined in the [interface_config.js] file.\n    \n    * `jwt`: _Optional._ The [JWT](https://jwt.io/) token.\n    \n    * `onload`: _Optional._ The IFrame onload event handler.\n    \n    * `invitees`: _Optional._ Object arrays that contain information about participants invited to a call.\n    \n    * `devices`: _Optional._ Information map about the devices used in a call.\n    \n    * `userInfo`: _Optional._ The JS object that contains information about the participant starting or joining the meeting (e.g., email).\n\n    * `lang`: _Optional._ The default meeting language.\n\n    * `iceServers`: _Optional._ Object with rules that will be used to modify/remove the existing ice server configuration. **NOTE: This property is currently experimental and may be removed in the future!**\n\n\nFor example:\n\n```javascript\nconst domain = 'meet.jit.si';\nconst options = {\n    roomName: 'JitsiMeetAPIExample',\n    width: 700,\n    height: 700,\n    parentNode: document.querySelector('#meet'),\n    lang: 'de'\n};\nconst api = new JitsiMeetExternalAPI(domain, options);\n```\n\nYou can set the initial media devices for the call using the following:\n\n```javascript\nconst domain = 'meet.jit.si';\nconst options = {\n    ...\n    devices: {\n        audioInput: '<deviceLabel>',\n        audioOutput: '<deviceLabel>',\n        videoInput: '<deviceLabel>'\n    },\n    ...\n};\nconst api = new JitsiMeetExternalAPI(domain, options);\n```\n\nYou can override options set in the [config.js] file and the [interface_config.js] file using the **`configOverwrite`** and **`interfaceConfigOverwrite`** objects, respectively.\n\nFor example:\n\n```javascript\nconst options = {\n    ...\n    configOverwrite: { startWithAudioMuted: true },\n    interfaceConfigOverwrite: { DISABLE_DOMINANT_SPEAKER_INDICATOR: true },\n    ...\n};\nconst api = new JitsiMeetExternalAPI(domain, options);\n```\nTo pass a JWT token to Jitsi Meet use the following:\n\n ```javascript\nconst options = {\n    ...\n    jwt: '<jwt_token>',\n    ...\n};\nconst api = new JitsiMeetExternalAPI(domain, options);\n ```\n\nYou can set the **`userInfo`** (e.g., email, display name) for the call using the following:\n\n```javascript\nvar domain = \"meet.jit.si\";\nvar options = {\n    ...\n    userInfo: {\n        email: 'email@jitsiexamplemail.com',\n        displayName: 'John Doe'\n    }\n}\nvar api = new JitsiMeetExternalAPI(domain, options);\n```\n\nexport const Anchor = ({children, name}) => (\n  <a\n    name = { name }\n    id = { name }\n    href = { \"#\" + name }>\n    {children}\n  </a>\n);\n\n<Anchor name = { \"ice-servers\" }></Anchor>\n\nYou can modify the default ice servers configuration with the **`iceServers`** property (**NOTE: This property is currently experimental and may be removed in the future!**) using the following:\n\n\n```javascript\nvar domain = \"meet.jit.si\";\nvar options = {\n    ...\n    iceServers: {\n        replace: [\n            { // replace the URL of all existing ice servers with type matching targetType \n                targetType: 'turn',\n                urls: 'turn:example.com:443'\n            },\n            { // replace the URL of all existing ice servers with type matching targetType \n                targetType: 'turns',\n                urls: 'turns:example.com:443?transport=tcp'\n            },\n            { // remove all existing ice servers with type matching targetType \n                targetType: 'stun',\n                urls: null\n            }\n        ]\n    },\n    ...\n}\nvar api = new JitsiMeetExternalAPI(domain, options);\n```\n\nConfiguring the tile view:\n\nYou can configure the maximum number of columns in the tile view by overriding the **`TILE_VIEW_MAX_COLUMNS`** property from the [interface_config.js] file via the **`interfaceConfigOverwrite`** object:\n\n```javascript\nconst options = {\n    ...\n    interfaceConfigOverwrite: { TILE_VIEW_MAX_COLUMNS: 2 },\n    ...\n};\nconst api = new JitsiMeetExternalAPI(domain, options);\n```\n:::note\n**`TILE_VIEW_MAX_COLUMNS`** accepts values from 1 to 5. The default value is 5.\n:::\n\n\n## Functions\n\nAll functions are documented [here](/handbook/docs/dev-guide/dev-guide-iframe-functions) now.\n\n## Commands\n\nAll commands are documented [here](/handbook/docs/dev-guide/dev-guide-iframe-commands) now.\n\n## Events\n\nAll events are documented [here](/handbook/docs/dev-guide/dev-guide-iframe-events) now.\n\n[config.js]: https://github.com/jitsi/jitsi-meet/blob/master/config.js\n[interface_config.js]: https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js\n"
  },
  {
    "path": "docs/dev-guide/ios-sdk.md",
    "content": "---\nid: dev-guide-ios-sdk\ntitle: iOS SDK\n---\n\nThe Jitsi Meet iOS SDK provides the same user experience as the Jitsi Meet app,\nin a customizable way which you can embed in your apps.\n\n:::important\niOS 15.1 or higher is required.\n:::\n\n## Sample applications using the SDK\n\nIf you want to see how easy integrating the Jitsi Meet SDK into a native application is, take a look at the\n[sample applications repository](https://github.com/jitsi/jitsi-meet-sdk-samples#ios).\n\n## Usage\n\nThere are 2 ways to integrate the SDK into your project:\n\n- Using CocoaPods\n- Building it yourself\n\n### Using CocoaPods\n\nThe recommended way for using the SDK is by using CocoaPods. In order to\ndo so, add the `JitsiMeetSDK` dependency to your existing `Podfile` or create\na new one following this example:\n\n```ruby\nplatform :ios, '15.1'\n\nworkspace 'JitsiMeetSDKTest.xcworkspace'\n\ntarget 'JitsiMeetSDKTest' do\n  project 'JitsiMeetSDKTest.xcodeproj'\n\n  pod 'JitsiMeetSDK'\nend\n```\n\nReplace `JitsiMeetSDKTest` with your project and target names.\n\nSince the SDK requests camera and microphone access, make sure to include the\nrequired entries for `NSCameraUsageDescription` and `NSMicrophoneUsageDescription`\nin your `Info.plist` file.\n\nIn order for app to properly work in the background, select the \"audio\" and \"voip\"\nbackground modes.\n\nLast, since the SDK shows and hides the status bar based on the conference state,\nyou may want to set `UIViewControllerBasedStatusBarAppearance` to `NO` in your\n`Info.plist` file.\n\n### Building it yourself\n\n1. Install all required [dependencies](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-mobile-jitsi-meet).\n\n2. Build it:\n\n```bash\nmkdir -p ios/sdk/out\nxcodebuild clean \\\n    -workspace ios/jitsi-meet.xcworkspace \\\n    -scheme JitsiMeetSDK\nxcodebuild archive \\\n    -workspace ios/jitsi-meet.xcworkspace \\\n    -scheme JitsiMeetSDK  \\\n    -configuration Release \\\n    -sdk iphonesimulator \\\n    -destination='generic/platform=iOS Simulator' \\\n    -archivePath ios/sdk/out/ios-simulator \\\n    SKIP_INSTALL=NO \\\n    BUILD_LIBRARY_FOR_DISTRIBUTION=YES\nxcodebuild archive \\\n    -workspace ios/jitsi-meet.xcworkspace \\\n    -scheme JitsiMeetSDK  \\\n    -configuration Release \\\n    -sdk iphoneos \\\n    -destination='generic/platform=iOS' \\\n    -archivePath ios/sdk/out/ios-device \\\n    SKIP_INSTALL=NO \\\n    BUILD_LIBRARY_FOR_DISTRIBUTION=YES\nxcodebuild -create-xcframework \\\n    -framework ios/sdk/out/ios-device.xcarchive/Products/Library/Frameworks/JitsiMeetSDK.framework \\\n    -framework ios/sdk/out/ios-simulator.xcarchive/Products/Library/Frameworks/JitsiMeetSDK.framework \\\n    -output ios/sdk/out/JitsiMeetSDK.xcframework\ncp -a ios/Pods/hermes-engine/destroot/Library/Frameworks/universal/hermes.xcframework ios/sdk/out\n```\n\nAfter successfully building Jitsi Meet SDK for iOS, the resulting XCFramework(s) will be in the ios/sdk/out directory.\n\nIf you are embedding the Framework directly into your project you'll also need to add the generated `hermes.xcframework`.\n\nNOTE: Your app will need to depend on the JitsiWebRTC CocoaPod.\n\n## API\n\nJitsiMeet is an iOS framework which embodies the whole Jitsi Meet experience and\nmakes it reusable by third-party apps.\n\nTo get started:\n\n1. Add a `JitsiMeetView` to your app using a Storyboard or Interface Builder,\n   for example.\n\n2. Then, once the view has loaded, set the delegate in your controller and load\n   the desired URL:\n\n```objc\n- (void)viewDidLoad {\n  [super viewDidLoad];\n\n  JitsiMeetView *jitsiMeetView = (JitsiMeetView *) self.view;\n  jitsiMeetView.delegate = self;\n\n  JitsiMeetConferenceOptions *options = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {\n      builder.serverURL = [NSURL URLWithString:@\"https://meet.jit.si\"];\n      builder.room = @\"test123\";\n      builder.audioOnly = YES;\n  }];\n\n  [jitsiMeetView join:options];\n}\n```\n\n### JitsiMeetView class\n\nThe `JitsiMeetView` class is the entry point to the SDK. It a subclass of\n`UIView` which renders a full conference in the designated area.\n\n#### delegate\n\nProperty to get/set the `JitsiMeetViewDelegate` on `JitsiMeetView`.\n\n#### join:JitsiMeetConferenceOptions\n\nJoins the conference specified by the given options.\n\n```objc\n  JitsiMeetConferenceOptions *options = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {\n      builder.serverURL = [NSURL URLWithString:@\"https://meet.jit.si\"];\n      builder.room = @\"test123testing\";\n      builder.audioOnly = NO;\n      builder.audioMuted = NO;\n      builder.videoMuted = NO;\n      builder.welcomePageEnabled = NO;\n     [builder setConfigOverride:@\"requireDisplayName\" withBoolean:YES];\n     [builder setConfigOverride:@\"customToolbarButtons\" withArray:@[\n        @{\n            @\"icon\": @\"ICON_URL\",\n            @\"id\": @\"CUSTOM_BTN_ID\"\n        },\n        @{\n            @\"icon\": @\"ICON_URL\",\n            @\"id\": @\"CUSTOM_BTN_ID\"\n        },\n        @{\n            @\"icon\": @\"ICON_URL\",\n            @\"id\": @\"CUSTOM_BTN_ID\"\n        },\n        @{\n            @\"icon\": @\"ICON_URL\",\n            @\"id\": @\"CUSTOM_BTN_ID\"\n        },\n        @{\n            @\"backgroundColor\": @\"CUSTOM_BTN_BACKGROUND_COLOR\",\n            @\"icon\": @\"ICON_URL\",\n            @\"id\": @\"CUSTOM_BTN_ID\"\n        }\n     ]];\n        <!-- If you want your custom button/s to appear inside the toolbar, \n        you will need to set your toolbar buttons too and always include \"overflowmenu\", \"hangup\".\n        All the buttons that, because of the screen size, won't fit the toolbar, will be automatically moved to the overflow menu. -->\n     [builder setConfigOverride:@\"toolbarButtons\" withArray:@[@\"CUSTOM_BTN_ID\", @\"CUSTOM_BTN_ID\", @\"CUSTOM_BTN_ID\", @\"CUSTOM_BTN_ID\", @\"CUSTOM_BTN_ID\", @\"overflowmenu\", @\"hangup\"]];\n  }];\n\n  [jitsiMeetView join:options];\n```\n\n#### leave\n\nLeaves the currently active conference.\n\n#### hangUp\n\nThe localParticipant leaves the current conference.\n\n#### setAudioMuted\n\nSets the state of the localParticipant audio muted according to the `muted` parameter.\n\n#### setVideoMuted\n\nSets the state of the localParticipant video muted according to the `muted` parameter.\n\n#### sendEndpointTextMessage\n\nSends a message via the data channel to one particular participant or to all of them.\nIf the `to` param is empty, the message will be sent to all the participants in the conference.\n\nIn order to get the participantId, the `PARTICIPANT_JOINED` event should be listened for,\nwhich `data` includes the id and this should be stored somehow.\n\n#### toggleScreenShare\n\nSets the state of the localParticipant screen sharing according to the `enabled` parameter.\n\n#### openChat\n\nOpens the chat dialog. If `to` contains a valid participantId, the private chat with that particular participant will be opened.\n\n#### sendChatMessage\n\nSends a chat message via to one particular participant or to all of them.\nIf the `to` param is empty, the message will be sent to all the participants in the conference.\n\nIn order to get the participantId, the `PARTICIPANT_JOINED` event should be listened for,\nwhich `data` includes the id and this should be stored somehow.\n\n#### closeChat\n\nCloses the chat dialog.\n\n#### retrieveParticipantsInfo\n\nRetrieves the participants information in the completionHandler sent as parameter.\n\n#### Universal / deep linking\n\nIn order to support Universal / deep linking, `JitsiMeet` offers 2 class\nmethods that you app's delegate should call in order for the app to follow those\nlinks.\n\nIf these functions return NO it means the URL wasn't handled by the SDK. This\nis useful when the host application uses other SDKs which also use linking.\n\n```objc\n-  (BOOL)application:(UIApplication *)application\ncontinueUserActivity:(NSUserActivity *)userActivity\n  restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler\n{\n  return [[JitsiMeet sharedInstance] application:application\n               continueUserActivity:userActivity\n                 restorationHandler:restorationHandler];\n}\n```\n\nAnd also one of the following:\n\n```objc\n// See https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623073-application?language=objc\n- (BOOL)application:(UIApplication *)app\n            openURL:(NSURL *)url\n            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {\n  return [[JitsiMeet sharedInstance] application:app\n                            openURL:url\n                            options: options];\n}\n```\n\n### JitsiMeetViewDelegate\n\nThis delegate is optional, and can be set on the `JitsiMeetView` instance using\nthe `delegate` property.\n\nIt provides information about the conference state: was it joined, left, did it\nfail?\n\nAll methods in this delegate are optional.\n\n#### conferenceJoined\n\nCalled when a conference was joined. `data` contains the following information:\n\n- `url`: the conference URL\n\n#### conferenceTerminated\n\nCalled when the active conference ends, be it because of user choice or because of a failure. `data` contains the\nfollowing information:\n\n- `url`: the conference URL\n- `error`: missing if the conference finished gracefully, otherwise contains the error message\n\n#### conferenceWillJoin\n\nCalled before a conference is joined. `data` contains the following information:\n\n- `url`: the conference URL\n\n#### enterPictureInPicture\n\nCalled when entering Picture-in-Picture is requested by the user. The app should\nnow activate its Picture-in-Picture implementation (and resize the associated\n`JitsiMeetView`. The latter will automatically detect its new size and adjust\nits user interface to a variant appropriate for the small size ordinarily\nassociated with Picture-in-Picture.)\n\nThe `data` dictionary is empty.\n\n#### participantJoined\n\nCalled when a participant has joined the conference. `data` contains the following information:\n\n- `email`: the email of the participant. It may not be set if the remote participant didn't set one.\n- `name`: the name of the participant.\n- `role`: the role of the participant.\n- `participantId`: the id of the participant.\n\n#### participantLeft\n\nCalled when a participant has left the conference. `data` contains the following information:\n\n- `participantId`: the id of the participant that left.\n\n#### audioMutedChanged\n\nCalled when the local participant's audio is muted or unmuted. `data` contains the following information:\n\n- `muted`: a boolean indicating whether the audio is muted or not.\n\n#### videoMutedChanged\n\nCalled when the local participant's video is muted or unmuted. `data` contains the following information:\n\n- `muted`: an integer indicating whether the video is muted or not. 0 means unmuted, 4 means muted.\n\n#### endpointTextMessageReceived\n\nCalled when an endpoint text message is received.\n\nThe `data` dictionary contains a `senderId` key with the participantId of the sender and a `message` key with the\ncontent.\n\n#### screenShareToggled\n\nCalled when a participant starts or stops sharing his screen.\n\nThe `data` dictionary contains a `participantId` key with the id of the participant and a 'sharing' key with boolean\nvalue.\n\n#### chatMessageReceived\n\nCalled when a chat text message is received. `data` contains the following information:\n\n- `senderId`: the id of the participant that sent the message.\n- `message`: the content of the message.\n- `isPrivate`: true if the message is private, false otherwise.\n- `timestamp`: the (optional) timestamp of the message.\n\n#### chatToggled\n\nCalled when the chat dialog is opened or closed. `data` contains the following information:\n\n- `isOpen`: true if the chat dialog is open, false otherwise.\n\n#### readyToClose\n\nCalled when the SDK is ready to be closed. No meeting is happening at this point.\n\n#### customButtonPressed\n\nCalled when custom buttons are added to the overflow menu. `data` contains the following information:\n\n- `id`: the id of the pressed custom button.\n- `text`: the label of the pressed custom button.\n\n#### conferenceUniqueIdSet\n\nCalled when a meeting unique id was set. `data` contains the following information:\n\n- `sessionId`: the unique conference id.\n\n#### recordingStatusChanged\n\nCalled when current recording status is changing. `data` contains the following information:\n\n- `error`\n- `id`\n- `initiator`\n- `liveStreamViewURL`\n- `mode`\n- `status`\n- `terminator`\n- `timestamp`\n\n### Picture-in-Picture\n\n`JitsiMeetView` will automatically adjust its UI when presented in a\nPicture-in-Picture style scenario, in a rectangle too small to accommodate its\n\"full\" UI.\n\nJitsi Meet SDK does not currently implement native Picture-in-Picture on iOS. If\ndesired, apps need to implement non-native Picture-in-Picture themselves and\nresize `JitsiMeetView`.\n\nIf `delegate` implements `enterPictureInPicture:`, the in-call toolbar will\nrender a button to afford the user to request entering Picture-in-Picture.\n\n## Dropbox integration\n\nTo setup the Dropbox integration, follow these steps:\n\n1. Add the following to the app's Info.plist and change `<APP_KEY>` to your\nDropbox app key:\n```\n<key>CFBundleURLTypes</key>\n<array>\n  <dict>\n    <key>CFBundleURLName</key>\n    <string></string>\n    <key>CFBundleURLSchemes</key>\n    <array>\n      <string>db-<APP_KEY></string>\n    </array>\n  </dict>\n</array>\n<key>LSApplicationQueriesSchemes</key>\n<array>\n  <string>dbapi-2</string>\n  <string>dbapi-8-emm</string>\n</array>\n```\n\n2. Make sure your app calls the Jitsi Meet SDK universal / deep linking delegate methods.\n\n## Screen Sharing integration\n\nThe screen sharing functionality for iOS was added to Jitsi starting with JitsiMeetSDK version 3.3.0. It is available for applications running on iOS 14 or newer.\n\nFor achieving this we are using the `Broadcast Upload Extension` for capturing the contents of the user's screen. Passing the frames to the RN WebRTC is done using Unix stream-oriented sockets communication, the extension acting as the client and the React Native WebRTC being the server.\n\nThe following documentation covers the code provided in the [sample app](https://github.com/jitsi/jitsi-meet-sdk-samples/tree/master/ios/swift-screensharing/JitsiSDKScreenSharingTest).\n\n### Creating the Broadcast Upload Extension\n\nThe `Broadcast Upload Extension` is one of the App Extensions types defined in iOS and is used for capturing the contents of the user's screen.\n\nFor creating the extension you need to add a new target to your application, selecting the `Broadcast Upload Extension` template. Fill in the desired name, change the language to Swift, make sure `Include UI Extension` is not selected, as we don't need custom UI for our case, then press Finish (screenshot 1). You will see that a new folder with the extension's name was added to the project's tree, containing the `SampleHandler.swift` class. Also, make sure to update the `Deployment Info`, for the newly created extension, to iOS 14 or newer. To learn more about creating App Extensions check the [official documentation](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionCreation.html).\n\n![screenshot 1](../assets/iOS_screensharing_1.png \"screenshot 1\")\n\nWith the extension created the next steps are to set up the socket connection, add the functionality for handling the received frames, and send them to RN WebRTC for processing. We will be using the code provided with the sample project for this. Copy `SampleUploader.swift`, `SocketConnection.swift`, `DarwinNotificationCenter.swift`, and `Atomic.swift` files to your extension's folder and make sure they're added to the target.\n\n### Setting up the socket connection\n\nSending the recorded frames to RN WebRTC is done via Unix SOCK_STREAM sockets. The extension needs to be set up as the client endpoint for this.\n\nWe will update `SampleHandler.swift` to initiate the socket connection with RN WebRTC, using the `SocketConnection` class. But before, we have to set up the file that the sockets will use for communication.\n\nEven though an app extension bundle is nested within its containing app’s bundle, the running app extension and containing app have no direct access to each other’s containers. We will address this by enabling data sharing. To enable data sharing, use Xcode or the Developer portal to enable app groups for the containing app and its contained app extensions. Next, register the app group in the portal and specify the app group to use in the containing app. To learn about working with app groups, see [Adding an App to an App Group](https://developer.apple.com/library/archive/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html#//apple_ref/doc/uid/TP40011195-CH4-SW19).\n\nNow, add a `private var socketFilePath: String` to your `SampleHandler` class and set it up with a shared file named `rtc_SSFD`, using the newly registered app group, like this:\n\n```swift\nprivate enum Constants {\n    static let appGroupIdentifier = \"my.custom.app.group\"\n}\n\nprivate var socketFilePath: String {\n    let sharedContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.appGroupIdentifier)\n     \n    return sharedContainer?.appendingPathComponent(\"rtc_SSFD\").path ?? \"\"\n}\n```\n\nNext, we will configure the `SocketConnection` to use the shared file. Add a `private var clientConnection: SocketConnection?` to the `SampleHandler` class and override `init` to set it up, like this:\n\n```swift\noverride init() {\n    super.init()\n    if let connection = SocketConnection(filePath: socketFilePath) {\n        clientConnection = connection\n    }\n} \n```\n\nIn order for this to work, the RN WebRTC end needs to know about the app group identifier we have configured the app with. We are doing this by adding a new key named `RTCAppGroupIdentifier` to the app's `Info.plist` with the app group identifier as the value.\n\n### Opening the socket connection\n\nFor starting screen sharing JitsiMeet SDK provides the UI to present the `RPSystemBroadcastPickerView` to the user. By default, the picker will display a list of all the available broadcast providers. In order to limit the picker to our particular broadcast provider, we have to set `preferredExtension` to the bundle identifier of the broadcast extension. We are doing this by adding a new key named `RTCScreenSharingExtension` to the app's Info.plist and setting the broadcast extension bundle identifier as the value.\n\nOnce screen recording has started ReplayKit invokes the methods to handle video buffers, as well as the methods to handle starting and stopping the broadcast, from the `SampleHandler` class. The `broadcastStarted(withSetupInfo:)` method is our entry point for opening the socket connection with the RN WebRTC server. To do this we have to post the `broadcastStarted` notification the server is listening for, in order to start the connection, and we are ready to connect. Add a new method `openConnection()` to the `SampleHandler` class which will repeatedly attempt connecting to the server, for cases when the server connection start is delayed:\n\n```swift\nfunc openConnection() {\n    let queue = DispatchQueue(label: \"broadcast.connectTimer\")\n    let timer = DispatchSource.makeTimerSource(queue: queue)\n    timer.schedule(deadline: .now(), repeating: .milliseconds(100), leeway: .milliseconds(500))\n    timer.setEventHandler { [weak self] in\n        guard self?.clientConnection?.open() == true else {\n            return\n        }\n        \n        timer.cancel()\n    }\n    \n    timer.resume()\n}\n```\n\nNext, update the `broadcastStarted(withSetupInfo:)` method to post the notification and connect:\n\n```swift\noverride func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {\n    DarwinNotificationCenter.shared.postNotification(.broadcastStarted)\n    openConnection()\n}\n```\n\n`DarwinNotificationCenter` is a simple helper class for broadcasting system-wide notifications, instead of delivering only within a single program, as `NSNotificationCenter` does. This mechanism allows the app to register for notifications sent from the extension.\n\nNow we are ready to start sending video frames.\n\n### Sending video frames\n\nRN WebRTC is designed to work with jpeg encoded images framed in a `CFHTTPMessage` object. The following header fields are required:\n- `Content-Length` - the size of the jpeg data\n- `Buffer-Width` - the width of the buffer, in pixels\n- `Buffer-Height` - the buffer height, in pixels\n- `Buffer-Orientation` - the value for the `RPVideoSampleOrientationKey` that describes the video orientation.\n\nWe are going to prepare and send our video frames using the `SampleUploader` class. Add a new `private var uploader: SampleUploader?` to the SampleHandler class and update `init()` to initialize it:\n\n```swift\noverride init() {\n    super.init()\n    if let connection = SocketConnection(filePath: socketFilePath) {\n      clientConnection = connection\n      uploader = SampleUploader(connection: connection)\n    }\n}\n```\n\nNext, we are going to update the `processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType)` method to send our video frames. For performance reasons, we'll also implement a very simple mechanism for adjusting the frame rate by using every third frame. Add a new `private var frameCount = 0` and update the above-mentioned method like this:\n\n```swift\noverride func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {\n    switch sampleBufferType {\n    case .video:\n        // very simple mechanism for adjusting frame rate by using every third frame\n        frameCount += 1\n        if frameCount % 3 == 0 {\n            uploader?.send(sample: sampleBuffer)\n        }\n    default:\n        break\n    }\n}\n```\n\nAlso, update `broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?)` to reset the `frameCount` every time screen sharing is started:\n\n```swift\noverride func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {\n    frameCount = 0\n    \n    DarwinNotificationCenter.shared.postNotification(.broadcastStarted)\n    openConnection()\n}\n```\n\nWith this, we've concluded sending the video frames and we can move to the last step, handling stop screen sharing.\n\n### Handling stop screen sharing\n\nBesides the in-meeting UI (screenshot 2), ReplayKit integration with iOS provides support for stopping screen recording outside of the app's control, from the status bar (screenshot 3) or using the Control Center (screenshot 4).\n\n![ios-screensharing](../assets/iOS_screensharing_2.png \"screenshot 2\") ![ios-screensharing](../assets/iOS_screensharing_3.png \"screenshot 3\") ![ios-screensharing](../assets/iOS_screensharing_4.png \"screenshot 4\") \n\nAny of these actions will trigger `broadcastFinished` in our `SampleHandler` implementation. This is our entry point for closing the connection and cleaning up. We will update `broadcastFinished` to post a `DarwinNotification.broadcastStopped` system-wide notification and close the connection:\n\n```swift\noverride func broadcastFinished() {\n    DarwinNotificationCenter.shared.postNotification(.broadcastStopped)\n    clientConnection?.close()\n}\n```\n\nAnother scenario we need to take care of is when the server connection is dropped, like when leaving a meeting while screen sharing or an error is encountered. We will address this by handling `clientConnection.didClose` event. Add a new method `setupConnection` to the `SampleHandler` class and update `init` to call it:\n\n```swift\nfunc setupConnection() {\n    clientConnection?.didClose = { [weak self] error in      \n        if let error = error {\n            self?.finishBroadcastWithError(error)\n        } else {\n            // the displayed failure message is more user friendly when using NSError instead of Error\n            let JMScreenSharingStopped = 10001\n            let customError = NSError(domain: RPRecordingErrorDomain, code: JMScreenSharingStopped, userInfo: [NSLocalizedDescriptionKey: \"Screen sharing stopped\"])\n            self?.finishBroadcastWithError(customError)\n        }\n    }\n}\n\noverride init() {\n    super.init()\n    if let connection = SocketConnection(filePath: socketFilePath) {\n      clientConnection = connection\n      setupConnection()\n      \n      uploader = SampleUploader(connection: connection)\n    }\n}\n```\n\nNow, that we are done writing the implementation, we just need to enable the functionality in Jitsi. We are doing this by configuring `JitsiMeetConferenceOptionsBuilder` with the `ios.screensharing.enabled feature` flag, like this:\n\n```swift\nlet options = JitsiMeetConferenceOptions.fromBuilder { [weak self] builder in\n    ...\n    builder.setFeatureFlag(\"ios.screensharing.enabled\", withBoolean: true)\n}\nmeetView.join(options)\n```\n\nFinally, we are ready to test the implementation. Before doing so, make sure voip is added to `UIBackgroundModes`, in the app's `Info.playlist`, in order to work when the app is in the background.\n\n### TL;DR\n\n- Add a `Broadcast Upload Extension`, without UI, to your app. Update deployment info to run in iOS 14 or newer.\n- Copy `SampleUploader.swift`, `SocketConnection.swift`, `DarwinNotificationCenter.swift` and `Atomic.swift` files from the sample project to your extension. Make sure they are added to the extension's target.\n- Add both the app and the extension to the same App Group. Next, add the app group id value to the app's `Info.plist` for the `RTCAppGroupIdentifier` key.\n- Add a new key `RTCScreenSharingExtension` to the app's `Info.plist` with the extension's `Bundle Identifier` as the value.\n- Update `SampleHandler.swift` with the code from the sample project. Update `appGroupIdentifier` constant with the App Group name your app and extension are both registered to.\n- Update `JitsiMeetConferenceOptions` to enable screen sharing using the `ios.screensharing.enabled` feature flag.\n- Make sure `voip` is added to `UIBackgroundModes`, in the app's `Info.plist`, in order to work when the app is in the background.\n\n## Debugging\n\n - If you choose `Console` app for debugging, please be sure that you select:\n`Action`(tab) -> `Include Info Messages`\n`Action`(tab) -> `Include Debug Messages`\n\n - In the Search filter please type `JitsiMeetSDK`, press Return key and replace `ANY` with `Category`.\nThis will show you all the real time logs related to our SDK.\n"
  },
  {
    "path": "docs/dev-guide/ljm-api.md",
    "content": "---\nid: dev-guide-ljm-api\ntitle: lib-jitsi-meet API (low level)\n---\n\nYou can use Jitsi Meet API to create Jitsi Meet video conferences with a custom GUI.\n\n## Installation\n\nTo embed Jitsi Meet API in your application you need to source the Jitsi Meet API library.\n**It should be sourced from your deployment.**\n\n```html\n<script src=\"https://meet.jit.si/libs/lib-jitsi-meet.min.js\"></script>\n```\n\nNow you can access Jitsi Meet API through the `JitsiMeetJS` global object.\n\n## Getting Started\n\n1. The first thing you must do in order to use Jitsi Meet API is to initialize `JitsiMeetJS` object:\n\n```javascript\nJitsiMeetJS.init();\n```\n\n2. Then you must create the connection object:\n\n\n```javascript\nvar connection = new JitsiMeetJS.JitsiConnection(null, null, options);\n```\n\n\n3. Now we can attach some listeners to the connection object and establish the server connection:\n\n```javascript\nconnection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_ESTABLISHED, onConnectionSuccess);\nconnection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_FAILED, onConnectionFailed);\nconnection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_DISCONNECTED, disconnect);\n\nconnection.connect();\n```\n\n4. After you receive the `CONNECTION_ESTABLISHED` event you are to create the `JitsiConference` object and\nalso you may want to attach listeners for conference events (we are going to add handlers for remote track, conference joined, etc. ):\n\n\n```javascript\nroom = connection.initJitsiConference(\"conference1\", confOptions);\nroom.on(JitsiMeetJS.events.conference.TRACK_ADDED, onRemoteTrack);\nroom.on(JitsiMeetJS.events.conference.CONFERENCE_JOINED, onConferenceJoined);\n```\n\n5. You also may want to get your local tracks from the camera and microphone:\n```javascript\nJitsiMeetJS.createLocalTracks().then(onLocalTracks);\n```\n\nNOTE: Adding listeners and creating local streams are not mandatory steps.\n\n6. Then you are ready to create / join a conference :\n\n```javascript\nroom.join();\n```\n\nAfter that step you are in the conference. Now you can continue with adding some code that will handle the events and manage the conference.\n\n## API Reference\n\n### setReceiverConstraints\n\nConfigures the video quality for remote participant streams.\n\n```javascript\nconference.setReceiverConstraints(videoConstraints);\n```\n\n**Parameters:**\n\n`videoConstraints` - Object containing the following properties:\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `lastN` | number | Maximum number of video streams to receive |\n| `selectedSources` | array | Source names to receive (e.g., `['abc123-v0', 'def456-v0']`) |\n| `onStageSources` | array | Source names for participants on stage |\n| `defaultConstraints` | object | Default quality for all sources `{ maxHeight: 360 }` |\n| `constraints` | object | Per-source quality `{ 'abc123-v0': { maxHeight: 720 } }` |\n\n**Example:**\n\n```javascript\n// Basic usage - receive up to 20 videos at 360p\nconference.setReceiverConstraints({\n    lastN: 20,\n    defaultConstraints: { maxHeight: 360 }\n});\n```\n\nCommon `maxHeight` values: `180` (low), `360` (medium), `720` (HD), `1080` (Full HD).\n\n:::warning DEPRECATED\n`setReceiverVideoConstraint(resolution)` is deprecated. Use `setReceiverConstraints()` instead.\n:::\n\n### setEffect\n\nApplies effects to local tracks (e.g., background blur, virtual backgrounds).\n\n```javascript\ntrack.setEffect(effect);\n```\n\n**Parameters:**\n\n`effect` - Object with the following methods, or `undefined` to remove effect:\n\n| Method | Description |\n|--------|-------------|\n| `isEnabled(track)` | Returns true if effect can be applied to this track |\n| `startEffect(stream)` | Processes stream and returns modified MediaStream |\n| `stopEffect()` | Cleans up effect resources |\n\n**Example:**\n\n```javascript\nconst blurEffect = {\n    isEnabled: (track) => track.isVideoTrack(),\n    startEffect: (stream) => {\n        // Apply blur and return processed stream\n        return processedStream;\n    },\n    stopEffect: () => {\n        // Release resources\n    }\n};\n\nlocalVideoTrack.setEffect(blurEffect);\n\n// Remove effect\nlocalVideoTrack.setEffect(undefined);\n```\n\n## Components\n\nSee [the full API docs](https://jitsi.github.io/lib-jitsi-meet/).\n\n## Usage\n\n:::note NOTE\nJaaS customers, please follow [this example](https://github.com/jitsi/ljm-jaas-example) or check out the [live demo](https://jitsi.github.io/ljm-jaas-example).\n:::\n"
  },
  {
    "path": "docs/dev-guide/ljm.md",
    "content": "---\nid: dev-guide-ljm\ntitle: Modifying lib-jitsi-meet\n---\n\n# Modifying `lib-jitsi-meet`\n\nBy default the library is referenced as a prebuilt artifact in a GitHub release. Packages are NOT published to npm. The default dependency path in package.json is:\n\n```json\n\"lib-jitsi-meet\": \"https://github.com/jitsi/lib-jitsi-meet/releases/download/v<version>+<commit-hash>/lib-jitsi-meet.tgz)\",\n```\n\nTo work with local copy you may change the path to:\n\n```json\n\"lib-jitsi-meet\": \"file:///Users/name/local-lib-jitsi-meet-packed-copy.tgz\",\n```\n\nIn order to create the packed file run `npm pack` in the lib-jitsi-meet project directory.\n\nTo make the project you must force it to take the sources as 'npm update':\n\n```\nnpm install lib-jitsi-meet --force && make\n```\n\nOr if you are making only changes to the library:\n\n```\nnpm install lib-jitsi-meet --force && make deploy-lib-jitsi-meet\n```\n\nAlternative way is to use [npm link](https://docs.npmjs.com/cli/link).\nIt allows to link `lib-jitsi-meet` dependency to local source in few steps:\n\n```bash\ncd lib-jitsi-meet\n\n#### create global symlink for lib-jitsi-meet package\nnpm link\n\ncd ../jitsi-meet\n\n#### create symlink from the local node_modules folder to the global lib-jitsi-meet symlink\nnpm link lib-jitsi-meet\n```\n\n:::note\nLinking will not work when building the mobile applications.\n:::\n\nAfter changes in your local `lib-jitsi-meet` repository, you can rebuild it with `npm run build` and your `jitsi-meet` repository will use that modified library:\n\n```bash\ncd node_modules/lib-jitsi-meet\nnpm run build\n```\n\nIf you do not want to use local repository anymore you should run:\n\n```bash\ncd jitsi-meet\nnpm unlink lib-jitsi-meet\nnpm install\n```\n"
  },
  {
    "path": "docs/dev-guide/mobile-dropbox.md",
    "content": "---\nid: mobile-dropbox\ntitle: Setting up Dropbox integration\n---\n\n1. Create a Dropbox app.\n2. Add the following to ```ios/app/src/Info.plist``` by replacing `<APP_KEY>`\n   with your own Dropbox app key (which can be found in the\n   [App Console](https://www.dropbox.com/developers/apps)):\n```\n<key>CFBundleURLTypes</key>\n<array>\n  <dict>\n    <key>CFBundleURLName</key>\n    <string></string>\n    <key>CFBundleURLSchemes</key>\n    <array>\n      <string>db-<APP_KEY></string>\n    </array>\n  </dict>\n</array>\n<key>LSApplicationQueriesSchemes</key>\n<array>\n  <string>dbapi-2</string>\n  <string>dbapi-8-emm</string>\n</array>\n```\n\n**NOTE:** Both Android and iOS builds of the apps will parse the Dropbox app key\nfrom ```ios/app/src/Info.plist```.\n\n**NOTE:** See [Dropbox developer guide](https://www.dropbox.com/developers/reference/developer-guide) for more information\n"
  },
  {
    "path": "docs/dev-guide/mobile-feature-flags.md",
    "content": "---\nid: mobile-feature-flags\ntitle: Feature flags\n---\n\nThe mobile SDK supports a number of feature flags which allow for customizing certain\nUI aspects and behavior.\n\nAll flags are defined [here](https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.ts).\n"
  },
  {
    "path": "docs/dev-guide/mobile-google-auth.md",
    "content": "---\nid: mobile-google-auth\ntitle: Setting up Google sign-in integration\n---\n\n- Create a Firebase project here: https://firebase.google.com/. You'll need a\nsigned Android build for that, that can be a debug self-signed build too, just\nretrieve the signing hash. The key hash of an already signed ap can be obtained\nas follows (on macOS): ```keytool -list -printcert -jarfile the-app.apk```\n- Place the generated ```google-services.json``` file in ```android/app```\nfor Android and the ```GoogleService-Info.plist``` into ```ios/app``` for\niOS (you can stop at that step, no need for the driver and the code changes they\nsuggest in the wizard).\n- You may want to exclude these files in YOUR GIT config (do not exclude them in\nthe ```.gitignore``` of the application itself!).\n- Your web client ID is auto generated during the Firebase project\n creation. Find them in the Google Developer console\n (https://console.developers.google.com/)\n- Make sure your config reflects this ID by setting\n```googleApiApplicationClientID``` in config.js.\n- Add your iOS client ID (the REVERSED_CLIENT_ID in the plist file) as an\napplication URL schema into ```ios/app/src/Info.plist```\n(replacing placeholder).\n- Enable YouTube API access on the developer console (see above) to enable live\nstreaming.\n"
  },
  {
    "path": "docs/dev-guide/mobile-jitsi-meet.md",
    "content": "---\nid: dev-guide-mobile-jitsi-meet\ntitle: Developer Guide for Jitsi Meet\nsidebar_label: Jitsi Meet development\n---\n\nThis guide will help you setup a development environment to start working on the Jitsi Meet mobile app itself.\n\n:::caution\nBuilding the apps / SDKs is not supported on Windows.\n:::\n\n## Overview\n\n:::note\nThis guide is about building the Jitsi Meet apps themselves. If you want to integrate the Jitsi Meet SDK into your own application check the dedicated page on the sidebar.\n:::\n\nJitsi Meet can be built as a standalone app for Android or iOS. It uses the\n[React Native] framework.\n\nFirst make sure the following dependencies are installed:\n\n* `watchman`\n* `nodejs`\n* `npm`\n\n:::warning Node version\nNode 20.x and npm 10.x are required. Any other version may result in runtime errors.\n:::\n\n:::note Xcode\nXcode 15 or higher is required.\n:::\n\n## iOS\n\n1. Install dependencies\n\n  - Install main dependencies:\n\n    ```bash\n    npm install\n    ```\n\n  - Install the required pods (CocoaPods must be installed first, it can\n    be done with Homebrew: `brew install cocoapods`)\n\n    ```bash\n    cd ios\n    pod install\n    cd ..\n    ```\n\n2. Build the app using Xcode\n\n    - Open `ios/jitsi-meet.xcworkspace` in Xcode. Make sure it's the workspace\n      file!\n\n    - Select your device from the top bar and hit the **Play ▶️** button.\n\n    When the app is launched from Xcode, the Debug Console will show the application output\n    logs.\n\n3. Other remarks\n\n    It's likely you'll need to change the bundle ID for deploying to a device.\n    This can be changed in the **General** tab. Under **Identity** set\n    **Bundle Identifier** to a different value, and adjust the **Team** in the\n    **Signing** section to match your own.\n\n\n## Android\n\n### Building and running with Android Studio\n\n1. Install [Android Studio].\n\n2. Set the JDK to at least Java 11: **Android Studio → Settings → Build, Execution, Deployment → Build Tools → Gradle → Gradle JDK**.\n\n   See also: https://developer.android.com/studio/intro/studio-config#jdk\n\n3. Install JavaScript dependencies from the **repository root**:\n\n   ```bash\n   npm install\n   ```\n\n4. Open the project in Android Studio:\n\n   - Choose **Open** and select the `android/` folder inside the repository.\n   - Wait for the Gradle sync to complete. Android Studio will download all required SDK components automatically on the first run.\n\n   :::tip\n   If Gradle sync fails, go to **File → Invalidate Caches / Restart** and try again. Make sure your JDK is set correctly (step 2).\n   :::\n\n5. Select your target device from the device drop-down in the top toolbar (a connected physical device or a configured AVD emulator).\n\n6. Click the **Run ▶️** button (or press `Shift + F10`). Android Studio will build the APK and deploy it to the selected device.\n\n7. Start the Metro bundler from the **repository root**:\n\n   ```bash\n   npm start\n   ```\n\n   The app will automatically connect to Metro once it launches on the device.\n\n   :::note\n   If the app cannot find the Metro bundler (e.g., on a physical device), run:\n   ```bash\n   adb reverse tcp:8081 tcp:8081\n   ```\n   :::\n\n### Building and running without Android Studio\n\n:::note\nThis section describes a fully command-line based workflow that works on **Linux**, **macOS**, and **WSL** (Windows Subsystem for Linux). You do not need Android Studio installed.\n:::\n\n#### 1. Download the Android SDK command-line tools\n\nDownload the **Command-line tools only** package (not Android Studio) from the [official Android developer site](https://developer.android.com/studio#command-tools).\n\nAfter extracting the archive, the expected directory structure should look like this:\n\n```\n~/Android/Sdk/\n└── cmdline-tools/\n    └── latest/          ← rename the extracted folder to \"latest\" if it isn't already\n        ├── bin/\n        ├── lib/\n        ├── NOTICE.txt\n        └── source.properties\n```\n\n:::tip\nIf the extracted folder has a name like `cmdline-tools-<version>`, rename it to `latest` so that `sdkmanager` and other tools resolve paths correctly:\n```bash\nmv ~/Android/Sdk/cmdline-tools/cmdline-tools-<version> ~/Android/Sdk/cmdline-tools/latest\n```\n:::\n\n#### 2. Configure environment variables\n\nAdd the following lines to your `~/.bashrc` or `~/.zshrc`, then reload the shell:\n\n```bash\nexport ANDROID_HOME=\"$HOME/Android/Sdk\"\nexport PATH=\"$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATH\"\n```\n\n```bash\nsource ~/.bashrc   # or source ~/.zshrc\n```\n\n#### 3. Install required SDK packages and accept licenses\n\nUse `sdkmanager` to install the necessary SDK components:\n\n```bash\nsdkmanager \"platform-tools\" \"platforms;android-34\" \"build-tools;34.0.0\"\nsdkmanager --licenses\n```\n\nAccept all prompts by pressing `y` when asked.\n\n#### 4. Install JavaScript dependencies\n\nFrom the **repository root**, run:\n\n```bash\nnpm install\n```\n\n#### 5. Build and install on a physical device\n\n1. Enable **USB debugging** on your Android device (**Settings → Developer options → USB debugging**).\n2. Connect the device via USB and verify it is detected:\n\n   ```bash\n   adb devices\n   ```\n\n   You should see your device listed with the status `device`. If it shows `unauthorized`, unlock your phone and accept the RSA key fingerprint prompt.\n\n3. Navigate to the `android/` directory and run:\n\n   ```bash\n   cd android\n   ./gradlew installDebug\n   ```\n\n   This will build the debug APK, install it on the connected device, and automatically start the Metro bundler. You do not need to run `npm start` separately.\n\n   :::note\n   The first build may take several minutes as Gradle downloads its own dependencies. Subsequent builds are faster due to caching.\n   :::\n\n### Adding extra dependencies\n\nDue to how our project is structured, React Native's automatic linking won't work so Android dependencies need to be manually linked.\n\nFirst, add your project to `android/settings.gradle` like so:\n\n```gradle title=\"android/settings.gradle\"\ninclude ':react-native-mydependency'\nproject(':react-native-mydependency').projectDir = new File(rootProject.projectDir, '../node_modules/@somenamespace/react-native-mydependency/android')\n```\n\nThen add a dependency on `android/sdk/build.gradle` like so:\n\n```gradle title=\"android/sdk/build.gradle\"\nimplementation project(':react-native-mydependency')\n```\n\nLast, link it in the `getReactNativePackages` method in `android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java` like so:\n\n```java title=\"android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java\"\nnew com.companyname.library.AwesomeLibraryPackage(),\n```\n\nMake sure you adjust the fully qualified package name.\n\n## Debugging\n\nThe official documentation on [debugging] is quite extensive and specifies the\npreferred method for debugging.\n\n:::note\nWhen using Chrome Developer Tools for debugging the JavaScript source\ncode is being interpreted by Chrome's V8 engine, instead of JSCore which React\nNative uses. It's important to keep this in mind due to potential differences in\nsupported JavaScript features. Also note Jitsi Meet does not support Flipper.\n:::\n\n## Enabling extra features\n\n- [Dropbox Integration](mobile-dropbox.md)\n- [Google Sign-In Integration (For YouTube Live Streaming)](mobile-google-auth.md)\n\n[Android Studio]: https://developer.android.com/studio/index.html\n[debugging]: https://facebook.github.io/react-native/docs/debugging/\n[React Native]: https://facebook.github.io/react-native/\n"
  },
  {
    "path": "docs/dev-guide/react-native-sdk.md",
    "content": "---\nid: dev-guide-react-native-sdk\ntitle: React Native SDK\n---\n\nThe Jitsi React Native SDK provides the same user experience as the Jitsi Meet app,\nin a customizable way which you can embed in your React Native apps.\n\n## Sample application using the React Native SDK\n\nIf you want to see how easy integrating the Jitsi React Native SDK into a React Native application is, take a look at the<br/>\n[sample applications repository](https://github.com/jitsi/jitsi-meet-sdk-samples#react-native).\n\n## Usage\n\nWhile this is a published library, you can `npm i @jitsi/react-native-sdk`.<br/>\nDependency conflicts may occur between RNSDK and your app. <br/>If that is the case, please run `npm i @jitsi/react-native-sdk --force`.<br/>\nTo check if some dependencies need to be added, please run the following script `node node_modules/@jitsi/react-native-sdk/update_dependencies.js`.<br/>\nThis will sync all of our peer dependencies with your dependencies. <br/>\nNext you will need to do `npm install`.\n\nBecause our SDK uses SVG files, you will need to update your metro bundler configuration accordingly:\n\n```config title=\"metro.config\"\nconst { getDefaultConfig } = require('metro-config');\n\nmodule.exports = (async () => {\n  const {\n    resolver: {\n      sourceExts,\n      assetExts\n    }\n  } = await getDefaultConfig();\n\n  return {\n    transformer: {\n      babelTransformerPath: require.resolve('react-native-svg-transformer'),\n      getTransformOptions: async () => ({\n        transform: {\n          experimentalImportSupport: false,\n          inlineRequires: true,\n        },\n      }),\n    },\n    resolver: {\n      assetExts: assetExts.filter(ext => ext !== 'svg'),\n      sourceExts: [...sourceExts, 'svg']\n    }\n  }\n})();\n```\n\n\n### Android\n\n#### Permissions\n- In `android/app/src/debug/AndroidManifest.xml` and `android/app/src/main/AndroidManifest.xml`, above the `</application>` tag, please include\n  ```xml\n   <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n   <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n   <uses-permission android:name=\"android.permission.BLUETOOTH\" />\n   <uses-permission android:name=\"android.permission.CAMERA\" />\n   <uses-permission android:name=\"android.permission.INTERNET\" />\n   <uses-permission android:name=\"android.permission.MANAGE_OWN_CALLS\" />\n   <uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\" />\n   <uses-permission android:name=\"android.permission.POST_NOTIFICATIONS\" />\n   <uses-permission android:name=\"android.permission.RECORD_AUDIO\" />\n   <uses-permission android:name=\"android.permission.WAKE_LOCK\" />\n  ```\n- Starting Android 14, specific foreground service types permissions require to be added in the manifest file: \n  ```xml\n   <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE\" />\n   <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK\" />\n   <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION\" />\n  ```\n\n#### Services\n- To enables the screen share feature you now need to go to your `MainApplication.java` file and:\n  1. `import com.oney.WebRTCModule.WebRTCModuleOptions;` that comes from `react-native-webrtc` dependency.\n  \n  2. `WebRTCModuleOptions options = WebRTCModuleOptions.getInstance();` instance it.\n  3. `options.enableMediaProjectionService = true;` enable foreground service that takes care of screen-sharing feature.\n\n#### API\n- Our app use `react-native-orientation-locker` dependency that uses API 33 features. Make sure that your app, in `android\\build.gradle`, targets, at least, that version:\n  ```markdown\n    buildscript {\n        ext {\n            compileSdkVersion = 33\n            targetSdkVersion = 33\n        }\n    }\n  ```\n\n### iOS \n\n#### Permissions\n- React Native SDK requests camera and microphone access, make sure to include the required entries for `NSCameraUsageDescription` and `NSMicrophoneUsageDescription`in your `Info.plist` file.\n- React Native SDK shows and hides the status bar based on the conference state,\n  you may want to set `UIViewControllerBasedStatusBarAppearance` to `NO` in your\n  `Info.plist` file.\n- For starting screen sharing React Native SDK provides the UI to present the `RPSystemBroadcastPickerView` to the user. By default, the picker will display a list of all the available broadcast providers. In order to limit the picker to our particular broadcast provider, we have to set `preferredExtension` to the bundle identifier of the broadcast extension. We are doing this by adding a new key named `RTCScreenSharingExtension` to the app's Info.plist and setting the broadcast extension bundle identifier as the value.\n- Make sure `voip` is added to `UIBackgroundModes`, in the app's `Info.plist`, in order to work when the app is in the background.\n \n\n#### Build Phases\n\n##### Run Script Phases\n- For the sounds to work, please add the following script in Xcode:\n  ```shell\n    SOUNDS_DIR=\"${PROJECT_DIR}/../node_modules/@jitsi/react-native-sdk/sounds\"\n    cp $SOUNDS_DIR/* ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/\n  ```\n\n \n\n## JitsiMeeting props\n\nOur JitsiMeeting component renders the full meeting experience. This has some customizable properties:\n\n\n### config\n`Object` - Overwrite different [config](https://github.com/jitsi/jitsi-meet/blob/master/config.js) options.\n- For example:\n```javascript\n<JitsiMeeting\n    config = {{\n        hideConferenceTimer: true,\n        subject: \"React Native SDK\",\n        customToolbarButtons: [\n            {\n                icon: \"https://w7.pngwing.com/pngs/987/537/png-transparent-download-downloading-save-basic-user-interface-icon-thumbnail.png\",\n                id: \"btn1\",\n                text: \"Button one\"\n            }, {\n                icon: \"https://w7.pngwing.com/pngs/987/537/png-transparent-download-downloading-save-basic-user-interface-icon-thumbnail.png\",\n                id: \"btn2\",\n                text: \"Button two\"\n            }\n        ]\n    }} />\n```\n\n\n### flags\n`Object` - Add different feature [flags](https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.ts)\nthat your meeting experience would like to have. \n- For example: \n```javascript\n<JitsiMeeting \n    flags={{\n    'call-integration.enabled': true, \n    'fullscreen.enabled': false, \n    'invite.enabled': true }} />\n```\n\n\n### eventListeners\n`Object` - Options that personalize your meeting experience:\n\n - onConferenceBlurred\n`Function` - Takes a function that gets triggered when ```CONFERENCE_BLURRED``` action is dispatched, more exactly when a conference screen is out of focus, more exactly when navigation to another screen is initiated. \n\n - onConferenceFocused\n`Function` - Takes a function that gets triggered when ```CONFERENCE_FOCUSED``` action is dispatched, more exactly when a conference screen is focused.\n\n - onAudioMutedChanged\n`Function` - Takes a function that gets triggered when ```SET_AUDIO_MUTED``` action is dispatched, more exactly when audio mute state is changed.\n\n - onConferenceJoined\n`Function` - Takes a function that gets triggered when ```CONFERENCE_JOINED``` action is dispatched, more exactly when a conference was joined.\n\n - onConferenceLeft\n   `Function` - Takes a function that gets triggered when ```CONFERENCE_LEFT``` action is dispatched, more exactly when a conference was left.\n\n - onConferenceWillJoin\n`Function` - Takes a function that gets triggered when ```CONFERENCE_WILL_JOIN``` action is dispatched, more exactly when a conference will be joined.\n\n - onEnterPictureInPicture\n   `Function` - Takes a function that gets triggered when ```ENTER_PICTURE_IN_PICTURE``` action is dispatched, more exactly when entering picture-in-picture is initiated.\n\n - onParticipantJoined\n`Function` - Takes a function that gets triggered when ```PARTICIPANT_JOINED``` action is dispatched, more exactly when a specific participant joined a conference.\n\n - onReadyToClose\n   `Function` - Takes a function that gets triggered when ```READY_TO_CLOSE``` action is dispatched, more exactly when one exits a conference.\n\n- onVideoMutedChanged\n  `Function` - Takes a function that gets triggered when ```SET_VIDEO_MUTED``` action is dispatched, more exactly when video mute state is changed.\n\n\n### room\n`string` - Name of the room where the conference takes place.\n\n\n### serverURL\n`string` - Server where the conference should take place.\n\n\n### style\n`Object` - CSS your meeting experience.\n\n\n### token\n`string` - JWT token used for authentication.\n\n\n### userInfo\n\n- avatarUrl\n`string` - Path to participant's avatar.\n\n- displayName\n`string` - Default participant name to be displayed.\n\n- email\n`string` - Default email for participant.\n"
  },
  {
    "path": "docs/dev-guide/react-sdk.md",
    "content": "---\nid: dev-guide-react-sdk\ntitle: React SDK\n---\n\nThe Jitsi Meet React SDK provides the same user experience as the Jitsi Meet app, in a customizable way which you can embed in your apps.\n\n:::important\nReact 16 or higher is required.\n:::\n\n## Sample application using the SDK\nIf you want to see how easy integrating the Jitsi Meet React SDK into a React application is, take a look at our [example](https://github.com/jitsi/jitsi-meet-react-sdk/tree/main/example).\n\n## Installation\nTo access the React SDK modules in your application you need to install it as a dependency:\n```bash\nnpm install @jitsi/react-sdk\n```\n\n## Modules\nThe SDK exposes two components with similar properties, intended for different use-cases.\n\n### JitsiMeeting\nTo be used with custom domains as-it-is in React projects:\n```jsx\n<JitsiMeeting\n    domain = { YOUR_DOMAIN }\n    roomName = \"PleaseUseAGoodRoomName\"\n    configOverwrite = {{\n        startWithAudioMuted: true,\n        disableModeratorIndicator: true,\n        startScreenSharing: true,\n        enableEmailInStats: false\n    }}\n    interfaceConfigOverwrite = {{\n        DISABLE_JOIN_LEAVE_NOTIFICATIONS: true\n    }}\n    userInfo = {{\n        displayName: 'YOUR_USERNAME'\n    }}\n    onApiReady = { (externalApi) => {\n        // here you can attach custom event listeners to the Jitsi Meet External API\n        // you can also store it locally to execute commands\n    } }\n    getIFrameRef = { (iframeRef) => { iframeRef.style.height = '400px'; } }\n/>\n```\n#### Properties specific to the `JitsiMeeting` component\n* `domain`: Optional. Field used to retrieve the external_api.js file that initializes the IFrame. If omitted, defaults to `meet.jit.si`.\n\n### JaaSMeeting\nTo be used with the `8x8.vc` domain as-it-is in React projects:\n```jsx\n<JaaSMeeting\n    appId = { YOUR_APP_ID }\n    roomName = \"PleaseUseAGoodRoomName\"\n    jwt = { YOUR_VALID_JWT }\n    configOverwrite = {{\n        disableLocalVideoFlip: true,\n        backgroundAlpha: 0.5\n    }}\n    interfaceConfigOverwrite = {{\n        VIDEO_LAYOUT_FIT: 'nocrop',\n        MOBILE_APP_PROMO: false,\n        TILE_VIEW_MAX_COLUMNS: 4\n    }}\n    spinner = { SpinnerView }\n    onApiReady = { (externalApi) => { ... } }\n/>\n```\n...or with the `stage.8x8.vc` domain:\n```js\n<JaaSMeeting\n    appId = { YOUR_APP_ID }\n    roomName = \"PleaseUseAGoodRoomName\"\n    ...\n    useStaging = { true }\n/>\n```\n#### Properties specific to the `JaaSMeeting` component\n* `appId`: Required. Provides an isolated context and prefixes the room name.\n* `useStaging`: Optional. Tells whether to use the staging environment or not.\n\n## Common properties\nThe component modules support a similar kind of customization to the Jitsi Meet IFrame. The following properties can be passed down to your instances of `JitsiMeeting` or `JaaSMeeting`.\n\n* `roomName`: Required. The name of the room to join.\n\n* `configOverwrite`: Optional. The JS object with overrides for options defined in the [config.js] file.\n\n* `interfaceConfigOverwrite`: Optional. The JS object with overrides for options defined in the [interface_config.js] file.\n\n* `jwt`: Optional. The [JWT](https://jwt.io/) token.\n\n* `invitees`: Optional. Object arrays that contain information about participants invited to a call.\n\n* `devices`: Optional. Information map about the devices used in a call.\n\n* `userInfo`: Optional. The JS object that contains information about the participant starting or joining the meeting (e.g., email).\n\n* `release`: Optional. Information regarding the `stage.8x8.vc` or `8x8.vc` release version. Expects the following format: `release-1234`.\n\n* `spinner`: Optional. The custom spinner to be displayed while the IFrame is loading.\n\n* `onApiReady`: Optional. The external API reference for events and commands.\n\n* `onReadyToClose`: Optional. The callback for when the meeting is ready to be closed.\n\n* `getIFrameRef`: Optional. The parent node used by the IFrame.\n\n[config.js]: https://github.com/jitsi/jitsi-meet/blob/master/config.js\n[interface_config.js]: https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js\n"
  },
  {
    "path": "docs/dev-guide/web-integrations.md",
    "content": "---\nid: dev-guide-web-integrations\ntitle: Web integrations\nsidebar_label: Integrations\n---\n\n## Creating the Google API client for Google Calendar and YouTube integration\n\n1. Log into a Google admin account.\n1. Go to Google cloud platform dashboard. https://console.cloud.google.com/apis/dashboard\n1. In the Select a Project dropdown, click New Project.\n1. Give the project a name.\n1. Proceed to the Credentials settings of the new project.\n1. In the Credentials tab of the Credentials settings, click Create Credentials and select the type OAuth client ID.\n1. Proceed with creating a Web application and add the domains (origins) on which the application will be hosted. Local development environments (http://localhost:8000 for example) can be added here.\n1. While still in the Google cloud platform dashboard, click the Library settings for the calendar project.\n1. Search for the Google Calendar API (used for calendar accessing), click its result, and enable it.\n1. Do the same for YouTube Data API v3\n\n## Creating the Microsoft app for Microsoft Outlook integration\n\n1. Go to https://apps.dev.microsoft.com/\n1. Proceed through the \"Add an app\" flow. Once created, a page with several Graph Permissions fields should display.\n1. Under \"Platforms\" add \"Web\"\n1. Add a redirect URL for the Microsoft auth flow to visit once a user has confirmed authentication. Target domain if available is just 'yourdomain.com' (the deployment address) and the redirect URL is `https://yourdomain.com/static/msredirect.html`.\n1. Add Microsoft Graph delegated permissions, if this option is available: Calendars.Read, Calendars.ReadWrite, Calendars.Read.Shared, Calendars.ReadWrite.Shared.\n1. Check `Allow Implicit Flow` (and `Restrict token issuing to this app` if available).\n1. Save the changes.\n\n## Creating the Dropbox app for Dropbox recording integration\n\n1. You need a Dropbox account (If you don't already have one, you can sign up for a free account [here](https://www.dropbox.com/register).)\n2. Create new App as described in [Getting Started Guide](https://www.dropbox.com/developers/reference/getting-started?_tk=guides_lp&_ad=guides2&_camp=get_started#app%20console) in App Console section.\n3. Choose\n    1. 'Dropbox API - For apps that need to access files in Dropbox.' \n    1. 'App folder– Access to a single folder created specifically for your app.'\n    1. Fill in the name of your app\n4. You need only, the newly created App key, goes in `/etc/jitsi/meet/yourdeployment.com-config.js` in \n    ``` title=\"/etc/jitsi/meet/yourdeployment.com-config.js\"\n        dropbox: {\n            appKey: '__dropbox_app_key__',\n            redirectURI: 'https://yourdeployment.com/static/oauth.html'\n        }\n    ```\n5. Add your Dropbox Redirect URIs in the Dropbox form `https://yourdeployment.com/static/oauth.html`\n6. Fill in Branding\n"
  },
  {
    "path": "docs/dev-guide/web-jitsi-meet.md",
    "content": "---\nid: dev-guide-web-jitsi-meet\ntitle: Developer Guide for Jitsi Meet\nsidebar_label: Jitsi Meet development\n---\n\nThis guide will help you setup a development environment to start working on the Jitsi Meet web app itself.\n\n## Building the sources\n\n:::note\nNode.js >= 22 and npm >= 10 are required.\n:::\n\n:::caution\nWindows is not supported natively. If you are on Windows, see\n[Running Jitsi Meet on Windows](dev-guide-windows) for workarounds using Linux-based environments.\n:::\n\nMake sure you have Node.js installed. If you don't, follow [these instructions](https://nodejs.org/en/download/).\n\nThen go ahead:\n```bash\n# Clone the repository\ngit clone https://github.com/jitsi/jitsi-meet\ncd ./jitsi-meet\n\nnpm install\n\n# To build the Jitsi Meet production application:\nmake\n\n# For development:\nmake dev\n```\n\n:::warning\n**DO NOT** run `npm update` or use `yarn` or delete `package-lock.json`. Dependencies are pinned for a reason.\n:::\n\n### Running with webpack-dev-server for development\n\nUse the following command in your terminal:\n\n```bash\nmake dev\n```\n\nBy default the backend deployment used is `alpha.jitsi.net`. You can point the Jitsi Meet app at a different backend by using a proxy server. To do this, set the WEBPACK_DEV_SERVER_PROXY_TARGET variable:\n\n```bash\nexport WEBPACK_DEV_SERVER_PROXY_TARGET=https://your-example-server.com\nmake dev\n```\n\nThe app should be running at https://localhost:8080/\n\n#### Certificate Error\n\nBrowsers may show a certificate error since the development certificate is self-signed. It's safe to disregard those\nwarning and continue to your site.\n\n### Building .debs\n\nTo make a deb you can easily deploy to a public test server, ensure you have the lib-jitsi-meet sources you wish, then:\n```\nnpm install\nmake\ndpkg-buildpackage -A -rfakeroot -us -uc -tc\n```\n\nYou'll have a bunch of .deb files in the parent directory, and can push the updated source to your server and install it with the jitsi-meet-web deb file.\n\n### Running from source on existing deployment\n\nFollow the document https://community.jitsi.org/t/how-to-how-to-build-jitsi-meet-from-source-a-developers-guide/75422\n"
  },
  {
    "path": "docs/dev-guide/windows.md",
    "content": "---\nid: dev-guide-windows\ntitle: Running Jitsi Meet on Windows\nsidebar_label: Windows\n---\n\n:::caution\nWindows is **not** natively supported for Jitsi Meet development or deployment. The methods described below rely on running a Linux environment on your Windows machine.\n:::\n\nThis guide describes ways to build and run [Jitsi Meet](dev-guide-web-jitsi-meet) on a Windows machine. All approaches work by providing a Linux environment where the standard Linux-based instructions apply.\n\n## Prerequisites\n\n- Windows 10 (version 2004 or later) or Windows 11\n- Hardware virtualization enabled in BIOS/UEFI\n- At least 8 GB of RAM (16 GB recommended)\n- A stable internet connection for cloning repositories and installing dependencies\n\n## Method 1: WSL2 (Recommended)\n\n[Windows Subsystem for Linux 2 (WSL2)](https://learn.microsoft.com/en-us/windows/wsl/) runs a real Linux kernel on Windows and provides the closest experience to native Linux development. It has near-native performance, low resource overhead, and integrates well with Windows-based editors such as VS Code.\n\n### Install WSL2\n\nOpen **PowerShell as Administrator** and run:\n\n```bash\nwsl --install\n```\n\nThis installs WSL2 with Ubuntu as the default distribution. Restart your computer when prompted.\n\nAfter restarting, open **Ubuntu** from the Start menu. On first launch you will be asked to create a Linux username and password. Complete the setup, then update the system packages:\n\n```bash\nsudo apt update && sudo apt upgrade -y\n```\n\n### Install nvm\n\n[nvm](https://github.com/nvm-sh/nvm) (Node Version Manager) lets you install and switch between multiple versions of Node.js. The Jitsi Meet repository includes an `.nvmrc` file that specifies the exact Node.js version the project requires. When you run `nvm install` or `nvm use` inside the project directory, nvm reads this file and automatically installs or switches to the correct version. This means you do not need to look up which version to install and the setup stays correct even when the project updates its Node.js requirement.\n\nInstall nvm by running:\n\n```bash\n# Install nvm\ncurl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n\n# Reload your shell profile so the nvm command becomes available\nsource ~/.bashrc\n```\n\n### Install build tools\n\nSome dependencies require native compilation:\n\n```bash\nsudo apt install -y build-essential git\n```\n\n### Clone and build Jitsi Meet\n\n:::tip\nAlways clone the repository inside the **Linux filesystem** (your home directory `~/`) rather than under `/mnt/c/`. The `/mnt/c/` path mounts your Windows `C:\\` drive and cross-filesystem operations are significantly slower, which causes long build times and unreliable file watching.\n:::\n\n```bash\n# Navigate to your Linux home directory\ncd\n\n# Clone the Jitsi Meet repository\ngit clone https://github.com/jitsi/jitsi-meet\ncd jitsi-meet\n\n# Install the Node.js version specified in the .nvmrc file\nnvm install\n\n# Switch to that version (nvm install usually does this automatically,\n# but running nvm use ensures the correct version is active)\nnvm use\n\n# Install dependencies\nnpm install\n\n# Start the development server\nmake dev\n```\n\nThe development server will start at `https://localhost:8080/`. Open this URL in your Windows browser (Chrome, Edge, etc.). WSL2 automatically forwards `localhost` to Windows. You can also use `http://localhost:8080/` which avoids the certificate warning below.\n\n:::note\nIf you access the development server over `https://`, your browser will show a certificate error because the server uses a self-signed certificate. It is safe to accept the warning and proceed. Alternatively, use `http://localhost:8080/` to avoid the warning entirely.\n:::\n\nFor the full list of build commands and configuration options, see the [Jitsi Meet web development guide](dev-guide-web-jitsi-meet).\n\n## Method 2: Docker Desktop\n\nIf your goal is to **self-host** a Jitsi Meet instance rather than develop the web application, Docker Desktop is a convenient option. It runs Linux containers on Windows using the WSL2 backend.\n\n### Install Docker Desktop\n\n1. Download [Docker Desktop for Windows](https://docs.docker.com/desktop/install/windows-install/) and run the installer.\n2. During installation, ensure **\"Use WSL 2 instead of Hyper-V\"** is selected.\n3. After installation, launch Docker Desktop and wait for the engine to start.\n\nOpen **PowerShell** or **Command Prompt** and verify:\n\n```bash\ndocker --version\ndocker compose version\n```\n\nBoth commands should print version information without errors.\n\n### Download the Jitsi Docker setup\n\nOpen **PowerShell** and download the latest release:\n\n```powershell\n# Download the latest stable release\nInvoke-WebRequest -Uri $(\n    (Invoke-RestMethod -Uri https://api.github.com/repos/jitsi/docker-jitsi-meet/releases/latest).assets |\n    Where-Object { $_.name -like '*.zip' } |\n    Select-Object -First 1 -ExpandProperty browser_download_url\n) -OutFile jitsi-docker.zip\n\n# Extract the archive\nExpand-Archive -Path jitsi-docker.zip -DestinationPath jitsi-docker\ncd jitsi-docker\\*\n```\n\n### Configure and start the containers\n\n```bash\n# Copy the example environment file\ncopy env.example .env\n\n# Generate strong passwords (run this from a WSL2 or Git Bash terminal)\nbash ./gen-passwords.sh\n\n# Create required configuration directories\nmkdir -p ~/.jitsi-meet-cfg/{web,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb,jigasi,jibri}\n\n# Start the containers\ndocker compose up -d\n```\n\nAccess the Jitsi Meet instance at [https://localhost:8443](https://localhost:8443).\n\n:::note\nThe Docker-based setup is designed for **deployment and testing**, not for active development of the Jitsi Meet web application source code. For development, use WSL2 (Method 1) or a Linux VM (Method 3).\n:::\n\nFor the full Docker deployment guide including TLS, authentication, and recording configuration, see the [Self-Hosting Guide: Docker](../devops-guide/devops-guide-docker).\n\n## Method 3: Linux Virtual Machine\n\nRunning a full Linux virtual machine provides complete isolation and the broadest compatibility. This approach works exactly like developing on a native Linux machine.\n\n### Install a hypervisor\n\nDownload and install one of the following:\n\n- [VirtualBox](https://www.virtualbox.org/) (free, open source)\n- [VMware Workstation Player](https://www.vmware.com/products/workstation-player.html) (free for personal use)\n\n### Create a virtual machine\n\n1. Download an [Ubuntu LTS](https://ubuntu.com/download/desktop) ISO (22.04 or newer recommended).\n\n2. Create a new virtual machine with the following recommended settings:\n\n   | Setting | Recommended Value |\n   |---------|-------------------|\n   | RAM | 4 GB minimum, 8 GB preferred |\n   | CPU Cores | 2 minimum, 4 preferred |\n   | Disk Space | 30 GB minimum |\n   | Network | NAT or Bridged Adapter |\n\n3. Mount the Ubuntu ISO and complete the installation.\n\n### Install dependencies and build\n\nAfter Ubuntu is installed and running, open a terminal and follow the same steps described in [Method 1: WSL2](#method-1-wsl2-recommended) starting from [Install nvm](#install-nvm). The setup is identical since the VM runs a full Linux environment.\n\nOnce dependencies are installed, follow the [Jitsi Meet web development guide](dev-guide-web-jitsi-meet) to clone and build the project.\n\nThe development server will be available at `https://localhost:8080/` inside the VM's browser.\n\n:::tip\nIf you want to access the development server from your Windows browser, configure the VM's network as **Bridged Adapter** and use the VM's IP address instead of `localhost`. You can find the IP by running `ip addr show` inside the VM.\n:::\n\nFor the full list of build commands and configuration options, see the [Jitsi Meet web development guide](dev-guide-web-jitsi-meet).\n\n## Tips and Caveats\n\n**Filesystem performance (WSL2).** The location of your project files matters significantly. Working inside the Linux filesystem (`~/jitsi-meet`) gives native Linux I/O speed, while working under the mounted Windows filesystem (`/mnt/c/Users/.../jitsi-meet`) adds cross-OS translation overhead and is much slower. Always use the Linux filesystem for builds and file watching.\n\n**Networking.** In WSL2, `localhost` on Windows automatically maps to WSL2, so the development server at `https://localhost:8080/` is accessible from your Windows browser. Docker Desktop containers bind to `localhost` by default and the web UI is at `https://localhost:8443`. For a virtual machine, use a Bridged Adapter network mode if you need to access the VM from Windows.\n\n**Resource usage.** Both WSL2 and VMs consume system memory. By default WSL2 may use up to 50% of your system RAM. You can limit this by creating a `.wslconfig` file at `C:\\Users\\<YourUsername>\\.wslconfig`:\n\n```ini\n[wsl2]\nmemory=4GB\nprocessors=2\n```\n\nAfter saving, restart WSL2 with `wsl --shutdown` in PowerShell.\n\n**Official support.** These workarounds are not officially supported. If you encounter issues, verify that the same problem occurs on a native Linux environment before reporting it upstream. The [Jitsi Community Forum](https://community.jitsi.org/) is a good place to ask for community help with Windows-specific issues.\n"
  },
  {
    "path": "docs/devops-guide/authentication.md",
    "content": "---\nid: authentication\ntitle: Authentication\nsidebar_label: Authentication\n---\n\n\nIt is possible to allow only authenticated users to create new conference rooms.\nWhenever a new room is about to be created, Jitsi Meet will prompt for a user\nname and password. After the room is created, others will be able to join from\nanonymous domain. Here's what has to be configured:\n\n## Keycloak install and configure\nInstall docker and prepare keycloak user. \n```\ncurl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc\nsudo chmod a+r /etc/apt/keyrings/docker.asc\n\nsudo tee /etc/apt/sources.list.d/docker.sources <<EOF\nTypes: deb\nURIs: https://download.docker.com/linux/ubuntu\nSuites: $(. /etc/os-release && echo \"${UBUNTU_CODENAME:-$VERSION_CODENAME}\")\nComponents: stable\nSigned-By: /etc/apt/keyrings/docker.asc\nEOF\napt update\n\napt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\nsystemctl start docker\n\nsudo useradd -r -m -s /bin/bash keycloak\nsudo usermod -aG docker keycloak\nsudo mkdir -p /home/keycloak/keycloak-data \nsudo chown -R keycloak:keycloak /home/keycloak/keycloak-data\nsudo su - keycloak\n```\n\nCreate .env file. Fill in your host and admin password:\n```\nKC_VERSION=26.2.3\nKEYCLOAK_ADMIN_PASSWORD=admin\n```\n\nCreate Dockerfile:\n```\nARG KC_VERSION\n\nFROM quay.io/keycloak/keycloak:${KC_VERSION} AS builder\n\nARG KC_VERSION\nRUN /opt/keycloak/bin/kc.sh build\n\nFROM quay.io/keycloak/keycloak:${KC_VERSION}\nCOPY --from=builder /opt/keycloak/ /opt/keycloak/\n\nENTRYPOINT [\"/opt/keycloak/bin/kc.sh\", \"start\", \"--optimized\"]\n```\n\nAnd docker-compose.yml file:\n```\nservices:\n  keycloak:\n    build:\n      context: .\n      args:\n        - KC_VERSION=${KC_VERSION}\n    container_name: keycloak\n    environment:\n      - KC_HTTP_ENABLED=true\n      - KC_HOSTNAME_STRICT=false\n      - KC_PROXY=edge\n      - KEYCLOAK_ADMIN=admin\n      - KEYCLOAK_ADMIN_PASSWORD=${KEYCLOAK_ADMIN_PASSWORD}\n    volumes:\n      - ./keycloak-data:/opt/keycloak/data\n    ports:\n      - \"18080:8080\"\n    restart: unless-stopped\n```\n\nStart it:\n```\ndocker compose up -d --build\n```\n_Note that if for some reason you cannot use docker you can follow this [guide](https://www.keycloak.org/getting-started/getting-started-zip).\n\nOpen the Keycloak admin console at http://localhost:18080/admin/.\nIf you are running Keycloak on a remote machine you can use ssh port forwarding to access the admin console, for example:\n```\nssh -L18080:localhost:8080 user@remote-machine\n```\n\nYou need to create a realm, name it `jitsi-realm`, and select it.\n\n![create realm](../assets/auth-scr-1.png \"screenshot create realm\")\n\nAdd new client in the realm, name it jitsi. When creating the client make sure you set for \"Valid Redirect URIs\":\n`https://jitsi.example.com/static/sso.html` and for \"Valid post logout redirect URIs\": `https://jitsi.example.com/static/logout.html`.\nAnd for \"Web origins\": `https://jitsi.example.com`. Replace `jitsi.example.com` with your own hostname.\n\n![create client](../assets/auth-scr-2.png \"screenshot create client\")![create client options](../assets/auth-scr-3.png \"screenshot create client options\")\n\n![create client URLs](../assets/auth-scr-4.png \"screenshot create client URLs\")\n\nIn your nginx configuration for jitsi, you need to add the following lines to the server block somewhere before `/xmpp-websocket`:\n```\n   location ~ ^/realms/(.*) {\n        proxy_pass http://localhost:18080; # Or your Keycloak instance address\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n    }\n```\n\nGo to \"Realm Settings\" → \"General tab\" and for \"Frontend URL\" set `https://jitsi.example.com`. Replace `jitsi.example.com` with your own hostname.\n![frontend url](../assets/auth-scr-5.png \"screenshot frontend url\")\n\nIn \"Users\" create a new user. After creating the user, go to \"Credentials\" tab of the user and set a password for the user. You will need it later.\n![create user](../assets/auth-scr-6.png \"screenshot create user\")\n\nMore information about keycloak and running it with docker can be found in the official documentation: https://www.keycloak.org/getting-started/getting-started-docker\n\n## Prosody configuration\n\nIf you have installed Jitsi Meet from the Debian package, these changes should\nbe made in `/etc/prosody/conf.avail/[your-hostname].cfg.lua`\n\nIn the example below, this hostname is assumed to be `jitsi.example.com`. Update\nthis value according to your own hostname.\n\n### Enable authentication\n\nInside the `VirtualHost \"[your-hostname]\"` section, replace anonymous\nauthentication with token authentication:\n\n```\nasap_accepted_issuers = { \"https://jitsi.example.com/realms/jitsi-realm\" }\nasap_accepted_audiences = { \"account\" }\nasap_require_room_claim = false;\n\nVirtualHost \"jitsi.example.com\"\n    authentication = \"token\"\n    app_id = \"jitsi\"\n    allow_empty_token = false\n    cache_keys_url = \"https://jitsi.example.com/realms/jitsi-realm/protocol/openid-connect/certs\"\n```\n\nYou will see your own hostname instead of `jitsi.example.com` in your config\nfile.\n\n### Enable anonymous login for guests\n\nAdd this section **after the previous VirtualHost** to enable the anonymous\nlogin method for guests:\n\n```\nVirtualHost \"guest.jitsi.example.com\"\n    authentication = \"jitsi-anonymous\"\n    c2s_require_encryption = false\n    modules_enabled = {\n        \"smacks\";\n    }\n```\n\n_Note that `guest.jitsi.example.com` is internal to Jitsi, and you do not need\nto (and should not) create a DNS record for it, or generate an SSL/TLS\ncertificate, or do any web server configuration. While it is internal, you\nshould still replace `jitsi.example.com` with your hostname._\n\n### Enable wait for host module\nEnable the `wait_for_host` module under the main muc component and enable `persistent_lobby`\n```\nVirtualHost \"jitsi.example.com\"\n    authentication = \"token\"\n    ...\n    modules_enabled = {\n        ...\n        \"persistent_lobby\";\n        ...\n    }\n\nComponent \"conference.jitsi.example.com\" \"muc\"`\n    modules_enabled = {\n        ...\n        \"muc_wait_for_host\";\n        ...\n    }\n```\n\n## Jitsi Meet configuration\n\nIn config.js, the `anonymousdomain` options has to be set.\n\nIf you have installed jitsi-meet from the Debian package, these changes should\nbe made in `/etc/jitsi/meet/[your-hostname]-config.js`.\n\n```\nvar config = {\n    hosts: {\n        domain: 'jitsi.example.com',\n        anonymousdomain: 'guest.jitsi.example.com',\n        // ...\n    },\n    // ...\n}\n```\n\nYou will see your own hostname instead of `jitsi.example.com` in your config\nfile. You should add only the `anonymousdomain` line. Be careful of commas.\n\nSet the token authentication URLs in the same file at the end append:\n```\nconfig.tokenAuthUrl='https://'+ config.hosts.domain + '/realms/jitsi-realm/protocol/openid-connect/auth?client_id=jitsi&response_type=code&scope=openid&state={state}&redirect_uri=https://'+ config.hosts.domain + '/static/sso.html&code_challenge={code_challenge}&code_challenge_method=S256';\nconfig.tokenLogoutUrl='https://'+ config.hosts.domain + '/realms/jitsi-realm/protocol/openid-connect/logout?post_logout_redirect_uri=https://'+ config.hosts.domain + '/static/logout.html';\nconfig.tokenAuthInline=true;\nconfig.sso={ ssoService: config.hosts.domain, tokenService: config.hosts.domain + '/realms/jitsi-realm/protocol/openid-connect', clientId: \"jitsi\" };\n```\n\n## Restart the services\n\nRestart prosody and nginx as `root`.\n\n```\nsystemctl restart prosody\nsystemctl restart nginx\n```\n"
  },
  {
    "path": "docs/devops-guide/bsd.md",
    "content": "---\nid: devops-guide-bsd\ntitle: Self-Hosting Guide - FreeBSD/NetBSD/OpenBSD\nsidebar_label: BSD\n---\n\nThis document is a reference point for pointing to the upstream packages provided by the FreeBSD, NetBSD and OpenBSD distributions. Jitsi only officially supports Linux, for any problems with the BSD packages you can contact their respective mailing lists.\n\n__Note__: Many of the installation steps require root access.\n\n## FreeBSD\n\nFreeBSD provides ports for [Jitsi](https://www.freshports.org/net-im/jitsi-meet-full) along with documentation on how to configure it and the current limitations - https://wiki.freebsd.org/Jitsi.\n\nJitsi can be installed using the meta port [net-im/jitsi-meet-full](https://www.freshports.org/net-im/jitsi-meet-full) which pulls in Jitsi Videobridge, Jicofo and Jitsi Meet Web UI, along with prosody, the Jitsi prosody plugins, nginx and other required dependencies. Instructions on how to build the port can be read on the FreeBSD Foundation site - https://freebsdfoundation.org/freebsd-project/resourcesold/installing-a-port-on-freebsd/.\n\n## NetBSD\n\nNetBSD provides individual ports for [Jitsi Videobridge](https://pkgsrc.se/chat/jitsi-videobridge), [Jicofo](https://pkgsrc.se/chat/jicofo), [Jitsi prosody plugins](https://pkgsrc.se/chat/jitsi-meet-prosody) and [Jitsi Meet Web UI](https://pkgsrc.se/wip/jitsi-meet). They can be installed using the command `pkg_add <pkg-name>`.\n\n## OpenBSD\n\nOpenBSD provides ports for [Jitsi](https://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/meta/jitsi/), along with a pkg-readme which details how to configure Jitsi for a single host install, located at [/usr/local/share/docs/pkg-readme/jitsi](https://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/meta/jitsi/pkg/).\n\nThe meta port can be installed by the command `pkg_add jitsi`, which pulls in the individual ports, Jitsi Videobridge, Jicofo and Jitsi Meet Web UI, along with prosody, Jitsi prosody plugins and other required dependencies.\n\n## Limitations\n\n- Jigasi and Jibri have not yet been ported to work with any BSD systems.\n"
  },
  {
    "path": "docs/devops-guide/cloud-api.md",
    "content": "---\nid: cloud-api\ntitle: Cloud API\n---\n\nThe Jitsi Meet Cloud API is a specification for services which can support the integration of Jitsi Meet into other applications, for mapping conferences for dial-in support, and for supporting directory search and user invitations to conferences.\n\nThe swagger for these services is provided in [cloud-api.swagger](https://github.com/jitsi/jitsi-meet/blob/master/resources/cloud-api.swagger).\n\n## Sample conference mapper application\n\nhttps://github.com/luki-ev/conferencemapper\n"
  },
  {
    "path": "docs/devops-guide/devops-guide.md",
    "content": "---\nid: devops-guide-start\ntitle: Self-Hosting Guide - Overview\nsidebar_label: Overview\n---\n\n:::note\nThese guides help you to ___host your own Jitsi-Meet server___.   \nIf you want to have a video conference without setting up any infrastructure, use https://meet.jit.si instead.\n:::\n\n## First, a bit of general advice\n\nJitsi Meet being based on [WebRTC](https://en.wikipedia.org/wiki/WebRTC), an encrypted communication link (https) is ___necessary___ to get working multimedia, and the setup is not always trivial.\n\nThe best option is an Internet server with a certificate for a domain registered in the [DNS](https://en.wikipedia.org/wiki/Domain_Name_System#Domain_name_registration).\n\nWhile it's possible to setup a server on a private network and/or a self-signed certificate, it can be less straightforward and you can expect difficulties, first if you want access both from the private network and the public internet, and second when using phones as these clients often don't accept self-signed certificates.\n\nIn case of trouble with clients using phones, [check your certificate](https://whatsmychaincert.com).\n\n<hr />\n\nimport DocCardList from '@theme/DocCardList';\n\n<DocCardList />\n"
  },
  {
    "path": "docs/devops-guide/docker.md",
    "content": "---\nid: devops-guide-docker\ntitle: Self-Hosting Guide - Docker\nsidebar_label: Docker\n---\n\n## Quick start\n\nIn order to quickly run Jitsi Meet on a machine running Docker and Docker Compose,\nfollow these steps:\n\n1. Download and extract the [latest release]. **DO NOT** clone the git repository. See below if you are interested in running test images:\n\n    ```bash\n    wget $(wget -q -O - https://api.github.com/repos/jitsi/docker-jitsi-meet/releases/latest | grep zip | cut -d\\\" -f4)\n    ```\n\n1. Unzip the package, and enter the new folder:\n\n    ```bash\n    unzip <filename>\n    cd <foldername>\n    ```\n\n1. Create a `.env` file by copying and adjusting `env.example`:\n\n   ```bash\n   cp env.example .env\n   ```\n\n1. Set strong passwords in the security section options of `.env` file by running the following bash script\n\n   ```bash\n   ./gen-passwords.sh\n   ```\n\n1. Create required `CONFIG` directories\n   * For linux:\n\n   ```bash\n   mkdir -p ~/.jitsi-meet-cfg/{web,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb,jigasi,jibri}\n   ```\n\n   * For Windows:\n\n   ```bash\n   echo web,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb,jigasi,jibri\n   ```\n   ```bash\n    mkdir \"~/.jitsi-meet-cfg/$_\"\n   ```\n\n1. Run ``docker compose up -d``\n1. Access the web UI at [``https://localhost:8443``](https://localhost:8443) (or a different port, in case you edited the `.env` file).\n\n:::note\nHTTP (not HTTPS) is also available (on port 8000, by default), but that's e.g. for a reverse proxy setup;\ndirect access via HTTP instead HTTPS leads to WebRTC errors such as\n_Failed to access your microphone/camera: Cannot use microphone/camera for an unknown reason. Cannot read property 'getUserMedia' of undefined_\nor _navigator.mediaDevices is undefined_.\n:::\n\n**IMPORTANT**: When deploying Jitsi Meet for real use you must set the `PUBLIC_URL` env variable to the real domain where your setup is running.\n\nIf you want to use jigasi too, first configure your env file with SIP credentials\nand then run Docker Compose as follows:\n\n```bash\ndocker compose -f docker-compose.yml -f jigasi.yml up\n```\n\nIf you want to enable document sharing via [Etherpad],\nconfigure it and run Docker Compose as follows:\n\n```bash\ndocker compose -f docker-compose.yml -f etherpad.yml up\n```\n\nIf you want to enable a virtual collaborative whiteboard via [Excalidraw],\nconfigure it and run Docker Compose as follows:\n\n```bash\ndocker compose -f docker-compose.yml -f whiteboard.yml up\n```\n\n\n\nIf you want to use jibri too, first configure a host as described in Jitsi Broadcasting Infrastructure configuration section\nand then run Docker Compose as follows:\n\n```bash\ndocker compose -f docker-compose.yml -f jibri.yml up -d\n```\n\nor to use jigasi too:\n\n```bash\ndocker compose -f docker-compose.yml -f jigasi.yml -f jibri.yml up -d\n```\n\nTo include a transcriber component, run Docker Compose as follows:\n\n```bash\ndocker compose -f docker-compose.yml -f transcriber.yml up -d\n```\n\nOr for them all together:\n```bash\ndocker compose -f docker-compose.yml -f transcriber.yml -f jigasi.yml -f jibri.yml up -d\n```\n\nFor the log analysis project, you will need both log-analyser.yml and grafana.yml files. This project allows you to analyze docker logs in grafana. If you want to run the log analyzer, run the Docker files as follows:\n\n```bash\ndocker-compose -f docker-compose.yml -f log-analyser.yml -f grafana.yml up -d\n```\nFollow [this](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-log-analyser) document for detailed information on log analysis.\n\n### Updating\n\nIf you want to update, simply run \n\n```bash\nwget $(wget -q -O - https://api.github.com/repos/jitsi/docker-jitsi-meet/releases/latest | grep zip | cut -d\\\" -f4)\n```\n\nagain (just like how you initially downloaded Jitsi). Then unzip and overwrite all when being asked:\n\n```bash\nunzip <filename>\n```\n\n### Testing development / unstable builds\n\nDownload the latest code:\n\n```bash\ngit clone https://github.com/jitsi/docker-jitsi-meet && cd docker-jitsi-meet\n```\n\n:::note\nThe code in `master` is designed to work with the unstable images. Do not run it with release images.\n:::\n\nRun `docker compose up` as usual.\n\nEvery day a new \"unstable\" image build is uploaded.\n\n### Building your own images\n\nDownload the latest code:\n\n```bash\ngit clone https://github.com/jitsi/docker-jitsi-meet && cd docker-jitsi-meet\n```\n\nThe provided `Makefile` provides a comprehensive way of building the whole stack or individual images.\n\nTo build all images:\n\n```bash\nmake\n```\n\nTo build a specific image (the web image for example):\n\n```bash\nmake build_web\n```\n\nOnce your local build is ready make sure to add `JITSI_IMAGE_VERSION=latest` to your `.env` file.\n\n### Security note\n\nThis setup used to have default passwords for internal accounts used across components.\nIn order to make the default setup secure by default these have been removed and the respective containers won't start without having a password set.\n\nStrong passwords may be generated as follows: `./gen-passwords.sh`\nThis will modify your `.env` file (a backup is saved in `.env.bak`) and set strong passwords for each of the\nrequired options. Passwords are generated using `openssl rand -hex 16` .\n\nDO NOT reuse any of the passwords.\n\n## Architecture\n\nA Jitsi Meet installation can be broken down into the following components:\n\n* A web interface\n* An XMPP server\n* A conference focus component\n* A video router (could be more than one)\n* A SIP gateway for audio calls\n* A Broadcasting Infrastructure for recording or streaming a conference.\n\n![](../assets/docker-jitsi-meet.png)\n\nThe diagram shows a typical deployment in a host running Docker. This project\nseparates each of the components above into interlinked containers. To this end,\nseveral container images are provided.\n\n### External Ports\n\nThe following external ports must be opened on a firewall:\n\n* `80/tcp` for Web UI HTTP (really just to redirect, after uncommenting `ENABLE_HTTP_REDIRECT=1` in `.env`)\n* `443/tcp` for Web UI HTTPS\n* `10000/udp` for RTP media over UDP\n\nAlso `20000-20050/udp` for jigasi, in case you choose to deploy that to facilitate SIP access.\n\nE.g. on a CentOS/Fedora server this would be done like this (without SIP access):\n\n```bash\nsudo firewall-cmd --permanent --add-port=80/tcp\nsudo firewall-cmd --permanent --add-port=443/tcp\nsudo firewall-cmd --permanent --add-port=10000/udp\nsudo firewall-cmd --reload\n```\n\nSee [the corresponding section in the debian/ubuntu setup guide](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-quickstart#setup-and-configure-your-firewall).\n\n### Images\n\n* **base**: Debian stable base image with the [S6 Overlay] for process control and the\n  [Jitsi repositories] enabled. All other images are based on this one.\n* **base-java**: Same as the above, plus Java (OpenJDK).\n* **web**: Jitsi Meet web UI, served with nginx.\n* **prosody**: [Prosody], the XMPP server.\n* **jicofo**: [Jicofo], the XMPP focus component.\n* **jvb**: [Jitsi Videobridge], the video router.\n* **jigasi**: [Jigasi], the SIP (audio only) gateway.\n* **jibri**: [Jibri], the broadcasting infrastructure.\n\n### Design considerations\n\nJitsi Meet uses XMPP for signaling, thus the need for the XMPP server.\nThe setup provided by these containers does not expose the XMPP server to the outside world.\nInstead, it's kept completely sealed, and routing of XMPP traffic only happens on a user-defined network.\n\nThe XMPP server can be exposed to the outside world,\nbut that's out of the scope of this project.\n\n## Configuration\n\nThe configuration is performed via environment variables contained in a ``.env`` file.\nYou can copy the provided ``env.example`` file as a reference.\n\nVariable | Description | Example\n--- | --- | ---\n`CONFIG` | Directory where all configuration will be stored | /opt/jitsi-meet-cfg\n`TZ` | System Time Zone | Europe/Amsterdam\n`HTTP_PORT` | Exposed port for HTTP traffic | 8000\n`HTTPS_PORT` | Exposed port for HTTPS traffic | 8443\n`JVB_ADVERTISE_IPS` | IP addresses of the Docker host (comma separated), needed for LAN environments | 192.168.1.1\n`PUBLIC_URL` | Public URL for the web service | https://meet.example.com\n\n:::note\nThe mobile apps won't work with self-signed certificates (the default).\nSee below for instructions on how to obtain a proper certificate with Let's Encrypt.\n:::\n\n### TLS Configuration\n\n#### Let's Encrypt configuration\n\nIf you want to expose your Jitsi Meet instance to the outside traffic directly, but don't own a proper TLS certificate, you are in luck\nbecause Let's Encrypt support is built right in. Here are the required options:\n\nVariable | Description | Example\n--- | --- | ---\n`ENABLE_LETSENCRYPT` | Enable Let's Encrypt certificate generation | 1\n`LETSENCRYPT_DOMAIN` | Domain for which to generate the certificate | meet.example.com\n`LETSENCRYPT_EMAIL` | E-Mail for receiving important account notifications (mandatory) | alice@atlanta.net\n\nIn addition, you will need to set `HTTP_PORT` to 80 and `HTTPS_PORT` to 443 and PUBLIC_URL to your domain.\nYou might also consider to redirect HTTP traffic to HTTPS by setting `ENABLE_HTTP_REDIRECT=1`.\n\n**Let's Encrypt rate limit warning**: Let's Encrypt has a limit to how many times you can submit a request\nfor a new certificate for your domain name. At the time of writing, the current limit is five new (duplicate)\ncertificates for the same domain name every seven days. Because of this, it is recommended that you disable the\nLet's Encrypt environment variables from `.env` if you plan on deleting the `.jitsi-meet-cfg` folder.\nOtherwise, you might want to consider moving the `.jitsi-meet-cfg` folder to a different location so you have a safe place to find\nthe certificate that already Let's Encrypt issued. Or do initial testing with Let's Encrypt disabled, then re-enable\nLet's Encrypt once you are done testing.\n\n:::note\nWhen you move away from `LETSENCRYPT_USE_STAGING`,\nyou will have to manually clear the certificates from `.jitsi-meet-cfg/web`.\n:::\n\nFor more information on Let's Encrypt's rate limits, visit:\nhttps://letsencrypt.org/docs/rate-limits/\n\n#### Using existing TLS certificate and key\n\nIf you own a proper TLS certificate and don't need a Let's Encrypt certificate, you can configure Jitsi Meet container\nto use it.\n\nUnlike Let's Encrypt certificates, this is not configured through the `.env`file, but by telling Jitsi Meet's `web` service\nto mount the following two volumes:\n\n* mount `/path/to/your/cert.key` file to `/config/keys/cert.key` mount point\n* mount `/path/to/your/cert.fullchain` file to the `/config/keys/cert.crt` mount point.\n\nDoing it in `docker-compose.yml` file should look like this:\n\n```yaml\nservices:\n    web:\n        ...\n        volumes:\n            ...\n            - /path/to/your/cert.fullchain:/config/keys/cert.crt\n            - /path/to/your/cert.key:/config/keys/cert.key\n```\n\n### Features configuration (config.js)\n\nVariable | Description | Example\n--- | --- | ---\n`TOOLBAR_BUTTONS` | Configure toolbar buttons. Add the buttons name separated with comma(no spaces between comma) | |\n`HIDE_PREMEETING_BUTTONS` | Hide the buttons at pre-join screen. Add the buttons name separated with comma | |\n`ENABLE_LOBBY` | Control whether the lobby feature should be enabled or not | 1\n`ENABLE_AV_MODERATION` | Control whether the A/V moderation should be enabled or not | 1\n`ENABLE_PREJOIN_PAGE` | Show a prejoin page before entering a conference | 1\n`ENABLE_WELCOME_PAGE` | Enable the welcome page | 1\n`ENABLE_CLOSE_PAGE` | Enable the close page | 0\n`DISABLE_AUDIO_LEVELS` | Disable measuring of audio levels | 0\n`ENABLE_NOISY_MIC_DETECTION` | Enable noisy mic detection | 1\n`ENABLE_BREAKOUT_ROOMS` | Enable breakout rooms | 1\n\n### Jigasi SIP gateway (audio only) configuration\n\nIf you want to enable the SIP gateway, these options are required:\n\nVariable | Description | Example\n--- | --- | ---\n`JIGASI_SIP_URI` | SIP URI for incoming / outgoing calls | test@sip2sip.info\n`JIGASI_SIP_PASSWORD` | Password for the specified SIP account | `<unset>`\n`JIGASI_SIP_SERVER` | SIP server (use the SIP account domain if in doubt) | sip2sip.info\n`JIGASI_SIP_PORT` | SIP server port | 5060\n`JIGASI_SIP_TRANSPORT` | SIP transport | UDP\n\n#### Display Dial-In information\n\nVariable | Description | Example\n--- | --- | ---\n`DIALIN_NUMBERS_URL` | URL to the JSON with all Dial-In numbers | https://meet.example.com/dialin.json\n`CONFCODE_URL` | URL to the API for checking/generating Dial-In codes | https://jitsi-api.jitsi.net/conferenceMapper\n\nThe JSON with the Dial-In numbers should look like this:\n\n```json\n{\"message\":\"Dial-In numbers:\",\"numbers\":{\"DE\": [\"+49-721-0000-0000\"]},\"numbersEnabled\":true}\n```\n\n### Recording / live streaming configuration with Jibri\n\n<details>\n  <summary>If you are using a release older than 7439 some extra setup is necessary.</summary>\nBefore running Jibri **on releases older than 7439**, you need to set up an ALSA loopback device on the host.\nThis **will not** work on a non-Linux host.\n\nFor CentOS 7, the module is already compiled with the kernel, so just run:\n\n```bash\n# configure 5 capture/playback interfaces\necho \"options snd-aloop enable=1,1,1,1,1 index=0,1,2,3,4\" > /etc/modprobe.d/alsa-loopback.conf\n# setup autoload the module\necho \"snd_aloop\" > /etc/modules-load.d/snd_aloop.conf\n# load the module\nmodprobe snd-aloop\n# check that the module is loaded\nlsmod | grep snd_aloop\n```\n\nFor Ubuntu:\n\n```bash\n# install the module\napt update && apt install linux-image-extra-virtual\n# configure 5 capture/playback interfaces\necho \"options snd-aloop enable=1,1,1,1,1 index=0,1,2,3,4\" > /etc/modprobe.d/alsa-loopback.conf\n# setup autoload the module\necho \"snd-aloop\" >> /etc/modules\n# check that the module is loaded\nlsmod | grep snd_aloop\n```\n\n:::note\nIf you are running on AWS you may need to reboot your machine to use the generic kernel instead\nof the \"aws\" kernel. If after reboot, your machine is still using the \"aws\" kernel, you'll need to manually update the grub file. So just run:\n:::\n\n```bash\n# open the grub file in editor\nnano /etc/default/grub\n# Modify the value of GRUB_DEFAULT from \"0\" to \"1>2\"\n# Save and exit from file\n\n# Update grub\nupdate-grub\n# Reboot the machine\nreboot now\n```\n\nFor using multiple Jibri instances, you have to select different loopback interfaces for each instance manually.\n\n  Default the first instance has:\n\n  ```\n  ...\n  slave.pcm \"hw:Loopback,0,0\"\n  ...\n  slave.pcm \"hw:Loopback,0,1\"\n  ...\n  slave.pcm \"hw:Loopback,1,1\"\n  ...\n  slave.pcm \"hw:Loopback,1,0\"\n  ...\n  ```\n\n  To setup the second instance, run container with changed `/home/jibri/.asoundrc`:\n\n  ```\n  ...\n  slave.pcm \"hw:Loopback_1,0,0\"\n  ...\n  slave.pcm \"hw:Loopback_1,0,1\"\n  ...\n  slave.pcm \"hw:Loopback_1,1,1\"\n  ...\n  slave.pcm \"hw:Loopback_1,1,0\"\n  ...\n  ```\n\n  Also you can use numbering id for set loopback interface. The third instance will have `.asoundrc` that looks like:\n\n  ```\n  ...\n  slave.pcm \"hw:2,0,0\"\n  ...\n  slave.pcm \"hw:2,0,1\"\n  ...\n  slave.pcm \"hw:2,1,1\"\n  ...\n  slave.pcm \"hw:2,1,0\"\n  ...\n\n  ```\n\n</details>\n\nIf you want to enable Jibri these options are required:\n\nVariable | Description | Example\n--- | --- | ---\n`ENABLE_RECORDING` | Enable recording / live streaming | 1\n\nExtended Jibri configuration:\n\nVariable | Description | Example\n--- | --- | ---\n`JIBRI_RECORDER_USER` | Internal recorder user for Jibri client connections | recorder\n`JIBRI_RECORDER_PASSWORD` | Internal recorder password for Jibri client connections | `<unset>`\n`JIBRI_RECORDING_DIR` | Directory for recordings inside Jibri container | /config/recordings\n`JIBRI_FINALIZE_RECORDING_SCRIPT_PATH` | The finalizing script. Will run after recording is complete | /config/finalize.sh\n`JIBRI_XMPP_USER` | Internal user for Jibri client connections. | jibri\n`JIBRI_STRIP_DOMAIN_JID` | Prefix domain for strip inside Jibri (please see env.example for details) | muc\n`JIBRI_BREWERY_MUC` | MUC name for the Jibri pool | jibribrewery\n`JIBRI_PENDING_TIMEOUT` | MUC connection timeout | 90\n\n### Jitsi Meet configuration\n\nJitsi-Meet uses two configuration files for changing default settings within\nthe web interface: ``config.js`` and ``interface_config.js``. The files are\nlocated within the ``CONFIG/web/`` directory configured within your environment file.\n\nThese files are re-created on every container restart.\nIf you'd like to provide your own settings, create your own config files:\n``custom-config.js`` and ``custom-interface_config.js``.\n\nIt's enough to provide your relevant settings only, the docker scripts will\nappend your custom files to the default ones!\n\n### Authentication\n\nAuthentication can be controlled with the environment variables below. If guest\naccess is enabled, unauthenticated users will need to wait until a user authenticates\nbefore they can join a room. If guest access is not enabled, every user will need\nto authenticate before they can join.\n\nIf authentication is enabled, once an authenticated user logged in, it is always\nlogged in before the session timeout. You can set `ENABLE_AUTO_LOGIN=0` to disable\nthis default auto login feature or you can set `JICOFO_AUTH_LIFETIME` to limit\nthe session lifetime.\n\nVariable | Description | Example\n--- | --- | ---\n`ENABLE_AUTH` | Enable authentication | 1\n`ENABLE_GUESTS` | Enable guest access | 1\n`AUTH_TYPE` | Select authentication type (internal, jwt or ldap) | internal\n`ENABLE_AUTO_LOGIN` | Enable auto login  | 1\n`JICOFO_AUTH_LIFETIME` | Select session timeout value for an authenticated user | 3 hours\n\n#### Internal authentication\n\nThe default authentication mode (`internal`) uses XMPP credentials to authenticate users.\nTo enable it you have to enable authentication with `ENABLE_AUTH` and set `AUTH_TYPE` to `internal`,\nthen configure the settings you can see below.\n\nInternal users must be created with the ``prosodyctl`` utility in the ``prosody`` container.\nIn order to do that, first, execute a shell in the corresponding container:\n\n```bash\ndocker compose exec prosody /bin/bash\n```\n\nOnce in the container, run the following command to create a user:\n\n```bash\nprosodyctl --config /config/prosody.cfg.lua register TheDesiredUsername meet.jitsi TheDesiredPassword\n```\n\nNote that the command produces no output.\n\nTo delete a user, run the following command in the container:\n\n```bash\nprosodyctl --config /config/prosody.cfg.lua unregister TheDesiredUsername meet.jitsi\n```\n\nTo list all users, run the following command in the container:\n\n```bash\nfind /config/data/meet%2ejitsi/accounts -type f -exec basename {} .dat \\;\n```\n\n#### Authentication using LDAP\n\nYou can use LDAP to authenticate users. To enable it you have to enable authentication with `ENABLE_AUTH` and\nset `AUTH_TYPE` to `ldap`, then configure the settings you can see below.\n\nVariable | Description | Example\n--- | --- | ---\n`LDAP_URL` | URL for ldap connection | ldaps://ldap.domain.com/\n`LDAP_BASE` | LDAP base DN. Can be empty. | DC=example,DC=domain,DC=com\n`LDAP_BINDDN` | LDAP user DN. Do not specify this parameter for the anonymous bind. | CN=binduser,OU=users,DC=example,DC=domain,DC=com\n`LDAP_BINDPW` | LDAP user password. Do not specify this parameter for the anonymous bind. | LdapUserPassw0rd\n`LDAP_FILTER` | LDAP filter. | (sAMAccountName=%u)\n`LDAP_AUTH_METHOD` | LDAP authentication method. | bind\n`LDAP_VERSION` | LDAP protocol version | 3\n`LDAP_USE_TLS` | Enable LDAP TLS | 1\n`LDAP_TLS_CIPHERS` | Set TLS ciphers list to allow | SECURE256:SECURE128\n`LDAP_TLS_CHECK_PEER` | Require and verify LDAP server certificate | 1\n`LDAP_TLS_CACERT_FILE` | Path to CA cert file. Used when server certificate verification is enabled | /etc/ssl/certs/ca-certificates.crt\n`LDAP_TLS_CACERT_DIR` | Path to CA certs directory. Used when server certificate verification is enabled. | /etc/ssl/certs\n`LDAP_START_TLS` | Enable START_TLS, requires LDAPv3, URL must be ldap:// not ldaps:// | 0\n\n#### Authentication using JWT tokens\n\nYou can use JWT tokens to authenticate users. To enable it you have to enable authentication with `ENABLE_AUTH` and\nset `AUTH_TYPE` to `jwt`, then configure the settings you can see below.\n\nVariable | Description | Example\n--- | --- | ---\n`JWT_APP_ID` | Application identifier | my_jitsi_app_id\n`JWT_APP_SECRET` | Application secret known only to your token | my_jitsi_app_secret\n`JWT_ACCEPTED_ISSUERS` | (Optional) Set asap_accepted_issuers as a comma separated list | my_web_client,my_app_client\n`JWT_ACCEPTED_AUDIENCES` | (Optional) Set asap_accepted_audiences as a comma separated list | my_server1,my_server2\n`JWT_ASAP_KEYSERVER` | (Optional) Set asap_keyserver to a url where public keys can be found | https://example.com/asap>\n`JWT_ALLOW_EMPTY` | (Optional) Allow anonymous users with no JWT while validating JWTs when provided | 0\n`JWT_AUTH_TYPE` | (Optional) Controls which module is used for processing incoming JWTs | token\n`JWT_TOKEN_AUTH_MODULE` | (Optional) Controls which module is used for validating JWTs | token_verification\n\nThis can be tested using the [jwt.io] debugger. Use the following sample payload:\n\n```json\n{\n  \"context\": {\n    \"user\": {\n      \"avatar\": \"https://robohash.org/john-doe\",\n      \"name\": \"John Doe\",\n      \"email\": \"jdoe@example.com\"\n    }\n  },\n  \"aud\": \"my_jitsi_app_id\",\n  \"iss\": \"my_jitsi_app_id\",\n  \"sub\": \"meet.jitsi\",\n  \"room\": \"*\"\n}\n```\n\n#### Authentication using Matrix\n\nFor more information see the documentation of the \"Prosody Auth Matrix User Verification\" [here](https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification).\n\nVariable | Description | Example\n--- | --- | ---\n`MATRIX_UVS_URL` | Base URL to the matrix user verification service (without ending slash) | `https://uvs.example.com:3000`\n`MATRIX_UVS_ISSUER` | (optional) The issuer of the auth token to be passed through. Must match what is being set as `iss` in the JWT. | issuer (default)\n`MATRIX_UVS_AUTH_TOKEN` | (optional) user verification service auth token, if authentication enabled | changeme\n`MATRIX_UVS_SYNC_POWER_LEVELS` | (optional) Make Matrix room moderators owners of the Prosody room. | 1\n\n#### Authentication using Hybrid Matrix Token\n\nYou can use `Hybrid Matrix Token` to authenticate users. It supports `Matrix` and `JWT Token` authentications\non the same setup. To enable it you have to enable authentication with `ENABLE_AUTH` and set `AUTH_TYPE` to\n`hybrid_matrix_token`, then configure the settings you can see below.\n\nFor more information see the documentation of the \"Hybrid Matrix Token\"\n[here](https://github.com/jitsi-contrib/prosody-plugins/tree/main/auth_hybrid_matrix_token).\n\nVariable | Description | Example\n--- | --- | ---\n`MATRIX_UVS_URL` | Base URL to the matrix user verification service (without ending slash) | `https://uvs.example.com:3000`\n`MATRIX_UVS_ISSUER` | (optional) The issuer of the auth token to be passed through. Must match what is being set as `iss` in the JWT. It allows all issuers (`*`) by default. | my_issuer\n`MATRIX_UVS_AUTH_TOKEN` | (optional) user verification service auth token, if authentication enabled | my_matrix_secret\n`MATRIX_UVS_SYNC_POWER_LEVELS` | (optional) Make Matrix room moderators owners of the Prosody room. | 1\n`MATRIX_LOBBY_BYPASS` | (optional) Allow Matrix room members to bypass Jitsi lobby check. | 1\n`JWT_APP_ID` | Application identifier | my_jitsi_app_id\n`JWT_APP_SECRET` | Application secret known only to your token | my_jitsi_app_secret\n`JWT_ALLOW_EMPTY` | (Optional) Allow anonymous users with no JWT while validating JWTs when provided | 0\n\n#### External authentication\n\nVariable | Description | Example\n--- | --- | ---\n`TOKEN_AUTH_URL` | Authenticate using external service or just focus external auth window if there is one already. | https://auth.meet.example.com/{room}>\n\n### Shared document editing using Etherpad\n\nYou can collaboratively edit a document via [Etherpad]. In order to enable it, set the config options below and run\nDocker Compose with the additional config file `etherpad.yml`.\n\nHere are the required options:\n\nVariable | Description | Example\n--- | --- | ---\n`ETHERPAD_URL_BASE` | Set etherpad-lite URL | `http://etherpad.meet.jitsi:9001`\n\n### Virtual collaborative whiteboard using Excalidraw\n\nYou can use a virtual collaborative whiteboard via [Excalidraw]. In order to enable set `WHITEBOARD_ENABLED` and run\nDocker Compose with the additional config file `whiteboard.yml`.\n\nHere are the required options:\n\nVariable | Description | Default value\n--- | --- | ---\n`WHITEBOARD_ENABLED` | Enable whiteboard addon | false\n\n\nThese options are also available\n\nVariable | Description | Example\n--- | --- | ---\n`WHITEBOARD_COLLAB_SERVER_PUBLIC_URL` | Set whiteboard URL | http://whiteboard.meet.jitsi:3000\n\n### Transcription configuration\n\nIf you want to enable the Transcribing function, set the config options below and run Docker Compose with the additional config file transcriber.yml.\n\nVariable | Description | Example\n--- | --- | ---\n`ENABLE_TRANSCRIPTIONS` | Enable Jigasi transcription in a conference | 1\n\nIn addition, the following are options are used to configure the various transcription backends and features:\n\nVariable | Description | Default\n--- | --- | ---\n`GC_PROJECT_ID` | `project_id` from Google Cloud Credentials\n`GC_PRIVATE_KEY_ID` | `private_key_id` from Google Cloud Credentials\n`GC_PRIVATE_KEY` | `private_key` from Google Cloud Credentials\n`GC_CLIENT_EMAIL` | `client_email` from Google Cloud Credentials\n`GC_CLIENT_ID` | `client_id` from Google Cloud Credentials\n`GC_CLIENT_CERT_URL` | `client_x509_cert_url` from Google Cloud Credentials\n`JIGASI_TRANSCRIBER_ADVERTISE_URL` | Jigasi will post an url to the chat with transcription file | true\n`JIGASI_TRANSCRIBER_CUSTOM_SERVICE` | Jigasi will use this class for custom transcriptions instead of google cloud\n`JIGASI_TRANSCRIBER_CUSTOM_TRANSLATION_SERVICE` | Jigasi will use this class for custom transctions instead of google cloud\n`JIGASI_TRANSCRIBER_ENABLE_SAVING` | Jigasi will save results to a transcription file | true\n`JIGASI_TRANSCRIBER_FILTER_SILENCE` | Jigasi will filter silent audio and not forward to backends\n`JIGASI_TRANSCRIBER_LIBRETRANSLATE_URL` | URL for libretranslate services\n`JIGASI_TRANSCRIBER_OCI_COMPARTMENT` | OCI compartment for use with Oracle Cloud Speech AI services\n`JIGASI_TRANSCRIBER_OCI_REGION` | OCI region name for use with Oracle Cloud Speech AI services\n`JIGASI_TRANSCRIBER_RECORD_AUDIO` | Jigasi will record audio when transcriber is on | true\n`JIGASI_TRANSCRIBER_REMOTE_CONFIG_URL` | URL to control transcriber custom service based on conference details\n`JIGASI_TRANSCRIBER_SEND_TXT` | Jigasi will send transcribed text to the chat when transcriber is on | true\n`JIGASI_TRANSCRIBER_USER` | Jigasi XMPP user\n`JIGASI_TRANSCRIBER_VOSK_URL` | URL for use with vosk backend\n`JIGASI_TRANSCRIBER_WHISPER_URL` | URL for use with whisper backend\n`JIGASI_TRANSCRIBER_WHISPER_PRIVATE_KEY_NAME` | Private Key ID of the private key to use with whisper\n`JIGASI_TRANSCRIBER_WHISPER_PRIVATE_KEY` | Private Key material to use with whisper, without newlines or START/END delimiters \n\nFor setting the Google Cloud Credentials please read https://cloud.google.com/text-to-speech/docs/quickstart-protocol &gt; section \"Before you begin\" paragraph 1 to 5.\n\n### Sentry logging configuration\n\nVariable | Description | Default value\n--- | --- | ---\n`JVB_SENTRY_DSN` | Sentry Data Source Name (Endpoint for Sentry project) | `https://public:private@host:port/1`\n`JICOFO_SENTRY_DSN` | Sentry Data Source Name (Endpoint for Sentry project) | `https://public:private@host:port/1`\n`JIGASI_SENTRY_DSN` | Sentry Data Source Name (Endpoint for Sentry project) | `https://public:private@host:port/1`\n`SENTRY_ENVIRONMENT` | Optional environment info to filter events | production\n`SENTRY_RELEASE` | Optional release info to filter events | 1.0.0\n\n### TURN server configuration\n\nConfigure external TURN servers.\n\nVariable | Description | Default value\n--- | --- | ---\n`TURN_CREDENTIALS` | Credentials for TURN servers |\n`TURN_HOST` | TURN server hostnames as a comma separated list (UDP or TCP transport) |\n`TURN_PORT` | TURN server port (UDP or TCP transport) | 443\n`TURN_TRANSPORT` | TURN server protocols as a comma separated list (UDP or TCP or both) | tcp\n`TURNS_HOST` | TURN server hostnames as a comma separated list (TLS transport) |\n`TURNS_PORT` | TURN server port (TLS transport) | 443\n`TURN_TLL`| TURN max allocation duration (sec) | 86400\n\n### Advanced configuration\n\nThese configuration options are already set and generally don't need to be changed.\n\nVariable | Description | Default value\n--- | --- | ---\n`XMPP_DOMAIN` | Internal XMPP domain | meet.jitsi\n`XMPP_AUTH_DOMAIN` | Internal XMPP domain for authenticated services | auth.meet.jitsi\n`XMPP_SERVER` | Internal XMPP server name xmpp.meet.jitsi | xmpp.meet.jitsi\n`XMPP_BOSH_URL_BASE` | Internal XMPP server URL for BOSH module | `http://xmpp.meet.jitsi:5280`\n`XMPP_MUC_DOMAIN` | XMPP domain for the MUC | muc.meet.jitsi\n`XMPP_INTERNAL_MUC_DOMAIN` | XMPP domain for the internal MUC | internal-muc.meet.jitsi\n`XMPP_GUEST_DOMAIN` | XMPP domain for unauthenticated users | guest.meet.jitsi\n`XMPP_RECORDER_DOMAIN` | Domain for the jibri recorder | recorder.meet.jitsi\n`XMPP_MODULES` | Custom Prosody modules for XMPP_DOMAIN (comma separated) | info,alert\n`XMPP_MUC_MODULES` | Custom Prosody modules for MUC component (comma separated) | info,alert\n`XMPP_INTERNAL_MUC_MODULES` | Custom Prosody modules for internal MUC component (comma separated) | info,alert\n`GLOBAL_MODULES` | Custom prosody modules to load in global configuration (comma separated) | statistics,alert\n`GLOBAL_CONFIG` | Custom configuration string with escaped newlines | foo = bar;\\nkey = val;\n`RESTART_POLICY` | Container restart policy | defaults to `unless-stopped`\n`DISABLE_HTTPS` | Handle TLS connections outside of this setup | 0\n`ENABLE_HTTP_REDIRECT` | Redirect HTTP traffic to HTTPS | 0\n`LOG_LEVEL` | Controls which logs are output from prosody and associated modules | info\n`ENABLE_HSTS` | Send a `strict-transport-security` header to force browsers to use a secure and trusted connection. Recommended for production use. | 1\n`ENABLE_IPV6` | Provides means to disable IPv6 in environments that don't support it | 1\n`ENABLE_COLIBRI_WEBSOCKET_UNSAFE_REGEX` | Enabled older unsafe regex for JVB colibri-ws URLs. WARNING: Enable with caution, this regex allows connections to arbitrary internal IP addresses and is not recommended for production use.  Unsafe regex is defined as `[a-zA-Z0-9-\\._]+` | 0\n`COLIBRI_WEBSOCKET_JVB_LOOKUP_NAME` | DNS name to look up JVB IP address, used for default value of `COLIBRI_WEBSOCKET_REGEX` | jvb\n`COLIBRI_WEBSOCKET_REGEX` | Overrides the colibri regex used for proxying to JVB.  Recommended to override in production with values matching possible JVB IP ranges | defaults to `dig $COLIBRI_WEBSOCKET_JVB_LOOKUP_NAME` unless `DISABLE_COLIBRI_WEBSOCKET_JVB_LOOKUP` is set to true\n`DISABLE_COLIBRI_WEBSOCKET_JVB_LOOKUP` | Controls whether to run `dig $COLIBRI_WEBSOCKET_JVB_LOOKUP_NAME` when defining COLIBRI_WEBSOCKET_REGEX | 0\n\n#### Advanced Prosody options\n\nVariable | Description | Default value\n--- | --- | ---\n`PROSODY_RESERVATION_ENABLED` | Enable Prosody's reservation REST API | false\n`PROSODY_RESERVATION_REST_BASE_URL` | Base URL of Prosody's reservation REST API |\n`PROSODY_AUTH_TYPE` | Select authentication type for Prosody (internal, jwt or ldap) | `AUTH_TYPE`\n`PROSODY_ENABLE_METRICS` | Enables the http_openmetrics module which exposes Prometheus metrics at `/metrics` | false\n`PROSODY_METRICS_ALLOWED_CIDR` | CIDR block permitted to access metrics | 172.16.0.0/12\n\n#### Advanced Jicofo options\n\nVariable | Description | Default value\n--- | --- | ---\n`JICOFO_COMPONENT_SECRET` | XMPP component password for Jicofo | s3cr37\n`JICOFO_AUTH_USER` | XMPP user for Jicofo client connections | focus\n`JICOFO_AUTH_PASSWORD` | XMPP password for Jicofo client connections | `<unset>`\n`JICOFO_ENABLE_AUTH` | Enable authentication in Jicofo | `ENABLE_AUTH`\n`JICOFO_AUTH_TYPE` | Select authentication type for Jicofo (internal, jwt or ldap) | `AUTH_TYPE`\n`JICOFO_AUTH_LIFETIME` | Select session timeout value for an authenticated user | 24 hours\n`JICOFO_ENABLE_HEALTH_CHECKS` | Enable health checks inside Jicofo, allowing the use of the REST api to check Jicofo's status | false\n\n#### Advanced JVB options\n\nVariable | Description | Default value\n--- | --- | ---\n`JVB_AUTH_USER` | XMPP user for JVB MUC client connections | jvb\n`JVB_AUTH_PASSWORD` | XMPP password for JVB MUC client connections | `<unset>`\n`JVB_STUN_SERVERS` | STUN servers used to discover the server's public IP | stun.l.google.com:19302, stun1.l.google.com:19302, stun2.l.google.com:19302\n`JVB_PORT` | UDP port for media used by Jitsi Videobridge | 10000\n`JVB_COLIBRI_PORT` | COLIBRI REST API port of JVB exposed to localhost | 8080\n`JVB_BREWERY_MUC` | MUC name for the JVB pool | jvbbrewery\n`COLIBRI_REST_ENABLED` | Enable the COLIBRI REST API | true\n`SHUTDOWN_REST_ENABLED` | Enable the shutdown REST API | true\n\n#### Advanced Jigasi options\n\nVariable | Description | Default value\n--- | --- | ---\n`JIGASI_ENABLE_SDES_SRTP` | Enable SDES srtp | 0\n`JIGASI_SIP_KEEP_ALIVE_METHOD` | Keepalive method | OPTIONS\n`JIGASI_HEALTH_CHECK_SIP_URI` | Health-check extension |\n`JIGASI_HEALTH_CHECK_INTERVAL` | Health-check interval | 300000\n`JIGASI_XMPP_USER` | XMPP user for Jigasi MUC client connections | jigasi\n`JIGASI_XMPP_PASSWORD` | XMPP password for Jigasi MUC client connections | `<unset>`\n`JIGASI_BREWERY_MUC` | MUC name for the Jigasi pool | jigasibrewery\n`JIGASI_PORT_MIN` | Minimum port for media used by Jigasi | 20000\n`JIGASI_PORT_MAX` | Maximum port for media used by Jigasi | 20050\n\n### Running behind NAT or on a LAN environment\n\nWhen running running in a LAN environment, or on the public Internet via NAT, the ``JVB_ADVERTISE_IPS`` env variable should be set.\nThis variable allows to control which IP addresses and ports the JVB will advertise for WebRTC media traffic. It is necessary to set it regardless of the use of a reverse proxy, since it's the IP address that will receive the media (audio / video) and not HTTP traffic, hence it's oblivious to the reverse proxy.\n\n:::note\nThis variable used to be called ``DOCKER_HOST_ADDRESS`` but it got renamed for clarity and to support a list of IPs.\n:::\n\nIf your users are coming in over the Internet (and not over LAN), this will likely be your public IP address. If this is not set up correctly, calls will crash when more than two users join a meeting.\n\nThe public IP address is attempted to be discovered via [STUN].\nSTUN servers can be specified with the ``JVB_STUN_SERVERS`` option.\n\n:::note\nDue to a bug in the docker version currently in the Debian repos (20.10.5), [Docker does not listen on IPv6 ports](https://forums.docker.com/t/docker-doesnt-open-ipv6-ports/106201/2), so for that combination you will have to [manually obtain the latest version](https://docs.docker.com/engine/install/debian/).\n:::\n\n#### Split horizon\n\nIf you are running in a split horizon environemt (LAN internal clients connect to a local IP and other clients connect to a public IP) you can specify\nmultiple advertised IPs by separating them with commas:\n\n```\nJVB_ADVERTISE_IPS=192.168.1.1,1.2.3.4\n```\n\n#### Advertising Ports\n\nIf your external port differs from the internal `JVB_PORT`, you can specify the advertised port along with the advertised IP:\n\n```\nJVB_ADVERTISE_IPS=192.168.1.1#12345,fe80::1#12345\n```\n\n:::note\nSince IPv6 addresses use `:` in their representation, the `#` character is used to separate the IP from the port.\n:::\n\n\n#### Offline / airgapped installation\n\nIf your setup does not have access to the Internet you'll need to disable STUN on the JVB since discovering its own IP address will fail, but that is not necessary on that type of environment.\n\n```\nJVB_DISABLE_STUN=true\n```\n\n### Adjust UDP buffers\n\nIf you are experiencing issues with UDP traffic, like synchronization issues, skipping frames and similar, or if you expect a high traffic and big conferences, you might want to adjust the UDP buffer sizes.\nYou need to do that on the host system, that hosts the jvb container.\nTo do so you can get this [sysctl config file](https://github.com/jitsi/jitsi-videobridge/blob/master/config/20-jvb-udp-buffers.conf) and save it in `/etc/sysctl.d` and load it via: `sysctl --system`.\n\n\n## Accessing server logs\n\nThe default bahavior of `docker-jitsi-meet` is to log to `stdout`.\n\nWhile the logs are sent to `stdout`, they are not lost: unless configured to drop all logs, Docker keeps them available for future retrieval and processing.\n\nIf you need to access the container's logs you have multiple options. Here are the main ones:\n\n* run `docker compose logs -t -f <service_name>` from command line, where `<service_name>` is one of `web`, `prosody`,`jvb`, `jicofo`. This command will output the logs for the selected service to stdout with timestamps.\n* use a standard [docker logging driver](https://docs.docker.com/config/containers/logging/configure/) to redirect the logs to the desired target (for instance `syslog` or `splunk`).\n* search [docker hub](https://hub.docker.com/search?q=) for a third party [docker logging driver plugin](https://docs.docker.com/config/containers/logging/plugins/)\n* or [write your own driver plugin](https://docs.docker.com/engine/extend/plugins_logging/) if you have a very specific need.\n\nFor instance, if you want to have all logs related to a `<service_name>` written to `/var/log/jitsi/<service_name>` as `json` output, you could use [docker-file-log-driver](https://github.com/deep-compute/docker-file-log-driver) and configure it by adding the following block in your `docker-compose.yml` file, at the same level as the `image` block of the selected `<service_name>`:\n\n```yaml\nservices:\n    <service_name>:\n        image: ...\n        ...\n        logging:\n            driver: file-log-driver\n            options:\n                fpath: \"/jitsi/<service_name>.log\"\n```\n\nIf you want to only display the `message` part of the log in `json` format, simply execute the following command (for instance if `fpath` was set to `/jitsi/jvb.log`) which uses `jq` to extract the relevant part of the logs:\n\n```\nsudo cat /var/log/jitsi/jvb.log | jq -r '.msg' | jq -r '.message'\n```\n\n## Build Instructions\n\nBuilding your images allows you to edit the configuration files of each image individually, providing more customization for your deployment.\n\nThe docker images can be built by running the `make` command in the main repository folder. If you need to overwrite existing images from the remote source, use `FORCE_REBUILD=1 make`.\n\nIf you are on the unstable branch, build the images with `FORCE_REBUILD=1 JITSI_RELEASE=unstable make`.\n\nYou are now able to run `docker compose up` as usual.\n\n## Running behind a reverse proxy\n\nWhen running behing a reverse proxy from the same host, the communication between the proxy and Jitsi Meet is often in HTTP and not HTTPS since we generally don't have valid certificates for `localhost`.\n\n:::note\nJitsi Meet does not currently work well when deployed in a subdirectory.\n:::\n\n### Disable HTTPS\n\nHTTPS can be disabled in the Docker Compose configuration (since HTTPS will probably not work on localhost):\n\n```bash\nDISABLE_HTTPS=1\nENABLE_HTTP_REDIRECT=0\nENABLE_LETSENCRYPT=0\n```\n\n### Do not expose the Jitsi Meet's ports publicly\n\nBy default, the `HTTP_PORT` and `HTTPS_PORT` are binding to any ip address, so are publicly open unless a firewall blocks them. When using a reverse proxy, this is not necessary. This can be changed by updating the web container's ports configuration:\n```yaml\n            - '127.0.0.1:${HTTP_PORT}:80'\n            - '127.0.0.1:${HTTPS_PORT}:443'\n```\ninstead of\n```yaml\n            - '${HTTP_PORT}:80'\n            - '${HTTPS_PORT}:443'\n```\n\n### Reverse proxy configuration\n\nBy default this setup is using WebSocket connections for 2 core components:\n\n* Signalling (XMPP)\n* Bridge channel (colibri)\n\nDue to the hop-by-hop nature of WebSockets the reverse proxy must properly terminate and forward WebSocket connections. There 2 routes require such treatment:\n\n* `/xmpp-websocket`\n* `/colibri-ws`\n\nThe other HTTP requests must be handled by the web container.\n\nIn the following configuration examples, `http://localhost:8000/` is the url of the web service's ingress (`8000` corresponds to `HTTP_PORT`).\n\n#### nginx\n\nWith nginx, these routes can be forwarded using the following config snippet:\n\n```nginx\nlocation /xmpp-websocket {\n    proxy_pass http://localhost:8000/xmpp-websocket;\n    proxy_http_version 1.1;\n    proxy_set_header Upgrade $http_upgrade;\n    proxy_set_header Connection \"upgrade\";\n}\n\nlocation /colibri-ws {\n    proxy_pass http://localhost:8000/colibri-ws;\n    proxy_http_version 1.1;\n    proxy_set_header Upgrade $http_upgrade;\n    proxy_set_header Connection \"upgrade\";\n}\n\nlocation / {\n    proxy_pass http://localhost:8000/;\n    proxy_http_version 1.1;\n}\n```\n\n#### Apache\n\nWith Apache, `mod_proxy`, `mod_proxy_http` and `mod_proxy_wstunnel` need to be enabled.\n      \nThe reverse proxy can be configured using the following config snippet:\n\n```apache\n<IfModule mod_proxy.c>\n    <IfModule mod_proxy_wstunnel.c>\n        ProxyTimeout 900\n        ProxyPass /xmpp-websocket ws://localhost:8000/xmpp-websocket\n        ProxyPass /colibri-ws/ ws://localhost:8000/colibri-ws/\n        ProxyPass / http://localhost:8000/\n        ProxyPassReverse / http://localhost:8000/\n    </IfModule>\n</IfModule>\n```\n\n### Disabling WebSocket connections\n\n:::note\nThis is not the recommended setup.\n:::\n\nIf using WebSockets is not an option, these environment variables can be set to fallback to HTTP polling and WebRTC datachannels:\n\n```bash\nENABLE_SCTP=1\nENABLE_COLIBRI_WEBSOCKET=0\nENABLE_XMPP_WEBSOCKET=0\n```\n\n[S6 Overlay]: https://github.com/just-containers/s6-overlay\n[Jitsi repositories]: https://jitsi.org/downloads/\n[Prosody]: https://prosody.im/\n[Jicofo]: https://github.com/jitsi/jicofo\n[Jitsi Videobridge]: https://github.com/jitsi/jitsi-videobridge\n[Jigasi]: https://github.com/jitsi/jigasi\n[STUN]: https://en.wikipedia.org/wiki/STUN\n[jwt.io]: https://jwt.io/#debugger-io\n[Etherpad]: https://github.com/ether/etherpad-lite\n[Excalidraw]: https://github.com/excalidraw/excalidraw\n[Jibri]: https://github.com/jitsi/jibri\n[latest release]: https://github.com/jitsi/docker-jitsi-meet/releases/latest\n"
  },
  {
    "path": "docs/devops-guide/faq.md",
    "content": "---\nid: faq\ntitle: FAQ\n---\n\n## How to migrate away from multiplexing and enable bridge websockets\n\nFor a while, we were using nginx multiplexing to serve Jitsi Meet's content on https(port 443) and use the same port for running a turn server.\nThis proved to be problematic(you cannot use websockets with this setup) and we moved away from it.\nHere is how to remove multiplexing and enable websockets in favor of WebRTC Data Channels.\n1. Dropping multiplexing in nginx\n  - delete `/etc/nginx/modules-enabled/60-jitsi-meet.conf`\n  - Then go to `/etc/nginx/sites-available/your-conf` and change your virtual host to listen on 443 instead of 4444.\n2. Edit turnserver config\n  - make sure your turnserver is listening on standard port tls port `5349`. Make sure in `/etc/turnserver.conf` you have the following: `tls-listening-port=5349`\n  - In `/etc/prosody/conf.avail/your-conf.cfg.lua` prosody is instructed to announce `turns` turn server on port `5349` by having this line:\n    `{ type = \"turns\", host = \"your-domain\", port = \"5349\", transport = \"tcp\" }`. Make sure you replace `your-domain` with the DNS of your deployment.\n3. Add the bridge websocket location in your nginx config (add it after the `location = /xmpp-websocket` section):\n  ```\n    # colibri (JVB) websockets for jvb1\n    location ~ ^/colibri-ws/default-id/(.*) {\n       proxy_pass http://127.0.0.1:9090/colibri-ws/default-id/$1$is_args$args;\n       proxy_http_version 1.1;\n       proxy_set_header Upgrade $http_upgrade;\n       proxy_set_header Connection \"upgrade\";\n       tcp_nodelay on;\n    }\n  ```\n4. Enable the websockets in Jitsi Videobridge. Make sure in `/etc/jitsi/videobridge/jvb.conf` you have:\n  ```\n  videobridge {\n    http-servers {\n        public {\n            port = 9090\n            host = \"localhost\"\n        }\n    }\n    websockets {\n        enabled = true\n        domain = \"your-domain:443\"\n        tls = true\n    }\n}\n  ```\n  Make sure you replace `your-domain` with the DNS of your deployment.\n5. After restarting the bridge (`systemctl restart jitsi-videobridge2`) and nginx (`systemctl restart nginx`) you are good to go!\n"
  },
  {
    "path": "docs/devops-guide/file-sharing.md",
    "content": "---\nid: file-sharing\ntitle: File sharing\n---\n\n## Deploying and configuring a demo file sharing service for Jitsi Meet\n\nThe Jitsi Meet UI can use a file sharing service which implements the following [API](https://github.com/jitsi/jitsi-meet/blob/master/resources/file-sharing.yaml).\n\nThere is an example implementation of such a service in the [jitsi-meet-file-sharing](https://github.com/jitsi/jitsi-meet-file-sharing-service). \nThat is a simple implementation using local filesystem storage.\n\n### Setup\n\nOn an existing deployment or after installing jitsi-meet following the [Self-Hosting Guide](https://jitsi.org/qi) you need to install the file sharing service and configure jitsi-meet to use it.\n\n- Download and install nvm\n```\ncurl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.4/install.sh | bash\n```\n\n- Clone the repository and deploy the service\n```bash\ncd /srv\ngit clone https://github.com/jitsi/jitsi-meet-file-sharing-service.git\ncd /srv/jitsi-meet-file-sharing-service\nnvm install\nnvm use\n./deploy.sh\n```\n\n- Setup sign material for short-lived tokens\n```bash\nmkdir /etc/jitsi/file-sharing-service\nmkdir -p /var/www/jitsi-meet-file-sharing-service/uploads\nopenssl genrsa -out /etc/jitsi/file-sharing-service/short_lived_token.key 2048\nopenssl rsa -in /etc/jitsi/file-sharing-service/short_lived_token.key -pubout -out /etc/jitsi/file-sharing-service/short_lived_token.pub\nssh-keygen -f /etc/jitsi/file-sharing-service/short_lived_token.key -e -m pem > /etc/jitsi/file-sharing-service/short_lived_token.pem\nchmod g+r /etc/jitsi/file-sharing-service/short_lived_token.key\nchown root:prosody /etc/jitsi/file-sharing-service/short_lived_token.key\n```\n\n- Enable short-lived token in your prosody config.\nAdd its configuration to `/etc/prosody/conf.avail/your-domain.cfg.lua`:\n```lua\nshort_lived_token = {\n    issuer = 'prosody';\n    accepted_audiences = { 'file-sharing' };\n    key_path = '/etc/jitsi/file-sharing-service/short_lived_token.key';\n    key_id = 'jitsi/short_lived_token_2025';\n    ttl_seconds = 30;\n};\n```\nEnable it in the `modules_enabled` section under the main virtual host:\n```lua\nmodules_enabled = {\n    ...\n    'short_lived_token';\n    ...\n};\n```\n\n- restart prosody:\n```bash\nsystemctl restart prosody\n```\n\n- Configure the file sharing service in config.js, add to the end:\n```javascript\nconfig.fileSharing = {\n    apiUrl :\"https://your-domain/file-service/v1/documents\",\n    enabled: true,\n};\n```\n\nConfigure nginx by adding the following to your nginx configuration file (e.g., `/etc/nginx/sites-available/your-domain.conf`):\n```nginx\n    client_max_body_size 50M;\n    location ^~ /file-service/ {\n        # Remove /file-service prefix when forwarding\n        rewrite ^/file-service(/.*)$ $1 break;\n\n        # Add CORS headers\n        add_header Access-Control-Allow-Origin \"$http_origin\" always;\n        add_header Access-Control-Allow-Methods \"GET, POST, PUT, DELETE, OPTIONS\" always;\n        add_header Access-Control-Allow-Headers \"Authorization, Content-Type, X-Requested-With\" always;\n        add_header Access-Control-Allow-Credentials \"true\" always;\n\n        # Handle preflight requests\n        if ($request_method = OPTIONS) {\n            return 204;\n        }\n\n        proxy_pass http://localhost:3000/;\n        proxy_http_version 1.1;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection 'upgrade';\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        proxy_cache_bypass $http_upgrade;\n        proxy_connect_timeout 60s;\n        proxy_send_timeout 60s;\n        proxy_read_timeout 60s;\n    }\n```\n- restart nginx\n```bash\nsystemctl restart nginx\n```\n\n- Setup the environment variables for the file sharing service in `/srv/jitsi-meet-file-sharing-service/.env`:\n```bash\nJWT_PUBLIC_KEY_PATH=/etc/jitsi/file-sharing-service/short_lived_token.pem\nUPLOAD_DIR=/var/www/jitsi-meet-file-sharing-service/uploads\n```\n\n- restart the file sharing service:\n```bash\ncd /srv/jitsi-meet-file-sharing-service\nnvm use\npm2 delete jitsi-meet-file-sharing-service\npm2 start ecosystem.config.js --env production\n```\n\n### Authentication Configuration\n\n- If you are using **JWT authentication**, make sure you pass 'file-upload' feature in `context.features` in the jwt.\n```\n{\n  ...,\n  \"context\": {\n    \"user\": {\n      \"id\": \"....\",\n      ...\n    },\n    \"features\": {\n        \"file-upload\":  true,\n        ...\n    },\n    ...\n  }\n}\n```\n- If you are using **internal authentication** (e.g. `prosodyctl register`) or other authentication methods without tokens, you need to configure `jitsi_default_permissions` in your Prosody config (e.g. `/etc/prosody/conf.avail/your-domain.cfg.lua`) to include `file-upload`.\n```lua\njitsi_default_permissions = {\n        livestreaming = true;\n        recording = true;\n        transcription = true;\n        ['outbound-call'] = true;\n        ['create-polls'] = true;\n        ['file-upload'] = true;\n        ['send-groupchat'] = true;\n        flip = true;\n    };\n```\nIf you change prosody configuration, make sure to restart it.\n\n### Usage\n\n - To rebuild the app: `npm run build`\n\n - To restart the service: `pm2 restart file-sharing-service`\n\n - To watch the logs: `pm2 logs file-sharing-service`\n\n\n"
  },
  {
    "path": "docs/devops-guide/ldap-authentication.md",
    "content": "---\nid: ldap-authentication\ntitle: LDAP Authentication\nsidebar_label: Authentication (LDAP)\n---\n\n:::note\nThis is a first draft and might not work on your system. It has been tested on Debian 11 installation with prosody 0.11 and authenticates against an OpenLDAP directory, and on Ubuntu 24.04 with Prosody 0.12 against an Active Directory.\n:::\n\nIf you want to authenticate your users against an LDAP directory instead \nof the local Prosody user database, you can use the Cyrus SASL package. \nUsing this package you might be able to validate user-supplied credentials \nagainst other sources, such as PAM, SQL and more - but this is beyond \nthis article.\n\n## Prerequisites\n\nBefore following this article, make sure you have set up Prosody as \ndescribed in [Authentication (Secure Domain)](secure-domain.md) first.\n\n### Required packages\n\nOn Debian systems you need to install some required packages:\n\n```\nsudo apt-get install sasl2-bin libsasl2-modules-ldap lua-cyrussasl prosody-modules\nsudo prosodyctl install --server=https://modules.prosody.im/rocks/ mod_auth_cyrus\n```\n\nThe first two packages are necessary for Cyrus' `saslauthd` and allow it \nto connect to an LDAP directory. The `lua-cyrussasl`-package allows \nProsody to access Cyrus SASL.\n\nInstalling the [mod_auth_cyrus](https://modules.prosody.im/mod_auth_cyrus) module is neccessary because support for Cyrus SASL has been [removed](https://prosody.im/doc/cyrus_sasl) from mainline Prosody and placed in the community module repository.\n\n## Install and set up Cyrus SASL\n\nThe following options define a basic LDAP configuration. A full set of \npossible options can be found in [LDAP_SASLAUTHD](https://github.com/winlibs/cyrus-sasl/blob/master/saslauthd/LDAP_SASLAUTHD).\n\nBy default Cyrus' `saslauthd` searches for its LDAP configuration in \n`/etc/saslauthd.conf`. So create this file and enter something similar \nto define your LDAP environment:\n\n```\nldap_servers: ldaps://ldap.example.com\nldap_bind_dn: admin@example.com\nldap_bind_pw: topsecret\nldap_auth_method: bind\nldap_search_base: ou=people,dc=example,dc=com\n```\n\n:::note\nOne omitted option you might want to look into is `ldap_filter` which defaults to `uid=%u` and should work for a lot of systems.  If you are using a Samba or Microsoft AD instance as your LDAP server you may need to change this to `ldap_filter: (sAMAccountName=%U)` as `uid` is NULL by default many configurations. You can also use the `ldap_filter` to allow only specific users access. For more details on this and other options see the `LDAP_SASLAUTHD` document linked above.\n\nPlease note that Prosody may experience issues with usernames containing the \"@\"-symbol. You can work around this issue by changing `uid=%u` to `uid=%U`, which is [defined](https://github.com/winlibs/cyrus-sasl/blob/d933c030ce12ec0668469d79ab8378e347a1b3ba/saslauthd/LDAP_SASLAUTHD#L126) as the \"user portion of %u (%U = test when %u = test@domain.tld)\"\n:::\n\n### Test LDAP authentication\n\nTo test if the LDAP configuration is working, you can start `saslauthd` in \ndebug mode while specifying the mandatory LDAP authentication mechanism:\n\n```\nsudo saslauthd -d -a ldap\n```\n\nThe test utility for the SASL authentication server can then be used in a \nsecondary terminal. Replace `user` and `password` with credentials stored \nin LDAP.\n\n```\nsudo testsaslauthd -u user -p password\n0: OK \"Success.\"\n\nsudo testsaslauthd -u user -p wrongpassword\n0: NO \"authentication failed\"\n```\n\nAfter testing, you can stop `saslauthd` using `ctrl-c`.\n\n### Enable the `saslauthd` service\n\nYou will need to edit the `/etc/default/saslauthd` to enable the `saslauthd` service to run at boot and have it use LDAP for authentication.  You can use sed to do this quickly.\n```\nsudo sed -i -e \"s/START=.*/START=yes/\" -e \"s/MECHANISMS=.*/MECHANISMS=\\\"ldap\\\"/\" /etc/default/saslauthd\n```\n\nThis will make the following changes to `/etc/default/saslauthd`.\n```\n[...]\n# Should saslauthd run automatically on startup? (default: no)\nSTART=yes\n[...]\n# Example: MECHANISMS=\"pam\"\nMECHANISMS=\"ldap\"\n[...]\n```\n\n\nIt is not necessary to point `MECH_OPTIONS` to the LDAP configuration file \nsince this is the default for this mechanism.\n\nNow you can start, restart and stop `saslauthd` using the `service` scripts:\n\n```\nsudo service saslauthd restart\n```\n\nIf you experience issues, check `/var/log/auth.log` for `saslauthd` entries.\n\n### Cyrus SASL Configuration file\n\nCyrus SASL requires a configuration file in order to know how to check user \ncredentials. For Prosody, the file is named `prosody.conf` by default. \nIts location varies by OS and distribution as shown in the following table:\n\n| Platform          | Location   |\n| ----------------- | ---------- |\n| Debian and Ubuntu | /etc/sasl  |\n| Arch, RHEL/CentOS | /etc/sasl2 |\n\nSo for Debian systems, create the file `/etc/sasl/prosody.conf`. \nThe directory `/etc/sasl` might not yet exist.\n\n```\nsudo mkdir /etc/sasl/\n\ncat << 'EOF' |sudo tee /etc/sasl/prosody.conf > /dev/null\npwcheck_method: saslauthd\nmech_list: PLAIN\nEOF\n```\n\n:::note\nThe filename `prosody.conf`  corresponds to a value for `cyrus_application_name` \nin the Prosody config. Since we have not changed the default this has a value of `prosody`.\n\n:::\n\nThe Prosody documentation has more details on a \n[Cyrus SASL-related setup](https://prosody.im/doc/cyrus_sasl).\n\n## Set up Prosody\n\nIf you have tested the LDAP authentication successfully and enabled the `saslauthd` service, you can change Prosody's authentication to the Cyrus backend by changing the `authentication` setting in `/etc/prosody/conf.avail/$(hostname -f).cfg.lua` via the command:\n```\nsed -i -E -e \"/^ *VirtualHost \\\"$(hostname -f)\\\"/,/^ *VirtualHost/ {s/authentication ?=.*$/authentication = \\\"cyrus\\\"/}\" /etc/prosody/conf.avail/$(hostname -f).cfg.lua\n```\n\nYou might also have to add the `allow_unencrypted_plain_auth` option to allow \nplain-text passwords to be sent over the network. *This is not recommended* as it \nmakes the setup less secure. So please try without this line first and only add\nit if you have problems authenticating.\n\n```\n        authentication = \"cyrus\"\n        allow_unencrypted_plain_auth = true\n```\n\n### Set Permissions\n\nProsody will now try to access the saslauthd socket in \n`/var/run/saslauthd/` to communicate with the authentication daemon. \nThis folder only allows access to user `root` and group `sasl` while prosody \nruns as the system user/group `prosody`. \n\nThe easiest solution is to add the `sasl` group to the `prosody` user and \nrestart the service.\n\n```\nsudo adduser prosody sasl\nsudo service prosody restart\n```\n\n"
  },
  {
    "path": "docs/devops-guide/log-analyser.md",
    "content": "---\nid: devops-guide-log-analyser\ntitle: Self-Hosting Guide - Log Analyser\nsidebar_label: Log Analyser\n---\n\nWelcome to the Jitsi Meet Log Analyser project! This integration leverages Grafana Loki and OpenTelemetry to enable effective log management and analysis for Jitsi Meet components.\n\n## Overview\n\nThis project offers a streamlined setup for collecting, parsing, and visualizing log data generated by Jitsi Meet components. The integration includes:\n\n- A Docker Compose setup file (`log-analyser.yml`) for configuring Loki and OpenTelemetry Collector.\n- A Docker Compose setup file (`grafana.yml`) for configuring Grafana.\n- A unified Docker Compose command that initiates all services concurrently.\n- Instructions on configuring Grafana with Loki as a log data source for enhanced visualization and analysis.\n\n## Getting Started\n\n### Prerequisites\n\nBefore you begin, ensure you have the following installed on your system:\n\n- [Docker](https://docs.docker.com/get-docker/)\n- [Docker Compose](https://docs.docker.com/compose/install/)\n\n### Setup\n\nTo set up the project, follow these steps:\n\n1. **Clone the repository:**\n\n   Clone the Jitsi Meet repository that contains the necessary Docker Compose files.\n\n    ```bash\n    git clone https://github.com/jitsi/docker-jitsi-meet.git\n    cd docker-jitsi-meet\n    ```\n2. **Update Jitsi Meet Docker Compose Configuration:**\n\n   To enable log collection and analysis, you need to modify the `docker-compose.yml` file for Jitsi Meet components. Add the following configuration to each Jitsi service within the `docker-compose.yml` file:\n\n    ```yaml\n    logging:\n      driver: \"json-file\"\n      options:\n        labels: \"service\"\n    ```\n\n   This configuration ensures that logs are collected in JSON format and tagged with service labels, which is essential for Loki to properly ingest and index the logs.\n\n3. **Start the Docker containers:**\n\n   Use the following command to start all required services, including the Jitsi Meet components, Grafana, Loki, and OpenTelemetry:\n\n    ```bash\n    docker-compose -f docker-compose.yml -f log-analyser.yml -f grafana.yml up -d\n    ```\n\n   - **Explanation:**\n      - The command combines multiple Docker Compose files to launch the entire stack:\n         - `docker-compose.yml` launches the Jitsi Meet components.\n         - `log-analyser.yml` sets up the log analysis tools, including Loki and OpenTelemetry.\n         - `grafana.yml` initializes Grafana for log visualization.\n      - Logs generated by the Jitsi Meet components are automatically forwarded to Loki via the OpenTelemetry Collector, making them available for analysis in Grafana.\n\n   - **Note:** If you only want to use Grafana for log visualization without the log analysis tools, you can run `grafana.yml` independently. However, for the complete log analysis setup, both `log-analyser.yml` and `grafana.yml` are required.\n\n### Access Grafana\n\nAfter starting the services, follow these steps to access Grafana:\n\n1. **Open Grafana in your web browser:**\n\n   Navigate to [http://localhost:3000](http://localhost:3000) to access the Grafana interface.\n\n2. **Log in to Grafana:**\n\n   Use the default login credentials provided below:\n\n   - **Username:** `admin`\n   - **Password:** `admin`\n\n   (It is recommended to change these credentials after the first login for security purposes.)\n\n### Pre-configured Dashboards\n\nGrafana comes with several pre-configured dashboards specifically designed for monitoring different Jitsi Meet components. These dashboards will be automatically available once you log in to Grafana.\n\n**Important❗️:** For data to appear in these dashboards, logs need to be generated by the Jitsi Meet components. Here are the available dashboards:\n\n- **Jicofo Dashboard:** Visualizes logs related to Jitsi Conference Focus (Jicofo), which handles media and signaling in Jitsi Meet.\n- **JVB Dashboard:** Focuses on Jitsi Videobridge (JVB) logs, showing details on video streaming and performance metrics.\n- **Prosody Dashboard:** Monitors Prosody, the XMPP server used by Jitsi Meet for signaling.\n- **Web Dashboard:** Displays logs and metrics related to the web frontend of Jitsi Meet.\n- **Jitsi All Dashboard:** A comprehensive dashboard that aggregates logs from all Jitsi Meet components.\n\n\n![Jitsi Meet Log Analyser Dashboard](https://github.com/user-attachments/assets/2e54833e-4906-4429-81ce-7e56b7bf38d1)\n\n### Filtering Logs with LogQL\n\nBeyond the pre-configured dashboards, you can explore and filter logs in Grafana's \"Explore\" section using LogQL, a powerful query language designed for Loki. Here’s how you can filter and analyze logs:\n\n1. **Access the Explore section:**\n\n   In Grafana, navigate to the **Explore** tab from the left sidebar. This section allows you to run queries on your logs in real-time.\n\n2. **Select the Loki Data Source:**\n\n   In the Explore section, ensure that the **Data Source** is set to **Loki**. This is essential because Loki is the backend that stores and manages the log data collected from Jitsi Meet components.\n\n3. **Using LogQL for Filtering:**\n\n   LogQL enables you to create complex queries to filter specific logs. For example, the following LogQL query filters logs for the `jitsi-jicofo` component and parses them:\n\n    ```logql\n    {exporter=\"OTLP\"} | json | attributes_attrs_service=\"jitsi-jicofo\"\n    ```\n\n   - **Explanation:**\n      - `{exporter=\"OTLP\"}`: Selects logs that are exported via OpenTelemetry.\n      - `| json`: Parses the log data as JSON, making attributes accessible for filtering.\n      - `attributes_attrs_service=\"jitsi-jicofo\"`: Filters logs where the `attributes_attrs_service` field equals `\"jitsi-jicofo\"`.\n\n   You can modify the query to filter logs from other components or adjust the criteria according to your needs.\n\n   For more details and advanced usage of LogQL, you can refer to the official [LogQL documentation](https://grafana.com/docs/loki/latest/logql/) from Grafana.\n\n\n   ![LogQL Query Example](https://github.com/user-attachments/assets/f75c77a6-6f7b-41ae-9efe-855a5b426bdf)\n\n## Usage\n\nOnce the setup is complete, you can start exploring your log data in Grafana. The pre-configured dashboards provide an insightful visualization of the logs collected from Jitsi Meet components. Use these dashboards to:\n\n- **Parse Logs:** View detailed logs collected from various components.\n- **Visualize Logs:** Analyze log data through various charts, graphs, and panels to gain insights into system performance and issues.\n\n### Customizing Dashboards\n\nWhile the pre-configured dashboards provide a solid starting point, you may want to customize them or create new dashboards to suit your specific needs. Here's how you can do that:\n\n1. **Create a New Dashboard:**\n   - Go to the Grafana home page, click on the \"+\" icon on the left sidebar, and select \"Dashboard.\"\n   - Add panels to visualize different metrics or logs by selecting the appropriate data source (Loki) and using LogQL queries.\n\n2. **Customize Existing Dashboards:**\n   - Navigate to an existing dashboard and click on the \"Edit\" button (pencil icon) on any panel.\n   - Adjust the LogQL query, visualization type, and panel settings to match your requirements.\n\n3. **Save and Share Dashboards:**\n   - After customizing, save the dashboard. You can also export it as a JSON file to share with others or for backup.\n   - To export the dashboard:\n      - Click on the dashboard title to open the options menu.\n      - Select **\"Dashboard settings\"** > **\"JSON Model\"**.\n      - Click **\"Download JSON\"** to save the file locally.\n\n4. **Contribute to Jitsi Meet Dashboards:**\n   - If you've created or customized a dashboard that could benefit the wider Jitsi community, you can contribute by updating the relevant dashboard JSON file.\n   - Here’s how to do it:\n      - Export the JSON file of your customized dashboard as described above.\n      - Locate the corresponding Jitsi component dashboard JSON file in the repository (e.g., `jicofo-dashboard.json`, `jvb-dashboard.json`).\n      - Update the existing JSON file with your changes.\n      - Submit a pull request to the repository with the updated JSON file and a brief description of the changes you made.\n   - This helps improve the pre-configured dashboards and makes your contributions available to all users.\n\n5. **Grafana Provisioning:**\n   - The Jitsi Meet Log Analyser uses Grafana provisioning to manage dashboards. When you update a dashboard JSON file in the repository, it will be automatically provisioned in Grafana when the stack is deployed.\n   - This ensures that everyone using the repository gets the latest version of the dashboards without needing to manually import them.\n\nBy following these steps, you can not only customize your own monitoring setup but also contribute improvements back to the Jitsi community.\n\n### Troubleshooting\n\nIf you encounter issues while setting up or using the Jitsi Meet Log Analyser, here are some common problems and their solutions:\n\n1. **Grafana Not Starting:**\n    - Check if the Grafana container is running with `docker ps` and inspect logs using `docker logs grafana` for any errors.\n\n2. **No Logs in Grafana Dashboards:**\n   - Ensure that Jitsi Meet components are generating logs. Clear browser cache, reload Grafana. Ensure OpenTelemetry, Loki, and Grafana containers are all running with `docker ps`, and inspect each container's logs for issues using `docker logs <container_name>`.\n\n3. **OpenTelemetry Collector Not Forwarding Logs:**\n   - Check OpenTelemetry's logs with `docker logs otel`, ensure it's connected to the correct endpoints, and verify the log format is correct.\n\n4. **Authentication Failures in Grafana:**\n    - Restart Grafana with `docker restart grafana otel`. If still unsuccessful, delete the data volume with `docker-compose down -v` and restart to reset to default credentials (admin/admin).\n \n5. **Slow Queries:**\n   - If LogQL queries are slow, try optimizing the query in Grafana.\n\n6. **Permission Issues:**\n   - If you encounter permission issues, make sure that Docker has the necessary access rights to the directories where logs are stored.\n\n7. **Docker Network Issues:**\n    - Verify Docker network connections, IP range, and restart the network if necessary.\n\n8. **OpenTelemetry Collector Not Forwarding Logs:**\n    - Check OpenTelemetry logs, verify configuration, and ensure log format compatibility.\n\n9. **Docker Containers Failing to Start:**\n    - Use `docker-compose logs` to view detailed startup errors, and check for common issues like incorrect configurations.\n\n## Acknowledgements\n\nWe appreciate your interest in the Jitsi Meet Log Analyser project! If you encounter any issues or have questions, feel free to reach out to the project maintainers or contribute to the repository.\n"
  },
  {
    "path": "docs/devops-guide/opensuse.md",
    "content": "---\nid: devops-guide-opensuse\ntitle: Self-Hosting Guide - openSUSE\nsidebar_label: openSUSE\n---\n\nThis document describes the steps for a quick Jitsi-Meet installation, paired\nwith a single Videobridge and a single Jicofo on openSUSE Leap 15.2.\n\n:::note\nMany of the installation steps require root access.\n:::\n\n## Installation\n\n1. Add the OBS repository:  \n   __Note:__ When Jitsi-Meet is merged into openSUSE Factory, this will be obsolete.\n\n```shell\nzypper addrepo https://download.opensuse.org/repositories/home:/SchoolGuy:/jitsi/openSUSE_Leap_15.2/home:SchoolGuy:jitsi.repo\n```\n\n2. Refresh the repositories:\n\n```shell\nzypper refresh\n```\n\n3. Install Jitsi-Meet and its dependencies:\n\n```shell\nzypper install nginx prosody lua51-zlib jitsi-meet jitsi-videobridge jitsi-jicofo\n```\n\n### optional Add-Ons\n\n* Install the Jibri Add-On: `zypper install jitsi-jibri`\n* Install the Jigasi Add-On: `zypper install jitsi-jigasi`\n\n## Configuration\n\nThe following sections describe how to configure the different packages.  \nReplace `<FQDN>` with your domain name and `YOURSECRET3` with a strong password.\n\n### Prosody\n\n* Open and adjust the Prosody configuration file under `/etc/prosody/prosody.cfg.lua`:\n\n```lua\n---------- Server-wide settings ----------\nadmins = { \"focus@auth.<FQDN>\" }\ncross_domain_bosh = true;\nmodules_enabled = {\n        -- HTTP modules\n                \"bosh\"; -- Enable BOSH clients, aka \"Jabber over HTTP\"\n        -- jitsi\n                \"smacks\";\n                \"mam\";\n                \"lastactivity\";\n                \"offline\";\n                \"pubsub\";\n                \"adhoc\";\n                \"websocket\";\n                \"http_altconnect\";\n                \"compression\";\n}\n```\n\n* Create a new configuration file named `<FQDN>.cfg.lua` in `/etc/prosody/conf.avail/`\n  with the following content:\n\n```lua title=\"/etc/prosody/conf.avail/meet.example.org.cfg.lua\"\nplugin_paths = { \"/usr/share/jitsi-meet/prosody-plugins/\" }\n\n-- As per https://prosody.im/doc/setting_up_bosh#proxying_requests\nconsider_bosh_secure = true\n\n-- domain mapper options, must at least have domain base set to use the mapper\nmuc_mapper_domain_base = \"<FQDN>\";\n\nturncredentials_secret = \"YOURSECRET3\";\n\nturncredentials = {\n  { type = \"stun\", host = \"<FQDN>\", port = \"3478\" },\n  { type = \"turn\", host = \"<FQDN>\", port = \"3478\", transport = \"udp\" },\n  --  { type = \"turns\", host = \"<FQDN>\", port = \"443\", transport = \"tcp\" }\n};\n\nVirtualHost \"<FQDN>\"\n    authentication = \"anonymous\"\n    ssl = {\n        key = \"/var/lib/prosody/<FQDN>.key\";\n        certificate = \"/var/lib/prosody/<FQDN>.crt\";\n    }\n    speakerstats_component = \"speakerstats.<FQDN>\"\n    conference_duration_component = \"conferenceduration.<FQDN>\"\n    modules_enabled = {\n        \"bosh\";\n        \"pubsub\";\n\t    \"speakerstats\";\n\t    \"turncredentials\";\n        \"conference_duration\";\n    }\n    c2s_require_encryption = false\n\nComponent \"conference.<FQDN>\" \"muc\"\n    modules_enabled = {\n        \"muc_meeting_id\";\n        \"muc_domain_mapper\";\n    }\n    admins = { \"focus@auth.<FQDN>\" }\n    muc_room_locking = false\n    muc_room_default_public_jids = true\n\n-- internal muc component\nComponent \"internal.auth.<FQDN>\" \"muc\"\n    modules_enabled = {\n      \"ping\";\n    }\n    admins = { \"focus@auth.<FQDN>\" }\n    muc_room_locking = false\n    muc_room_default_public_jids = true\n    muc_room_cache_size = 1000\n\nComponent \"jitsi-videobridge.<FQDN>\"\n    component_secret = \"YOURSECRET3\"\n\nVirtualHost \"auth.<FQDN>\"\n    ssl = {\n        key = \"/var/lib/prosody/auth.<FQDN>.key\";\n        certificate = \"/var/lib/prosody/auth.<FQDN>.crt\";\n    }\n    authentication = \"internal_plain\"\n\nComponent \"focus.<FQDN>\"\n    component_secret = \"YOURSECRET3\"\n\nComponent \"speakerstats.<FQDN>\" \"speakerstats_component\"\n    muc_component = \"conference.<FQDN>\"\n\nComponent \"conferenceduration.<FQDN>\" \"conference_duration_component\"\n    muc_component = \"conference.<FQDN>\"\n```\n\n* Create a symlink for the configuration:  \n`ln --symbolic /etc/prosody/conf.avail/<FQDN>.cfg.lua /etc/prosody/conf.d/<FQDN>.cfg.lua`\n\n* Create the certificates via `prosodyctl cert generate <DOMAIN>`.  \nThe value `<DOMAIN>` represents the following URLs.\n\n    * `auth.<FQDN>`\n    * `conference.<FQDN>`\n    * `conferenceduration.<FQDN>`\n    * `internal.auth.<FQDN>`\n    * `FQDN`\n    * `focus.<FQDN>`\n    * `jitsi-videobridge.<FQDN>`\n    * `callcontrol.<FQDN>` __Note:__ This is only needed if you deploy Jigasi\n    * `recorder.<FQDN>` __Note:__ This is only needed if you deploy Jibri\n* `/var/lib/prosody/`: Symlink all generated `*.crt` and `*.key` files to `/etc/prosody/certs/`.  \n\n:::note\nPlease do not link other certificates.\n:::\n\n* Add the certificates to the system keystore:\n    * `ln --symbolic --force /var/lib/prosody/auth.<FQDN>.crt /usr/local/share/ca-certificates/auth.<FQDN>.crt`\n    * `update-ca-certificates --fresh`\n* Create conference focus user: `prosodyctl register focus auth.<FQDN> YOURSECRET3`\n\n### Nginx\n\nEdit the file `jitsi-meet.conf` in `/etc/nginx/vhosts.d/` (which was installed\nalong with `jitsi-meet`) and do the following:\n\n* Check the `server_name` value.\n* Check the TLS certificates (Let's Encrypt for production use, Prosody for testing, for example).\n\n:::warning[Mobile apps]\nThe jitsi mobile apps _require_ a valid certificate signed by a trusted [Certificate Authority](https://en.wikipedia.org/wiki/Certificate_authority), so if you don't have TLS configured, the mobile apps won't be able to connect to your jitsi instance.\n:::\n\n:::note\nIf you are using an existing server, please make sure to adjust the websocket and bosh part, too.\n:::\n\n### Jitsi-Meet\n\n* Go to `/srv/jitsi-meet` and edit `config.js`:\n\n```js title=\"/srv/jitsi-meet/config.js\"\nvar config = {\n    hosts: {\n        domain: '<FQDN>',\n        muc: 'conference.<FQDN>',\n        bridge: 'jitsi-videobridge.<FQDN>',\n        focus: 'focus.<FQDN>'\n    },\n    useNicks: false,\n    bosh: '//<FQDN>/http-bind',\n};\n```\n\n:::note\nPlease be aware that this is the minimal configuration.\n:::\n\n### Jitsi-Videobridge\n\n:::note\nWe use a combination of the [new Videobridge configuration](https://github.com/jitsi/jitsi-videobridge/blob/master/doc/muc.md#videobridge-configuration)\nand the legacy one with the `sip-communicator.properties` file. We have\nto do this because of the `STATISTICS_TRANSPORT` property.\n:::\n\nIf we remove `org.jitsi.videobridge.STATISTICS_TRANSPORT=muc,colibri`\nfrom `sip-communicator.properties`, the videobridge will not work!\n\n* Go to the directory `/etc/jitsi/videobridge`\n* Edit the file `jitsi-videobridge.conf`\n    * Set `JVB_HOSTNAME` to your `<FQDN>`.\n    * Set `JVB_SECRET` to your own secret.\n* Edit the file `application.conf` and adjust the values under `apis`\n  and `websockets`, especially set a unique ID as `muc_nickname`\n  with `uuidgen` for example.\n\n```HUCON\napis {\n    xmpp-client {\n      configs {\n        xmpp-server-1 {\n          hostname=\"localhost\"\n          domain = \"auth.${FQDN}\"\n          username = \"focus\"\n          password = \"YOURSECRET3\"\n          muc_jids = \"JvbBrewery@internal.auth.${FQDN}\"\n          # The muc_nickname must be unique across all jitsi-videobridge instances\n          muc_nickname = \"unique-id\"\n          disable_certificate_verification = true\n        }\n      }\n    }\n}\nwebsockets {\n  enabled=true\n  server-id=\"default-id\"\n  domain=\"${FQDN}\"\n}\n```\n\n### Jitsi-Jicofo\n\n* Go to the directory `/etc/jitsi/jicofo`\n* Edit the file `jitsi-jicofo.conf`\n    * Set the property `JICOFO_HOSTNAME` to `<FQDN>`.\n    * Set the property `JICOFO_SECRET` to the password the Prosody user got in above setup.\n    * Set the property `JICOFO_AUTH_DOMAIN` to `auth.<FQDN>`.\n    * Set the property `JICOFO_AUTH_USER` to the Prosody user from above setup.\n* Edit the file `sip-cmmunicator.properties`\n    * Set the property `org.jitsi.jicofo.BRIDGE_MUC` to `JvbBrewery@internal.auth.<FQDN>`.\n    * Set the property `org.jitsi.jicofo.jibri.BREWERY` to `JibriBrewery@internal.auth.<FQDN>`.\n    * Depending on your cert setup set `org.jitsi.jicofo.ALWAYS_TRUST_MODE_ENABLED` to `true` or `false`.\n\n## Add-On: Jitsi-Jibri\n\n* Add to the file `/etc/prosody/conf.avail/<FQDN>.cfg.lua` the following snippet at the end of the file.\n\n```lua\nVirtualHost \"recorder.<FQDN>\"\n  modules_enabled = {\n    \"ping\";\n  }\n  authentication = \"internal_plain\"\n```\n\n* Run `prosodyctl register jibri auth.<FQDN> YOURSECRET3` and replace `YOURSECRET3` with an appropiate one.\n* `prosodyctl register recorder recorder.<FQDN> YOURSECRET3` and replace `YOURSECRET3` with an appropiate one.\n* Go to the directory `/etc/jitsi/jibri` and edit the following properties you see listed below. The rest can be left as is.\n\n```HUCON\njibri{\n    api{\n        environments = [\n            {\n                xmpp-domain = \"<FQDN>\"\n                control-muc {\n                    domain = \"internal.<FQDN>\"\n                }\n                control-login {\n                    domain = \"recorder.<FQDN>\"\n                    username = \"recorder\"\n                    password = \"YOURSECRET3\"\n                }   \n                call-login {\n                    domain = \"recorder.<FQDN>\"\n                    username = \"recorder\"\n                    password = \"YOURSECRET3\"\n                }\n            }\n        ]\n    }\n}\n```\n\n* Edit the file `/etc/jitsi/jicofo/sip-communicator.properties` and add the\n  following properties:\n\n```HUCON\norg.jitsi.jicofo.jibri.BREWERY=JibriBrewery@internal.auth.<FQDN>\norg.jitsi.jicofo.jibri.PENDING_TIMEOUT=90\n```\n\n* Edit the file `/srv/jitsi-meet/config.js` and set the\n  following properties:\n\n```js\nfileRecordingsEnabled: true, // If you want to enable file recording\nliveStreamingEnabled: true, // If you want to enable live streaming\nhiddenDomain: 'recorder.<FQDN>',\n```\n\n* Edit `/srv/jitsi-meet/interface_config.js` and make sure the\n  `TOOLBAR_BUTTONS` array contains the `recording` and\n  the `livestreaming` value if you want those features.\n\n```js\nTOOLBAR_BUTTONS: [\n        'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',\n        'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',\n        'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',\n        'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',\n        'tileview', 'videobackgroundblur', 'download', 'help', 'mute-everyone', 'security'\n],\n```\n\n## Add-On: Jitsi-Jigasi\n\n:::note[Note from openSUSE packagers:]\nWe've packaged it but we don't have the infrastructure to set up this component. Hence we can't provide a guide for this so far.\n:::\n\n## Services\n\nNow everything should be working. That means you are ready to start everything up:\n\n1. `systemctl start prosody`\n1. `systemctl start jitsi-videbridge`\n1. `systemctl start jitsi-jicofo`\n1. `systemctl start jitsi-jibri` (if configured and installed beforehand)\n1. `systemctl start jitsi-jigasi` (if configured and installed beforehand)\n1. `systemctl start nginx`\n\n## Final notes\n\n* The Jitsi Software has a lot of dependencies and thus we recommend to run\n  this on a dedicated host for Jitsi.\n* Updating Jitsi is crucial to get rid of bugs and updated dependencies with\n  possible security fixes.\n* Although tempted through Chrome: Don't install a full X11 stack like KDE or\n  Gnome for this.\n* Don't mix the `rpms` or `debs` with a source installation of the same component.\n* Securely backup your configuration, preferably in a VCS. This saves time and\n  pain when doing rollbacks or dealing with other problems.\n"
  },
  {
    "path": "docs/devops-guide/quickstart.md",
    "content": "---\nid: devops-guide-quickstart\ntitle: \"Self-Hosting Guide - Debian/Ubuntu server\"\nsidebar_label: \"Debian/Ubuntu server\"\n---\n\nFollow these steps for a quick Jitsi-Meet installation on a Debian-based GNU/Linux system.\nThe following distributions are supported out-of-the-box:\n- Debian 11 (Bullseye) or newer\n- Ubuntu 22.04 (Jammy Jellyfish) or newer\n\n:::note\nMany of the installation steps require `root` or `sudo` access. So it's recommended to have `sudo`/`root` access to your system.\n:::\n\n## Required packages and repository updates\n\nYou will need the following packages:\n* `gnupg2`\n* `nginx-full`\n* `sudo` => **Only needed if you use `sudo`**\n* `curl` => **Or** `wget` **to [Add the Jitsi package repository](#add-the-jitsi-package-repository)**\n\n:::note Note\nOpenJDK 17 must be used.\n:::\n\nMake sure your system is up-to-date and required packages are installed:\n\nRun as `root` or with `sudo`:\n\n```bash\n# Retrieve the latest package versions across all repositories\nsudo apt update\n\n# Ensure support for apt repositories served via HTTPS\nsudo apt install apt-transport-https\n```\n\nOn Ubuntu systems, Jitsi requires dependencies from Ubuntu's `universe` package repository.  To ensure this is enabled, run this command:\n\n```bash\nsudo add-apt-repository universe\n```\n\nRetrieve the latest package versions across all repositories:\n```bash\nsudo apt update\n```\n\n## Install Jitsi Meet\n\n### Domain of your server and set up DNS\n\nDecide what domain your server will use. For example, `meet.example.org`.\n\nSet a DNS A record for that domain, using:\n- your server's public IP address, if it has its own public IP; or\n- the public IP address of your router, if your server has a private (RFC1918) IP address (e.g. 192.168.1.2) and connects through your router via Network Address Translation (NAT).\n\nIf your computer/server or router has a dynamic IP address (the IP address changes constantly), you can use a dynamic dns-service instead. Example [DuckDNS](https://www.duckdns.org/).\n\nDNS Record Example:\n\n| **Record Type** | **Hostname** | **Public IP** | **TTL (Seconds)** |\n|:---:|:---:|:---:|:---:|\n| `A` | `meet.example.org` | Your Meeting Server Public IP (`x.x.x.x`) | `1800` |\n\n### Set up the Fully Qualified Domain Name (FQDN) (optional)\n\nIf the machine used to host the Jitsi Meet instance has a FQDN (for example `meet.example.org`) already set up in DNS, you can set it with the following command:\n\n```bash\nsudo hostnamectl set-hostname meet.example.org\n```\n\nThen add the same FQDN in the `/etc/hosts` file:\n\n    127.0.0.1 localhost\n    x.x.x.x meet.example.org\n\n:::note\n`x.x.x.x` is your server's public IP address.\n:::\n\nFinally on the same machine test that you can ping the FQDN with:\n\n`ping \"$(hostname)\"`\n\nIf all worked as expected, you should see:\n`meet.example.org`\n\n### Add the Prosody package repository\n\nThis will add the Prosody repository so that an up to date Prosody is installed, which is necessary for features including the lobby feature.\n\n```bash\nsudo curl -sL https://prosody.im/files/prosody-debian-packages.key -o /usr/share/keyrings/prosody-debian-packages.key\necho \"deb [signed-by=/usr/share/keyrings/prosody-debian-packages.key] http://packages.prosody.im/debian $(lsb_release -sc) main\" | sudo tee /etc/apt/sources.list.d/prosody-debian-packages.list\nsudo apt install lua5.2\n```\n\n### Add the Jitsi package repository\n\nThis will add the jitsi repository to your package sources to make the Jitsi Meet packages available.\n\n```bash\ncurl -sL https://download.jitsi.org/jitsi-key.gpg.key | sudo sh -c 'gpg --dearmor > /usr/share/keyrings/jitsi-keyring.gpg'\necho \"deb [signed-by=/usr/share/keyrings/jitsi-keyring.gpg] https://download.jitsi.org stable/\" | sudo tee /etc/apt/sources.list.d/jitsi-stable.list\n```\n\nUpdate all package sources:\n\n```bash\nsudo apt update\n```\n\n### Setup and configure your firewall\n\nThe following ports need to be open in your firewall, to allow traffic to the Jitsi Meet server:\n\n* `80 TCP` => For SSL certificate verification / renewal with Let's Encrypt. **Required**\n* `443 TCP` => For general access to Jitsi Meet. **Required**\n* `10000 UDP` => For General Network Audio/Video Meetings. **Required**\n* `22 TCP` => For Accessing your Server using SSH (change the port accordingly if it's not 22). **Required**\n* `3478 UDP` => For querying the stun server (coturn, optional, needs `config.js` change to enable it).\n* `5349 TCP` => For fallback network video/audio communications over TCP (when UDP is blocked for example), served by coturn. **Required**\n\nIf you are using `ufw`, you can use the following commands:\n\n```bash\nsudo ufw allow 80/tcp\nsudo ufw allow 443/tcp\nsudo ufw allow 10000/udp\nsudo ufw allow 22/tcp\nsudo ufw allow 3478/udp\nsudo ufw allow 5349/tcp\nsudo ufw enable\n```\n\nCheck the firewall status with:\n\n```\nsudo ufw status verbose\n```\n\n#### Using SSH\nFor more details on using and hardening SSH access, see the corresponding [Debian](https://wiki.debian.org/SSH) or [Ubuntu](https://help.ubuntu.com/community/SSH/OpenSSH/Configuring) documentation.\n\n#### Forward ports via your router\n\nIf you are running Jitsi Meet on a server [behind NAT](https://jitsi.github.io/handbook/docs/faq#how-to-tell-if-my-server-instance-is-behind-nat), forward the ports on your router to your server's IP address.\n\n_Note_: if participants cannot see or hear each other, double check your firewall / NAT rules.\n\n### TLS Certificate\n\nIn order to have encrypted communications, you need a [TLS certificate](https://en.wikipedia.org/wiki/Transport_Layer_Security). \n\nDuring installation of Jitsi Meet you can choose between different options:\n\n1. The recommended option is to choose Let's Encrypt Certificate option\n\n2. But if you want to use a different certificate you should get that certificate first and then install jitsi-meet and choose ___I want to use my own certificate___.\n\n3. You could also use the self-signed certificate(___Generate a new self-signed certificate___) but this is not recommended for the following reasons:\n\n    * Using a self-signed certificate will result in warnings being shown in your users browsers, because they cannot verify your server's identity.\n\n\t* Jitsi Meet mobile apps *require* a valid certificate signed by a trusted [Certificate Authority](https://en.wikipedia.org/wiki/Certificate_authority) and will not be able to connect to your server if you choose a self-signed certificate.\n\n### Install Jitsi Meet\n\n_Note_: The installer will check if [Nginx](https://nginx.org/) or [Apache](https://httpd.apache.org/) are present (in that order) and configure a virtual host within the web server it finds to serve Jitsi Meet.\n\nIf you are already running Nginx on port 443 on the same machine, turnserver configuration will be skipped as it will conflict with your current port 443.\n\n\n```bash\n# jitsi-meet installation\nsudo apt install jitsi-meet\n```\n\n**SSL/TLS certificate generation:**\nYou will be asked about SSL/TLS certificate generation. \nSee [above](#tls-certificate) for details.\n\n**Hostname:**\nYou will also be asked to enter the hostname of the Jitsi Meet instance. If you have a domain, use the specific domain name, for example:\n`meet.example.org`.\nAlternatively you can enter the IP address of the machine (if it is static or doesn't change).\n\nThis hostname will be used for virtualhost configuration inside Jitsi Meet and also, you and your correspondents will be using it to access the web conferences.\n\n### Access Control\n\n**Jitsi Meet server:**\n_Note_: By default, anyone who has access to your Jitsi Meet server will be able to start a conference: if your server is open to the world, anyone can have a chat with anyone else. \nIf you want to limit the ability to start a conference to registered users, follow the instructions to set up a [secure domain](https://jitsi.github.io/handbook/docs/devops-guide/secure-domain/).\n\n**Conferences/Rooms:**\nThe access control for conferences/rooms is managed in the rooms, you can set a password on the webpage of the specific room after creation.\nSee the User Guide for details: https://jitsi.github.io/handbook/docs/user-guide/user-guide-start-a-jitsi-meeting\n\n### Advanced configuration\n\nIf the installation is on a machine [behind NAT](https://jitsi.github.io/handbook/docs/faq#how-to-tell-if-my-server-instance-is-behind-nat) jitsi-videobridge should configure itself automatically on boot. If three way calls do not work, further configuration of jitsi-videobridge is needed in order for it to be accessible from outside.\n\nProvided that all required ports are routed (forwarded) to the machine that it runs on. By default these ports are TCP/443 and UDP/10000.\n\nAdd a static mapping to the `ice4j.harvest.mapping` section in `/etc/jitsi/videobridge/jvb.conf`:\n\n```\nice4j {\n  harvest {\n    mapping {\n      static-mappings = [\n        {\n          local-address = \"<Local.IP.Address>\"\n          public-address = \"<Public.IP.Address>\"\n        }\n      ]\n    }\n  }\n}\n```\n\nSee [the documentation of ice4j](https://github.com/jitsi/ice4j/blob/4f1329607cdcfd9ea13c0a5e7e099205775f7a0b/src/main/resources/reference.conf#L91)\nfor details.\n\n**Systemd/Limits:**\nDefault deployments will have low values for maximum processes and open files. For greater than 100 participants, change `/etc/systemd/system.conf` to:\n```\nDefaultLimitNOFILE=65000\nDefaultLimitNPROC=65000\nDefaultTasksMax=65000\n```\n\nTo check values just run:\n```\nsystemctl show --property DefaultLimitNPROC\nsystemctl show --property DefaultLimitNOFILE\nsystemctl show --property DefaultTasksMax\n```\n\nTo load the values and check them see [below](#systemd-details) for details.\n\n##### Systemd details\nTo reload the systemd changes on a running system execute `sudo systemctl daemon-reload` and `sudo systemctl restart jitsi-videobridge2`.\nTo check the tasks part execute `sudo systemctl status jitsi-videobridge2` and you should see `Tasks: XX (limit: 65000)`.\nTo check the files and process part execute ```cat /proc/`cat /var/run/jitsi-videobridge/jitsi-videobridge.pid`/limits``` and you should see:\n```\nMax processes             65000                65000                processes\nMax open files            65000                65000                files\n```\n\n### Confirm that your installation is working\n\nLaunch a web browser (such as Firefox, Chrome or Safari) and enter the hostname or IP address from the previous step into the address bar.\n\nIf you used a self-signed certificate (as opposed to using Let's Encrypt), your web browser will ask you to confirm that you trust the certificate. If you are testing from the iOS or Android app, it will probably fail at this point, if you are using a self-signed certificate.\n\nYou should see a web page prompting you to create a new meeting.  \nMake sure that you can successfully create a meeting and that other participants are able to join the session.\n\nIf this all worked, then congratulations!  You have an operational Jitsi conference service.\n\n\n## Uninstall\n\n```bash\nsudo apt purge jigasi jitsi-meet jitsi-meet-web-config jitsi-meet-prosody jitsi-meet-turnserver jitsi-meet-web jicofo jitsi-videobridge2\n```\n\nSometimes the following packages will fail to uninstall properly:\n\n- jigasi\n- jitsi-videobridge\n\nWhen this happens, just run the uninstall command a second time and it should be ok.\n\nThe reason for the failure is that sometimes the uninstall script is faster than the process that stops the daemons. The second run of the uninstall command fixes this, as by then the jigasi or jitsi-videobridge daemons are already stopped.\n\n\n## Debugging problems\n\n* Web Browser:\nYou can try to use a different web browser. Some versions of some browsers are known to have issues with Jitsi Meet. \n\n* WebRTC, Webcam and Microphone:\nYou can also visit https://webrtc.github.io/samples/src/content/getusermedia/gum to test your browser's [WebRTC](https://en.wikipedia.org/wiki/WebRTC) support.\n\n* Firewall:\nIf participants cannot see or hear each other, double check your firewall / NAT rules.\n\n* Nginx/Apache:\nAs we prefer the usage of Nginx as webserver, the installer checks first for the presence of Nginx and then for Apache. In case you desperately need to enforce the usage of apache, try pre-setting the variable `jitsi-meet/enforce_apache` for package `jitsi-meet-web-config` on debconf.\n\n* Log files:\nTake a look at the various log files:\n\n```\n/var/log/jitsi/jvb.log\n/var/log/jitsi/jicofo.log\n/var/log/prosody/prosody.log\n```\n\n## Additional Functions\n\n### Adding sip-gateway to Jitsi Meet\n\n#### Install Jigasi\n\nJigasi is a server-side application acting as a gateway to Jitsi Meet conferences. It allows regular [SIP](https://en.wikipedia.org/wiki/Session_Initiation_Protocol) clients to join meetings and provides transcription capabilities.\n\n```bash\nsudo apt install jigasi\n```\n\nDuring the installation, you will be asked to enter your SIP account and password. This account will be used to invite the other SIP participants.\n\n#### Reload Jitsi Meet\n\nLaunch again a browser with the Jitsi Meet URL and you'll see a telephone icon on the right end of the toolbar. Use it to invite SIP accounts to join the current conference.\n\nEnjoy!\n"
  },
  {
    "path": "docs/devops-guide/region.md",
    "content": "---\nid: devops-guide-region\ntitle: Region-based setup\n---\n\n## Configuration for region-based setup\n\nThis approach allows jicofo to recognize the region of a participant and then choose a bridge for them based on that. To set this up:\n\n* Jicofo: enable RegionBasedBridgeSelectionStrategy and set jicofo.local-region to jicofo's region\n* JVB: set videobridge.relay.region to the local region\n* prosody: enable the `jiconop`, `muc_meeting_id`, and `jitsi_session` modules and set region_name to the local region\n  \n## User region detection\n\nFor your global deployment we assume you are using some sort of DNS routing from a cloud provider. At some point on your ingress add the 'X-Proxy-Region' HTTP header with the region name to the request, and forward that to the shard. This header will be handled and sent to jicofo. Some cloud providers have a feature like this built in. If you have to add the proxy yourself, you can add a regional nginx reverse proxy or HAProxy in front of your shards that adds this header.\n\n## How it works\nOn the shard, the request to /xmpp-websocket will go through nginx. On participant join, prosody will extract the region and add it to the participant presence, which will be seen by jicofo.\n\nThat region will be in the self-presence of the participant and the client will add it to the config.deploymentInfo (for debugging purposes).\nAfter connection `jiconop` will send the client the shard and region information, both will end up in the config.deploymentInfo.\n\nThere is also a stat fired from ljm to jitsi-meet. The value of this is visible in the local user stats in the UI (when you hover over the gsm bars) - serverRegion. This is the region of the bridge that is assigned by jicofo.\n"
  },
  {
    "path": "docs/devops-guide/requirements.md",
    "content": "---\nid: devops-guide-requirements\ntitle: Requirements\n---\n\n:::note\nJitsi Meet is a real-time system.\nRequirements are very different from a web server and depend on many factors.\nMiscalculations can very easily destroy basic functionality rather than cause slow performance.\nAvoid adding other functions to your Jitsi Meet setup as it can harm performance and complicate optimizations.\n\nNote that Jitsi Meet design priorizes scalability by adding servers on using a huge server. Check Jitsi-videobridge documentation on adding several bridges to a Jitsi Meet server, and OCTO to go even beyond that (federation of Jitsi Meet servers). If you feel that you are a network and server administration newbie, don't even think of going there.\n:::\n\n# Jitsi Meet needs, by order of importance\n\n- Network link: basic speed and reliability are essential. Check speed against the provider claims using any download tool (or ftp), and\nverify latency using a tool such as iperf3.\nExact calculation is very complex and depend on many optimisations and tricks, but you should at least remember these numbers on resolution:\n180 = 200 kbits/s\n360 = 500 kbits/s\n720 (HD) = 2500 kbits/s\n4k = 10 Mbits/s\nSo don't expect to have 20 users using 4K on a server with 100Mbits/s upload and download.\nFor a friends/small organization server, 1 Gbits/s will often be enough but for a serious server 10 Gbits/s\nis advisable. Several (or many...) bridges having each a 10 Gbits/s link are used by big deployments.\n\n**These requirements concern the videobridge. If there are only external videobridges (as can be the case on high end Jitsi Meet servers), network performance matters much less.**\n\n- **RAM:** it's usually suggested to get 8 GB.\n For small meetings you can get away with 4 GB, for test servers or very small meetings you can try to use 2 GB.\n For big meetings it's suggested to go the scalable way over getting huge amounts of memory.\n\n\n- **CPU:** very low processor performance can seriously harm a real time system, especially when using a shared server (where your CPU performance can be stolen by other customers of your hoster, check on 'dedicated CPU' if you are getting a VPS, rather than a physical server). However, a consideration is that a Jitsi Meet component, Prosody, can only use ONE (1) core. So getting a lot of cores, let's say more than 32, is not always useful. For a basic server, 4 dedicated cores can be enough.\n\n- **Disk:** unless you are doing heavy logging or have very specific needs, you can get away with 20 Gbytes of standard hard disk.\nSSD are more a nice to have than a necessity.\n\n\n**If you want additional services, requirements can go up.**\n\n\n# Recording\n\nJibri needs ONE system per recording.\nOne Jibri instance = one meeting. For 5 meetings recorded simultaneously, you need 5 Jibris.\nThere is no workaround to that.\nIf you are knowledgeable, you can setup Jibris in containers and use a big server to save a bit on resources but that's about it.\n\nJibri RAM, CPU and hard disk needs are far higher than Jitsi Meet itself, as it does video encoding.\nFor `1080x720` you currently need at least 8 GB RAM, for `1280x1024` 12 GB (this is for recording a __single__  meeting).\nFor cloud storage you will need at least SSD drives.\nIf memory is not sufficient, CPU can't encode fast enough or hard disk is not fast enough, recordings will fail.\n\nWhile Jibri and Jitsi Meet can technically be hosted in a single server, it's not recommended because Jibri is a resource drain and it can harm Jitsi Meet performance, and can exhaust disk space and stop Jitsi Meet function altogether.\n\n\n\n"
  },
  {
    "path": "docs/devops-guide/reservation.md",
    "content": "---\nid: reservation\ntitle: Reservation System setup\nsidebar_label: Reservation System\n---\n\n### Support for a reservation system over REST API\n\nIt is possible to connect to an external conference reservation system using a\nREST API. Before a new Jitsi Meet conference is created, the reservation system will be\nqueried for room availability. The system is supposed to return a positive or\nnegative response code, which also contains conference duration. Prosody will enforce\nconference duration and if the time limit is exceeded the conference will be\nterminated.\n\n#### Enable reservation system\n\nIn order to enable the reservation system, the URL base for the REST API endpoint must be\n configured. Under the main virtual host in prosody, enable module \"reservations\" and \nadd the config `reservations_api_prefix`:\n\n```\nVirtualHost \"jitmeet.example.com\"\n    -- ....\n    modules_enabled = {\n        -- ....\n        \"reservations\";\n    }\n    reservations_api_prefix = \"http://reservation.example.com\"\n```\n\nThe URL base is used to construct the request URL. Currently, only `'/conference'`\nendpoint is supported, so all request will go to:\n\n```\nhttp://reservation.example.com/conference\n```\nAdditional configuration options are available:\n* \"reservations_api_timeout\" to change API call timeouts (defaults to 20 seconds)\n* \"reservations_api_headers\" to specify custom HTTP headers included in\n  all API calls e.g. to provide auth tokens.\n* \"reservations_api_retry_count\" to specify the number of times API call failures are retried (defaults to 3)\n* \"reservations_api_retry_delay\" seconds to wait between retries (defaults to 3s)\n* \"reservations_api_should_retry_for_code\" as a function that takes an HTTP response code and\n  returns true if the API call should be retried. By default, retries are done for 5XX\n  responses. Timeouts are never retried, and HTTP call failures are always retried.\n* \"reservations_enable_max_occupants\" to enable support for setting max occupants. If this is set to `true`, and if\n  the API response payload includes a \"max_occupants\" value, then that value will be set as the max occupancy limit\n  for that specific room.\n  * \"muc_max_occupants\" module must also be enabled for this to work.\n* \"reservations_enable_lobby_support\" to enable support for lobby. If this is set to `true`, and if\n  the API response payload includes a \"lobby\" field set to `true` , then the lobby will be enabled for the room.\n  * \"muc_lobby_rooms\" and \"persistent_lobby\" modules must also be enabled for this to work.\n* \"reservations_enable_password_support\" to enable support for room password. If this is set to `true`, and if\n  the API response payload includes a \"password\" value, then that value will be set as room password. Users will then\n  be required to know that password to be able to join the room, or in the case where lobby is enabled, can use the\n  password to bypass the lobby.\n\n```\n    --- The following are all optional\n    reservations_api_headers = {\n        [\"Authorization\"] = \"Bearer TOKEN-237958623045\";\n    }\n    reservations_api_timeout = 10  -- timeout if API does not respond within 10s\n    reservations_api_retry_count = 5  -- retry up to 5 times\n    reservations_api_retry_delay = 1  -- wait 1s between retries\n    reservations_api_should_retry_for_code = function (code)\n        return code >= 500 or code == 408\n    end\n    reservations_enable_max_occupants = true -- enable integration with muc_max_occupants\n    reservations_enable_lobby_support = true -- enable integration with muc_lobby_rooms\n    reservations_enable_password_support = true -- enable support for setting room passwords\n```\n\n#### Call flow\n\n##### Notes\n\nAll API calls use the following datetime format:\n\n`yyyy-MM-dd'T'HH:mm:ss.SSSX` - more info can be found in\n`SimpleDateFormat` [JavaDoc]\n\n[JavaDoc]: https://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html\n\n##### Conference allocation\n\nWhen the first user joins a MUC room (i.e. Jitsi Meet URL is opened), an `HTTP POST`\nrequest is sent to `'/conference'` endpoint with the following parameters\nincluded:\n\n* `name (string)` - short name of the conference room (not full MUC address). If tenant is used, the name will be `[tenant]roomname`.\n* `start_time (string)` - conference start date and time\n* `mail_owner (string)` - if authentication system is enabled this field will\n contain user's identity. It that case it will not be possible to create a new\n conference room without authenticating.\n\nThe payload sent to the endpoint will be encoded as `application/x-www-form-urlencoded`.\n\nThe reservation system is expected to respond with one of the following\nresponses:\n\n###### HTTP 200 or 201 Conference created successfully\n\nIn the HTTP response, a JSON object is expected. It should contain conference `id`\nassigned by the system and `duration` measured in seconds. Sample response body:\n\n```\n{\n  \"id\": 364758328,\n  \"name\": \"conference1234\",\n  \"mail_owner\": \"user@server.com\",\n  \"start_time\": \"2048-04-20T17:55:12.000Z\",\n  \"duration\": 900000\n}\n```\n\nThe object can optionally include a `max_occupants` key with an integer value. When provided, and if\n`reservations_enable_max_occupants` is enabled, then the value will be passed to muc_mod_max_occupants to enforce\nper-room occupancy limits.\n\n\n###### HTTP 409 - Conference already exists\n\nThis is to recover from previous failures. If for some reason the conference was\nrestarted and the user tries to create the room again, this response informs Prosody\nthat the conference room already exists. It is expected to contain\n`conflict_id` in the JSON response body:\n\n```\n{\n  \"conflict_id\": 364758328\n}\n```\n\nProsody will use `HTTP GET` to fetch information about the conflicting conference for the \ngiven `conflict_id`. More info about this request can be found in the \"Reading conference info\"\nsection.\n\n###### HTTP 4xx\n\nOther response codes will cause conference creation failure. The JSON response\ncan contain a `message` object which will be sent back to the client.\n\nFor example `user1` tries to start a new conference by sending\n`conference` IQ to Jicofo. The system will reject the request.\n\nClient -> Jicofo:\n\n```\n<iq from='client1@xmpp.com' to='jicofo.meet.com' type='set'>\n  <conference xmlns='http://jitsi.org/protocol/focus' room='testroom1' />\n</iq>\n```\n\nProsody -> Reservation system:\n\n```\nPOST /conference HTTP/1.1\ncontent-type:application/x-www-form-urlencoded;charset=utf-8\nhost: http://reservation.example.com\ncontent-length: length\n\nname=testroom1&start_time=2048-04-20T17%3A55%3A12.000Z&mail_owner=client1%40xmpp.com\n```\n\nReservation system -> Prosody:\n\n```\nHTTP/1.1 403 Forbidden\nContent-Type: application/json; charset=utf-8\nContent-Length: length\n\n{\n  \"message\": \"client1 is not allowed to create the room at this time\"\n}\n```\n\nProsody -> Client:\n\n```\n<iq from='jicofo.meet.com' to='client1@xmpp.com' type='error'>\n  <error type='cancel'>\n    <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas' />\n    <text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>\n          client1 is not allowed to create the room at this time\n    </text>\n    <reservation-error xmlns='http://jitsi.org/protocol/focus' error-code='403'/>\n  </error>\n</iq>\n```\n\nThe application can use `text` and `reservation-error` elements to\nprovide meaningful information to the user.\n\n##### Reading conference info\n\nIn case of a `409` response to the `HTTP POST` request, Prosody will try\nto read information about the conflicting conference using an `HTTP GET`\n`/conference/{conflict_id}` endpoint. The response should provide all\ninformation about the conference stored in the reservation system:\n\n* `\"id\"`: conference identifier assigned by the reservation system\n* `\"name\"`: conference room name\n* `\"mail_owner\"`: identity of the user who has created the conference\n* `\"start_time\"`: conference start date and time\n* `\"duration\"`: scheduled conference duration in seconds\n\nThe optional `max_occupants` value should also be provided if applicable.\n\nSample response JSON body (contains the same info as `200 OK` to\n`HTTP POST`):\n\n```\n{\n  \"id\": 364758328,\n  \"name\": \"conference1234\",\n  \"mail_owner\": \"user@server.com\",\n  \"start_time\": \"2048-04-20T17:55:12.000Z\",\n  \"duration\": 900000\n}\n```\n\n##### Deleting conference\n\nProsody deletes conferences in the reservation system in two cases. First when\nall users leave XMPP Multi User Chat room. Secondly when the conference duration limit\nis exceeded. In the latter case Prosody will destroy the XMPP MUC room.\nAfter the MUC room is destroyed, Prosody sends an `HTTP DELETE` request to\n`'/conference/{id}'` endpoint where `{id}` is replaced with\nconference identifier assigned by the reservation system.\n\n```\nDELETE /conference/364758328 HTTP/1.1\nhost: http://reservation.example.com\n...\n```\n\n#### Implementation diagram\n\n![](https://raw.githubusercontent.com/jitsi/handbook/master/docs/assets/reservation-api.png)\n"
  },
  {
    "path": "docs/devops-guide/scalable.md",
    "content": "---\nid: devops-guide-scalable\ntitle: DevOps Guide (scalable setup)\nsidebar_label: Scalable setup\n---\n\nA single server Jitsi installation is good for a limited size of concurrent conferences.\nThe first limiting factor is the videobridge component, that handles the actual video and audio traffic.\nIt is easy to scale the video bridges horizontally by adding as many as needed.\nIn a cloud based environment, additionally the bridges can be scaled up or down as needed.\n\n:::warning\nThe [Youtube Tutorial on Scaling](https://www.youtube.com/watch?v=LyGV4uW8km8) is outdated and describes an old configuration method.\nThe current default Jitsi Meet install is already configured for horizontal scalability.\n:::\n\n:::note\nBuilding a scalable infrastructure is not a task for beginning Jitsi Administrators.\nThe instructions assume that you have installed a single node version successfully, and that\nyou are comfortable installing, configuring and debugging Linux software.\nThis is not a step-by-step guide, but will show you, which packages to install and which\nconfigurations to change.\nIt is highly recommended to use configuration management tools like Ansible or Puppet to manage the\ninstallation and configuration.\n:::\n\n## Architecture (Single Jitsi-Meet, multiple videobridges)\n\nA first step is to split the functions of the central jitsi-meet instance (with nginx, prosody and jicofo) and\nvideobridges.\n\nA simplified diagram (with open network ports) of an installation with one Jitsi-Meet instance and three\nvideobridges that are load balanced looks as follows. Each box is a server/VM.\n\n```\n               +                                       +\n               |                                       |\n               |                                       |\n               v                                       v\n          80, 443 TCP                          443 TCP, 10000 UDP\n       +--------------+                     +---------------------+\n       |  nginx       |  5222 TCP           |                     |\n       |  Jitsi Meet  |<-------------------+|  jitsi-videobridge  |\n       |  prosody     |         |           |                     |\n       |  jicofo      |         |           +---------------------+\n       +--------------+         |\n                                |           +---------------------+\n                                |           |                     |\n                                +----------+|  jitsi-videobridge  |\n                                |           |                     |\n                                |           +---------------------+\n                                |\n                                |           +---------------------+\n                                |           |                     |\n                                +----------+|  jitsi-videobridge  |\n                                            |                     |\n                                            +---------------------+\n```\n\n## Machine Sizing\n\nThe Jitsi-Meet server will generally not have that much load (unless you have many) conferences\ngoing at the same time. A 4 CPU, 8 GB machine will probably be fine.\n\nThe videobridges will have more load. 4 or 8 CPU with 8 GB RAM seems to be a good configuration.\n\n\n### Installation of Jitsi-Meet\n\nAssuming that the installation will run under the following FQDN: `meet.example.com` and you have\nSSL cert and key in `/etc/ssl/meet.example.com.{crt,key}`\n\nSet the following DebConf variables prior to installing the packages.\n(We are not installing the `jitsi-meet` package which would handle that for us)\n\nInstall the `debconf-utils` package\n\n```\n$ cat << EOF | sudo debconf-set-selections\njitsi-videobridge\tjitsi-videobridge/jvb-hostname\tstring\tmeet.example.com\njitsi-meet\tjitsi-meet/jvb-serve\tboolean\tfalse\njitsi-meet-prosody\tjitsi-videobridge/jvb-hostname\tstring\tmeet.example.com\njitsi-meet-web-config\tjitsi-meet/cert-choice\tselect\tI want to use my own certificate\njitsi-meet-web-config\tjitsi-meet/cert-path-crt\tstring\t/etc/ssl/meet.example.com.crt\njitsi-meet-web-config\tjitsi-meet/cert-path-key\tstring\t/etc/ssl/meet.example.com.key\njitsi-meet-web-config\tjitsi-meet/jaas-choice\tboolean\tfalse\nEOF\n```\n\nTo enable integration with [Jitsi Meet Components](https://jaas.8x8.vc/#/components) for telephony support, set\nthe `jitsi-meet/jaas-choice` option above to `true`.\n\nOn the jitsi-meet server, install the following packages:\n\n* `nginx`\n* `prosody`\n* `jicofo`\n* `jitsi-meet-web`\n* `jitsi-meet-prosody`\n* `jitsi-meet-web-config`\n\n### Installation of Videobridge(s)\n\nFor simplicities sake, set the same `debconf` variables as above and install\n\n* `jitsi-videobridge2`\n\n### Configuration of jitsi-meet\n\n#### Firewall\n\nOpen the following ports:\n\nOpen to world:\n\n* 80 TCP\n* 443 TCP\n\nOpen to the videobridges only\n\n* 5222 TCP (for Prosody)\n\n\n#### NGINX\n\nCreate the `/etc/nginx/sites-available/meet.example.com.conf` as usual\n\n#### Jitsi-Meet\n\nAdapt `/usr/share/jitsi-meet/config.js` and `/usr/share/jitsi-meet/interface-config.js` to your specific needs\n\n#### Jicofo\n\nNo changes necessary from the default install.\n\n### Configuration of the Videobridge\n\n#### Firewall\n\nOpen the following ports:\n\nOpen to world:\n\n* 10000 UDP (for media)\n\n#### jitsi-videobridge2\n\nNo changes necessary from the default setup.\n\n## Testing\n\nAfter restarting all services (`prosody`, `jicofo` and all the `jitsi-videobridge2`) you can see in\n`/var/log/prosody/prosody.log` and\n`/var/log/jitsi/jicofo.log` that the videobridges connect to Prososy and that Jicofo picks them up.\n\nWhen a new conference starts, Jicofo picks a videobridge and schedules the conference on it.\n"
  },
  {
    "path": "docs/devops-guide/secure-domain.md",
    "content": "---\nid: secure-domain\ntitle: Secure Domain Setup\nsidebar_label: Authentication (Secure Domain) - Deprecated\n---\n\n:::note\nThis method of authentication is deprecated and should not be used in new installations. It is recommended to use JWT authentication instead, as described in [Authentication](authentication.md).\n:::\n\nIt is possible to allow only authenticated users to create new conference rooms.\nWhenever a new room is about to be created, Jitsi Meet will prompt for a user\nname and password. After the room is created, others will be able to join from\nanonymous domain. Here's what has to be configured:\n\n## Prosody configuration\n\nIf you have installed Jitsi Meet from the Debian package, these changes should\nbe made in `/etc/prosody/conf.avail/[your-hostname].cfg.lua`\n\nIn the example below, this hostname is assumed to be `jitsi.example.com`. Update\nthis value according to your own hostname.\n\n### Enable authentication\n\nInside the `VirtualHost \"[your-hostname]\"` section, replace anonymous\nauthentication with hashed password authentication:\n\n```\nVirtualHost \"jitsi.example.com\"\n    authentication = \"internal_hashed\"\n```\n\nYou will see your own hostname instead of `jitsi.example.com` in your config\nfile.\n\n### Enable anonymous login for guests\n\nAdd this section **after the previous VirtualHost** to enable the anonymous\nlogin method for guests:\n\n```\nVirtualHost \"guest.jitsi.example.com\"\n    authentication = \"jitsi-anonymous\"\n    c2s_require_encryption = false\n```\n\n_Note that `guest.jitsi.example.com` is internal to Jitsi, and you do not need\nto (and should not) create a DNS record for it, or generate an SSL/TLS\ncertificate, or do any web server configuration. While it is internal, you\nshould still replace `jitsi.example.com` with your hostname._\n\n## Jitsi Meet configuration\n\nIn config.js, the `anonymousdomain` options has to be set.\n\nIf you have installed jitsi-meet from the Debian package, these changes should\nbe made in `/etc/jitsi/meet/[your-hostname]-config.js`.\n\n```\nvar config = {\n    hosts: {\n        domain: 'jitsi.example.com',\n        anonymousdomain: 'guest.jitsi.example.com',\n        // ...\n    },\n    // ...\n}\n```\n\nYou will see your own hostname instead of `jitsi.example.com` in your config\nfile. You should add only the `anonymousdomain` line. Be carefull of commas.\n\n## Jicofo configuration\n\nWhen running Jicofo, specify your main domain in an additional configuration\nproperty. Jicofo will accept conference allocation requests only from the\nauthenticated domain. This should go as a new `authentication` section in\n`/etc/jitsi/jicofo/jicofo.conf`:\n\n```\njicofo {\n  authentication: {\n    enabled: true\n    type: XMPP\n    login-url: \"jitsi.example.com\"\n  }\n}\n```\n\nReplace `jitsi.example.com` with your own hostname. Don't create a new `jicofo`\nsection. Create the `authentication` section inside the existing `jicofo`\nsection.\n\n## Restart the services\n\nRestart prosody, jicofo and jitsi-videobridge2 as `root`.\n\n```\nsystemctl restart prosody\nsystemctl restart jicofo\nsystemctl restart jitsi-videobridge2\n```\n\n## Create users in Prosody\n\nFinally, run `prosodyctl` to create a user in Prosody:\n\n```\nsudo prosodyctl register <username> <your-hostname> <password>\n```\n\nFor example:\n\n```\nsudo prosodyctl register myname jitsi.example.com mypassword123\n```\n\n:::note\nDocker users may require an alternate config path. Users of the official\n[`jitsi/prosody`](https://github.com/jitsi/docker-jitsi-meet) image should\ninvoke `prosodyctl` as follows.\n\n```\nprosodyctl --config /config/prosody.cfg.lua register <username> meet.jitsi <password>\n```\n\nFull documentation for `prosodyctl` can be found on\n[the official site](https://prosody.im/doc/prosodyctl).\n:::\n\n## Remove users from Prosody\n\nTo remove an existing user:\n\n```\nsudo prosodyctl unregister <username> <your-hostname>\n```\n\nFor example:\n\n```\nsudo prosodyctl unregister myname jitsi.example.com\n```\n\n## Optional: Jigasi configuration\n\n### Enable Authentication\n\nIf you are using Jigasi, set it to authenticate by editing the following lines\nin `/etc/jitsi/jigasi/sip-communicator.properties`:\n\n```\norg.jitsi.jigasi.xmpp.acc.USER_ID=SOME_USER@SOME_DOMAIN\norg.jitsi.jigasi.xmpp.acc.PASS=SOME_PASS\norg.jitsi.jigasi.xmpp.acc.ANONYMOUS_AUTH=false\n```\n\nNote that the password is the actual plaintext password, not a base64 encoding.\n\n### Debugging\n\nIf you experience problems with a certificate chain, you may need to uncomment\nthe following line, also in `sip-communicator.properties`:\n\n```\nnet.java.sip.communicator.service.gui.ALWAYS_TRUST_MODE_ENABLED=true\n```\n\n:::note\nThis should only be used for testing/debugging purposes, or in\ncontrolled environments. If you confirm that this is the problem, you should\nthen solve it in another way (e.g. get a signed certificate for Prosody, or add\nthe particular certificate to Jigasi’s trust store).\n:::\n"
  },
  {
    "path": "docs/devops-guide/speakerstats.md",
    "content": "---\nid: speakerstats\ntitle: Enabling Speaker Stats\nsidebar_label: Speaker Stats\n---\n\nTo enable the speaker stats we need to enable speakerstats module under the main\nvirtual host, this is to enable the advertising the speaker stats component, \nwhich address needs to be specified in `speakerstats_component` option.\n\nWe need to also enable the component with the address specified in `speakerstats_component`.\nThe component needs also to have the option with the muc component address in\n`muc_component` option.\n\n```lua\nVirtualHost \"jitsi.example.com\"\n    speakerstats_component = \"speakerstats.jitsi.example.com\"\n    modules_enabled = {\n        \"speakerstats\";\n    }\n\nComponent \"speakerstats.jitsi.example.com\" \"speakerstats_component\"\n    muc_component = \"conference.jitsi.example.com\"\n\nComponent \"conference.jitsi.example.com\" \"muc\"\n```\n"
  },
  {
    "path": "docs/devops-guide/token-authentication.md",
    "content": "---\nid: token-authentication\ntitle: Token Authentication\nsidebar_label: Authentication (Token)\n---\n\nIt is possible to allow only users with a valid token to create new conference\nrooms. After the room is created, others will be able to join from anonymous\ndomain. Here's what has to be configured:\n\n## Token package\n\nInstall `jitsi-meet-tokens` packages.\n\n```\napt-get install jitsi-meet-tokens\n```\n\nSet `Application ID` and `Application Secret` when asked. This command will add\n`app_id` and `app_secret` into the Prosody config and set `authentication`.\n\n## Prosody configuration\n\nIf you have installed Jitsi Meet from the Debian package, the changes should be\nmade in `/etc/prosody/conf.avail/[your-hostname].cfg.lua`\n\nIn the example below, this hostname is assumed to be `jitsi.example.com`.\n\nAfter installing the package you will see the following lines in your Prosody\nconfig:\n\n```\nVirtualHost \"jitsi.example.com\"\n    authentication = \"token\"\n    app_id=\"myappid\"\n    app_secret=\"myappsecret\"\n---\n---\n\nComponent \"conference.jitsi.example.com\" \"muc\"\n    ---\n    ---\n    modules_enabled = {\n        ---\n        ---\n        \"token_verification\";\n        ---\n        ---\n    }\n```\n\n### allow_empty_token\n\nAdd `allow_empty_token` into `VirtualHost`:\n\n```\nVirtualHost \"jitsi.example.com\"\n    authentication = \"token\"\n    app_id=\"myappid\"\n    app_secret=\"myappsecret\"\n    allow_empty_token = true\n```\n\n### persistent_lobby\n\nAdd `persistent_lobby` as module into `VirtualHost`:\n\n```\nVirtualHost \"jitsi.example.com\"\n    ---\n    ---\n    modules_enabled = {\n        ---\n        ---\n        \"muc_lobby_rooms\";\n        \"persistent_lobby\";\n```\n\n### muc_wait_for_host\n\nAdd `muc_wait_for_host` as module into `Component`:\n\n```\nComponent \"conference.jitsi.example.com\" \"muc\"\n    ---\n    ---\n    modules_enabled = {\n        ---\n        \"token_verification\";\n        \"muc_wait_for_host\";\n    }\n```\n\n### Enable anonymous login for guests\n\nAdd this section **after the previous VirtualHost** to enable the anonymous\nlogin method for guests:\n\n```\nVirtualHost \"guest.jitsi.example.com\"\n    authentication = \"jitsi-anonymous\"\n    c2s_require_encryption = false\n```\n\n_Note that `guest.jitsi.example.com` is internal to Jitsi, and you do not need\nto (and should not) create a DNS record for it, or generate an SSL/TLS\ncertificate, or do any web server configuration. While it is internal, you\nshould still replace `jitsi.example.com` with your hostname._\n\n## Jitsi Meet configuration\n\nIn config.js, the `anonymousdomain` options has to be set.\n\nIf you have installed jitsi-meet from the Debian package, these changes should\nbe made in `/etc/jitsi/meet/[your-hostname]-config.js`.\n\n```\nvar config = {\n    hosts: {\n        domain: 'jitsi.example.com',\n        anonymousdomain: 'guest.jitsi.example.com',\n        // ...\n    },\n    // ...\n}\n```\n\nYou will see your own hostname instead of `jitsi.example.com` in your config\nfile. You should add only the `anonymousdomain` line. Be carefull of commas.\n\n## Jicofo configuration\n\nNo need to update anything in Jicofo config. Some out-dated documents recommend\nto enable the authentication in `jicofo.conf`. Don't do that. The authentication\nmust be disabled in `jicofo.conf` when the `token` authentication is active.\n\nSimply keep `jicofo.conf` as it is without changing anything.\n\n## Restart the services\n\nRestart prosody, jicofo and jitsi-videobridge2 as `root`.\n\n```\nsystemctl restart prosody\nsystemctl restart jicofo\nsystemctl restart jitsi-videobridge2\n```\n"
  },
  {
    "path": "docs/devops-guide/turn.md",
    "content": "---\nid: turn\ntitle: Setting up TURN\nsidebar_label: TURN setup\n---\n\nOne-to-one calls should avoid going through the JVB for optimal performance and for optimal resource usage. This is why we've added the peer-to-peer mode where the two participants connect directly to each other. Unfortunately, a direct connection is not always possible between the participants. In those cases you can use a TURN server to relay the traffic (n.b. the JVB does much more than just relay the traffic, so this is not the same as using the JVB to \"relay\" the traffic).\n\nThis document describes how to enable TURN server support in one-to-one calls in Jitsi Meet. Even though it gives some hints how to configure [prosody](https://prosody.im) and [coTURN](https://github.com/coturn/coturn), it assumes a properly configured TURN server, and a properly configured XMPP server.\n\nOne way to configure TURN support in meet is with a static configuration. You can simply fill out the `p2p.stunServers` option with appropriate values, e.g.:\n\n```\n    [\n        { urls: 'turn:turn.example.com1', username: 'user', credential: 'pass' },\n    ]\n```\n\n:::caution\nThis technique doesn't require any special configuration on the XMPP server, but it exposes the credentials to your TURN server and other people can use your bandwidth freely, so while it's simple to implement, it's not recommended.\n:::\n\nThis [draft](https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00) describes a proposed standard REST API for obtaining access to TURN services via ephemeral (i.e. time-limited) credentials. These credentials are vend by a web service over HTTP, and then supplied to and checked by a TURN server using the standard TURN protocol. The usage of ephemeral credentials ensures that access to the TURN server can be controlled even if the credentials can be discovered by the user.\n\nJitsi Meet can fetch the TURN credentials from the XMPP server via [XEP-0215](https://xmpp.org/extensions/xep-0215.html) and this is configured by default using [mod_external_services](https://prosody.im/doc/modules/mod_external_services). The default configured turnserver uses the default ports for the protocol UDP 3478 and TCP(TLS) on 5349.\n\n## Use TURN server on port 443\n\nBy default, TURN server listens on standard ports UDP 3478 and TCP 5349 (for TLS connections). \nThere are certain corporate networks which allow only TCP connections using port 443(https) and to cover \nthis kind of scenarios it is useful to have TURN server listening on port 443 for TLS connections.\nHere is how to run nginx and TURN server on the same machine sharing port.\nFor this you will need a second DNS record for your turn domain pointing to the same machine (as a reference below we will use `turn-jitsi-meet.example.com`).\n\n- You need to enable the multiplexing based on that new DNS record. You need to create a file in `/etc/nginx/modules` or `/etc/nginx/modules-available`. If you are placing the file in `/etc/nginx/modules-available` you need to add a symlink in `/etc/nginx/modules-enabled`.\nThe file content should be:\n```\nstream {\n    map $ssl_preread_server_name $name {\n        jitsi-meet.example.com web_backend;\n        turn-jitsi-meet.example.com turn_backend;\n    }\n\n    upstream web_backend {\n        server 127.0.0.1:4444;\n    }\n\n    upstream turn_backend {\n        server __your_public_ip__:5349;\n    }\n\n    server {\n        listen 443;\n        listen [::]:443;\n\n        # since 1.11.5\n        ssl_preread on;\n\n        proxy_pass $name;\n\n        # Increase buffer to serve video\n        proxy_buffer_size 10m;\n    }\n}\n```\nMake sure you edit the file and replace `jitsi-meet.example.com` with your domain of deployment, `turn-jitsi-meet.example.com` with the DNS name you will use for the TURN server and `__your_public_ip__` with your public IP of the deployment.\nIf you have more virtual hosts make sure you add them here and do the port change for them (the next step).\n\n- Then go to `/etc/nginx/sites-available/your-conf` and change your virtual host to listen on port 4444 instead of 443.\n```\nserver {\n    listen 4444 ssl;\n    listen [::]:4444 ssl;\n    server_name jitsi-meet.example.com;\n```\n\n- Next you need to make sure Prosody is advertising the correct DNS name and port for the TURN server. You should edit the line using port `5349` in the file `/etc/prosody/conf.d/jitsi-meet.example.com.cfg.lua` and make it look like (change port and address):\n```\n{ type = \"turns\", host = \"turn-jitsi-meet.example.com\", port = \"443\", transport = \"tcp\" }\n```\n- Now you need to make sure the TURN server (coturn) uses trusted certificates.\nHere is how to request those from Let's Encrypt (make sure you set correct values for the domain and email):\n```\nsystemctl stop nginx\nDOMAIN=\"turn-jitsi-meet.example.com\"\napt install socat\n/opt/acmesh/.acme.sh/acme.sh -f --issue -d ${DOMAIN} --standalone --server letsencrypt\n/opt/acmesh/.acme.sh/acme.sh -f --install-cert \\\n    -d ${DOMAIN} \\\n    --key-file /etc/jitsi/meet/${DOMAIN}.key \\\n    --fullchain-file /etc/jitsi/meet/${DOMAIN}.crt \\\n    --reloadcmd \"/usr/share/jitsi-meet/scripts/coturn-le-update.sh ${DOMAIN}\"\nsystemctl start nginx\n``` \n- After restarting prosody (`systemctl restart prosody`) you are good to go!\n"
  },
  {
    "path": "docs/devops-guide/video-sipgw.md",
    "content": "---\nid: videosipgw\ntitle: Configuring a video SIP gateway\nsidebar_label: Video SIP gateway\n---\n\nThis document describes how you can configure jitsi-meet to use sipgw jibri and enable rooms in 'Add people dialog'\nYou will need a working deployment of jibri configured to use a regular sip video device, for more info check out the [jibri documentation](https://github.com/jitsi/jibri/blob/master/README.md).\n\nThis feature is available for non-guests of the system, so this relies on setting in config.js ``enableUserRolesBasedOnToken: true`` and providing a jwt token when accessing the conference.\n\n* Jicofo configuration:\nedit /etc/jitsi/jicofo/sip-communicator.properties (or similar), set the appropriate MUC to look for the Jibri Controllers. This should be the same MUC as is referenced in jibri's config.json file. Restart Jicofo after setting this property.\n\n```\n  org.jitsi.jicofo.jibri.SIP_BREWERY=TheSipBrewery@conference.yourdomain.com\n ```\n\n* Jitsi Meet configuration:\n - config.js: add \n```\n  enableUserRolesBasedOnToken: true,\n  peopleSearchQueryTypes: ['conferenceRooms'],\n  peopleSearchUrl: 'https://api.yourdomain.com/testpath/searchpeople',\n```\n\nThe combination of the above settings and providing a jwt token will enable a button under invite option which will show the dialog 'Add people'.\n\n## People search service\n\nWhen searching in the dialog, a request for results is made to the `peopleSearchUrl` service.\n\nThe request is in the following format:\n```\nhttps://api.yourdomain.com/testpath/searchpeople?query=testroomname&queryTypes=[%22conferenceRooms%22]&jwt=somejwt\n```\nThe parameters are:\n - query - The text entered by the user.\n - queryTypes - What type of results we want people, rooms, conferenceRooms. This is the value from config.js `peopleSearchQueryTypes`\n - jwt - The token used by the user to access the conference.\n\nThe response of the service is a json in the following format:\n```\n[\n   {\n       \"id\": \"address@sip.domain.com\",\n       \"name\": \"Some room name\",\n       \"type\": \"videosipgw\"\n   },\n  {\n      \"id\": \"address2@sip.domain.com\",\n      \"name\": \"Some room name2\",\n      \"type\": \"videosipgw\"\n  }\n]\n```\nType should be `videosipgw`, `name` is the name shown to the user and `id` is the sip address to be called by the sipgw jibri.\n"
  },
  {
    "path": "docs/devops-guide/videotutorials.md",
    "content": "---\nid: devops-guide-videotutorials\ntitle: Video Tutorials\n---\n\n## [Installing Jitsi Meet on your own Linux Server](https://jitsi.org/news/new-tutorial-installing-jitsi-meet-on-your-own-linux-server/)\n\n<figure class=\"video-container\">\n    <iframe src=\"https://www.youtube.com/embed/8KR0AhDZF2A\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</figure>\n\n## [How to Load Balance Jitsi Meet](https://jitsi.org/blog/tutorial-video-how-to-load-balance-jitsi-meet/)\n\n<figure class=\"video-container\">\n    <iframe src=\"https://www.youtube.com/embed/LyGV4uW8km8\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</figure>\n\n## [Scaling Jitsi Meet in the Cloud](https://jitsi.org/blog/new-tutorial-video-scaling-jitsi-meet-in-the-cloud/)\n\n<figure class=\"video-container\">\n    <iframe src=\"https://www.youtube.com/embed/Jj8a6ZRgehI\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n</figure>\n"
  },
  {
    "path": "docs/faq.md",
    "content": "---\nid: faq\ntitle: FAQ\n---\n\n## How to tell if my server instance is behind NAT?\n\nIn general, if the tool ifconfig (or ipconfig) shows the assigned IPv4 address to be a private / local address (10.x.x.x, or  172.16.x.x - 172.31.x.x, or 192.168.x.x) but you know that its public IPv4 address is different from that, the server is most probably behind NAT.\n\nIf you are hosting your server on a VPS, and you are not sure, ask your VPS provider's support team.\n\n## Clients could communicate well in the room created at `meet.jit.si`. The same clients still could connect to my self-hosted instance but could neither hear nor see one another. What's wrong?\n\nMost probably, the server is behind NAT, but you haven't added the NAT-specific configuration. See this [resolved question](https://community.jitsi.org/t/cannot-see-video-or-hear-audio-on-self-hosted-instance/). You need to follow the steps detailed [here](devops-guide/devops-guide-quickstart#advanced-configuration).\n\n## It works with two participants but crashes or does not work properly when a third joins\n\nP2P mode is working, but it fails when you are trying to pass traffic via jitsi-videobridge2.\n\nCheck you've got your firewall / NAT set up correctly — especially UDP 10000. For more information, see [here](devops-guide/devops-guide-quickstart#setup-and-configure-your-firewall).\n\n## Can I mute and unmute other participants?\n\nIf you are the moderator of a conference, you can mute everyone's microphone. You cannot unmute other people's microphones, and they can unmute their microphones at any time.\n\nYou may want to set some \"ground rules\" for who can talk and when, just as with any physical meeting or classroom.\n\nIf you would like to limit who can become a moderator, you need to set up your instance of Jitsi and enable \"secure domain\". Please see [here](#4-enable-secure-domain-if-you-are-using-your-instance-of-jitsi) for more information.\n\n## How can I protect my meetings with Jitsi?\n\n### _1. Create a \"strong\" room name._\n\nUse a strong room name, that no one else is likely to be using. Use the name generator on the welcome page, or else generate your own \"strong\" name.\n\nFor example, on macOS, in the terminal, you can use `uuidgen` to generate a string of letters of numbers (e.g. B741B63E-C5E6-4D82-BAC4-048BE25D8CC7).\n\nYour room name would be `meet.jit.si/B741B63E-C5E6-4D82-BAC4-048BE25D8CC7` on the hosted `meet.jit.si` platform.\n\nIf you use \"test\" or \"LucysMeeting\" \"pilates\" or similar, then it's highly likely that other users will have had the same idea.\n\n### _2. Use a different room name for each meeting / conference you have._\n\nIf you are going to have multiple meetings, ideally use a different room name for each one.\n\nIf that is not practical, at least use a different room name for each group of people.\n\n### _3. Add a password to the room._\n\nOnce you have started your room, set a password for it. Only people who have the password can join from that point on, but it does not affect people who have already joined.\n\nYou will need to tell everyone the password.\n\nIf they give the password to others, those other people can also join.\n\n### _4. Enable \"secure domain\" if you are using your instance of Jitsi._\n\nIn addition to the tips above, consider enabling the [\"secure domain\" configuration](https://jitsi.github.io/handbook/docs/devops-guide/secure-domain). This requires you (or someone else) to enter a username and password to open a room. It also allows you to become a moderator.\n\n## It's working when I connect from a browser, but not from the iOS or Android apps\n\nThis probably means that you are not serving the fullchain for your TLS certificate. You can check if your cert chain\nis properly configured [here](https://whatsmychaincert.com/).\n\nIn nginx, if you are using Let's Encrypt, you should have a line like this:\n\n`ssl_certificate /etc/letsencrypt/live/jitsi.example.com/fullchain.pem;`\n\n\n## Can I record and save the video?\n\nYes. There are multiple methods (using external software, services or the embedded feature):\n\n#### Record using the native feature\n\nJitsi offers the possiblity to record locally the video (with audio) of the room. When the recording is stopped (either manually or when the max size of the file is reached) the file (in webm format) is saved into the device storage.\n\nTo configure the feature, for self-hosted instances, see information [here](dev-guide/dev-guide-configuration/#recording).\n\n#### Record using external software / services\n\n_Note_: If you want to use a privacy-friendly method, use method 1 or 2.\n\n1. **OBS**: Use [OBS](https://obsproject.com/) to record your Session (e.g. your browser window).\n\n2. **RTMP-Server**: For this you have to setup your own RTMP-Server and then use your RTMP URL + Stream key instead of the Youtube Stream key as described [here](https://jitsi.org/blog/live-streaming-with-jitsi-and-youtube/). Self-installed Jitsi Meet deployments will need to setup Jibri to do this.\n\n3. **Dropbox**: [Connect to Dropbox with Jitsi Meet](/handbook/docs/dev-guide/dev-guide-web-integrations#creating-the-dropbox-app-for-dropbox-recording-integration) and save the video in the Dropbox. \n\n4. **Video Services/Websites**: Stream your conference to YouTube or other sites (e.g. Twitch) and access the recording there (see [howto](https://jitsi.org/blog/live-streaming-with-jitsi-and-youtube/)). Self-installed Jitsi Meet deployments will need to setup Jibri to do this. \n\n## I set the password in meeting but it is not working the next time\nOnce the meeting ends it's password also gets removed, so you need to set the password again for next meeting.\n\n## How to limit the number of participants?\n\n1. Use the command `prosodyctl about` to view the version of prosody and plug directory, similar to the output below.\n\n```\nProsody 0.11.6\n\n# Prosody directories\n\nData directory: /var/lib/prosody\n\nConfig directory: /etc/prosody\n\nSource directory: /usr/lib/prosody\n\nPlugin directories:\n\n/usr/share/jitsi-meet/prosody-plugins/\n\n/usr/lib/prosody/modules/\n```\n\n2. Check if there is a `mod_muc_max_occupants.lua` file in your plugin directory.\n\nIf not, please create a new file `mod_muc_max_occupants.lua` in the plugin directory And copy everything from [here](https://github.com/jitsi/jitsi-meet/blob/master/resources/prosody-plugins/mod_muc_max_occupants.lua) to paste.\n\nIf it exists, please ignore this step.\n\n3.Edit your `/etc/prosody/conf.avail/meet.example.com.cfg.lua` file and add `muc_max_occupants` as a module_enabled in the conference.meet.example.com \"muc\" section.\n\nThen, add the options below that. You need both `muc_max_occupants` and `muc_access_whitelist` defined.\n\nExample:\n\n```\nComponent \"conference.meet.example.com\" \"muc\"\n   storage = \"memory\"\n   modules_enabled = {\n       \"muc_meeting_id\";\n       \"muc_domain_mapper\";\n       \"muc_max_occupants\"; \n   }\n   muc_max_occupants = \"5\"\n   muc_access_whitelist = { \"focus@auth.meet.example.com\" }\n   admins = { \"focus@auth.meet.example.com\" }\n   muc_room_locking = false\n   muc_room_default_public_jids = true\n```\n\nNote: the relationship between storage = \"\" and your prosody version, and you need to modify all storage=\"\" .\n- Prosody nightly747 storage = \"null\"\n- Prosody 0.10 storage = \"none\"\n- Prosody 0.11 storage = \"memory\"\n\n4. You need to use the command `prosodyctl restart` to see the effect.\n\n5. If you want to update to use prosody, you can check [here](https://community.jitsi.org/t/how-to-how-do-i-update-prosody/72205).\n\n## Other participants complain my screen sharing is very bright and appears washed out?\nYou might have HDR streaming enabled in your OS display or graphic card settings.  If you are on Windows, you can quickly toggle HDR on/off using `Win + Alt + B` for all your HDR-capable screens at any time, even while screen sharing.\n\n"
  },
  {
    "path": "docs/intro.md",
    "content": "---\nid: intro\ntitle: Introduction\n---\n\n## What is Jitsi?\n\nJitsi is a video conferencing platform.  It is easy to use, easy to self-host, secure, state-of-the-art, and built from a [collection of Open Source projects](architecture.md).\n\n## About this handbook\n\nThis handbook aims to be the one-stop shop for all Jitsi documentation.\n\n:::note It's work in progress.\nIf you want to help, please create a **Pull Request** in our [GitHub repository](https://github.com/jitsi/handbook)!\n:::\n\nThe content is divided into 3 main areas:\n\n* [User guide](/docs/category/user-guide): Designed to help users of the service, to better\nunderstand all the available features and how to use them.\n\n* [Developer guide](/docs/category/developer-guide): Designed to help developers who want to either\nintegrate the Jitsi Meet API / SDK in their products or want to improve Jitsi Meet\nitself by developing new features or fixing bugs.\n\n* [Self-Hosting guide](devops-guide/devops-guide.md): Designed for folks wanting to self-host, system administrators\nor anyone who wishes to deploy and operate their own Jitsi Meet instance.\n\n## JaaS customers\n\nAre you a JaaS customer? If so, please start [here](https://developer.8x8.com/jaas/docs/jaas-onboarding).\n"
  },
  {
    "path": "docs/releases.md",
    "content": "---\nid: releases\ntitle: Releases\n---\n\n:::tip\nRelease notes for Jitsi Meet are kept [here](https://github.com/jitsi/jitsi-meet-release-notes).\n:::\n\nimport Tabs from '@theme/Tabs';\nimport TabItem from '@theme/TabItem';\n\n<Tabs queryString=\"release\">\n  <TabItem value=\"mobile\" label=\"Mobile\" default>\n### Apps\n\n| Android | Android (F-Droid) | iOS |\n|:-:|:-:|:-:|\n| [<img src=\"https://raw.githubusercontent.com/jitsi/handbook/master/docs/assets/google-play-badge.png\" height=\"50\" />](https://play.google.com/store/apps/details?id=org.jitsi.meet) | [<img src=\"https://raw.githubusercontent.com/jitsi/handbook/master/docs/assets/f-droid-badge.png\" height=\"50\" />](https://f-droid.org/en/packages/org.jitsi.meet/) | [<img src=\"https://raw.githubusercontent.com/jitsi/handbook/master/docs/assets/appstore-badge.png\" height=\"50\" />](https://itunes.apple.com/us/app/jitsi-meet/id1165103905) |\n\n### Apps (beta)\n\nIf you are feeling adventurous and want to get an early scoop of the features as they are being\ndeveloped you can also sign up for our open beta testing here:\n\n| Android | iOS |\n|:-:|:-:|\n| [Play Store Beta](https://play.google.com/apps/testing/org.jitsi.meet) | [TestFlight](https://testflight.apple.com/join/isy6ja7S)\n\n### SDKs\n\n| Android | iOS |\n| :--: | :--: |\n| [Maven repository](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-android-sdk#use-pre-build-sdk-artifactsbinaries) | [CocoaPods](https://cocoapods.org/pods/JitsiMeetSDK)\n  </TabItem>\n  <TabItem value=\"desktop\" label=\"Desktop\">\n| Windows | macOS | GNU/Linux (AppImage) | GNU/Linux (Deb) |\n| :--: | :--: | :--: | :--: |\n| [Download](https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet.exe) | [Download](https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet.dmg) | [Download](https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet-x86_64.AppImage) | [Download](https://github.com/jitsi/jitsi-meet-electron/releases/latest/download/jitsi-meet-amd64.deb) |\n\nThe desktop applications are based on Electron. For macOS, it is also available as a `brew` cask which can be installed using `brew install jitsi-meet`.\n  </TabItem>\n  <TabItem value=\"server\" label=\"Server\">\n### Docker images\n\nSee the Docker image releases [here](https://github.com/jitsi/docker-jitsi-meet/releases).\n\n### Debian/Ubuntu packages\n\n* [`stable`](https://download.jitsi.org/stable/) ([instructions](https://jitsi.org/downloads/ubuntu-debian-installations-instructions/))\n* [`testing`](https://download.jitsi.org/testing/) ([instructions](https://jitsi.org/downloads/ubuntu-debian-installations-instructions-for-testing/))\n* [`nightly`](https://download.jitsi.org/unstable/) ([instructions](https://jitsi.org/downloads/ubuntu-debian-installations-instructions-nightly/))\n\n### Web frontend\n\nLatest stable release | [![release](https://img.shields.io/badge/release-latest-green.svg)](https://github.com/jitsi/jitsi-meet/releases/latest) |\n|---|---|\n\nPrebuilt [source builds](https://download.jitsi.org/jitsi-meet/src/) are also available.\n\n:::note\nGenerally, you won't need to download this, as it's part of the Debian packages and Docker images already.\n:::\n  </TabItem>\n</Tabs>\n"
  },
  {
    "path": "docs/security.md",
    "content": "---\nid: security\ntitle: Security\n---\n\nThis topic is relatively complex, so we created a post that covers it much\nmore broadly here: https://jitsi.org/security\n"
  },
  {
    "path": "docs/user-guide/advanced.md",
    "content": "---\nid: user-guide-advanced\ntitle: User Guide (advanced)\nsidebar_label: Advanced options\n---\n\nThere are some options to tweak the invitation link to unlock more features in\nJitsi. The following parameters apply to the web, iframe and mobile version.\n\nAll keys listed here are prefixed with `config.`.\nYou pick a key, combine it with its value using `=` and link parameters\nwith `&`, e.g. `#config.defaultLanguage=en&config.minParticipants=3`.\n\n<!--\n  See also for implementation:\n  https://github.com/jitsi/jitsi-meet/blob/b0188a71841c966122c3cce8c7023b7de8e32a82/config.js\n  https://github.com/jitsi/jitsi-meet/blob/b0188a71841c966122c3cce8c7023b7de8e32a82/react/features/base/config/configWhitelist.js\n  https://github.com/jitsi/jitsi-meet/blob/b0188a71841c966122c3cce8c7023b7de8e32a82/react/features/base/config/functions.any.js#L70\n-->\n\n## Invitations\n\nThese parameters affect how you can invite people either before or within a session.\n\nKey                             | Value  | Effect\n------------------------------- | ------ | -----------------------------------\n`disableInviteFunctions`        | `true` | disable invite function of the app\n`minParticipants`               | `2`    | override the minimum number of participants before starting a call\n`prejoinConfig.enabled`         | `true` | show an intermediate page before joining to allow for adjustment of devices\n\n## UI\n\nThese parameters have an effect on the user interface.\n\nKey                             | Value  | Effect\n------------------------------- | ------ | -----------------------------------\n`defaultLanguage`               | `en`   | change the UI default language\n`disableThirdPartyRequests`     | `true` | generate avatars locally and disable third-party requests\n`enableDisplayNameInStats`      | `true` | send display names of participants to analytics\n`enableEmailInStats`            | `true` | send email (if available) to analytics\n`enableInsecureRoomNameWarning` | `true` | show a warning label if the room name is deemed insecure\n\n## Video\n\nUse these parameters to influence the video of a session.\n\nKey                             | Value  | Effect\n------------------------------- | ------ | -----------------------------------\n`desktopSharingFrameRate.min`   | `5`    | override the minimum framerate for desktop sharing\n`desktopSharingFrameRate.max`   | `5`    | override the maximum framerate for desktop sharing\n`startWithVideoMuted`           | `true` | disable video when joining\n\n## Audio\n\nUse these parameters to influence the audio of a session.\n\nKey                             | Value  | Effect\n------------------------------- | ------ | -----------------------------------\n`disableAudioLevels`            | `true` | disable audio statistics polling (thereby perhaps improving performance)\n`disableRemoteMute`             | `true` | disable all muting operations of remote participants\n`startWithAudioMuted`           | `true` | turn off audio input when joining\n`startSilent`                   | `true` | mute audio input and output\n"
  },
  {
    "path": "docs/user-guide/basic.md",
    "content": "---\nid: user-guide-basic\ntitle: User Guide (basic)\nsidebar_label: Basic options\n---\n\nWelcome to the user guide!\n\nCheck back soon!\n"
  },
  {
    "path": "docs/user-guide/browsers.md",
    "content": "---\nid: supported-browsers\ntitle: Supported Browsers\n---\n\n## Desktop browsers\n\n| Browser | Support | Versions | Notes |\n|---|:---:|:---:|---|\n| Chrome [^1] | ✅ | >= 72 | Best results with >= 96 |\n| Firefox | ✅ | >= 68 | Best results with >= 101 |\n| Safari | ✅ | >= 14 | Best results with >= 15, output device selection unsupported |\n| Edge | ✅ | >= 79 | Edge Legacy is unsupported |\n| Internet Explorer | ❌ | | |\n\n## Mobile browsers\n\n### Android\n\n| Browser | Support | Versions | Notes |\n|---|:---:|:---:|---|\n| Chrome [^1] | ✅ | | Same support as the desktop version |\n| Firefox | ✅ | | Same support as the desktop version |\n\n:::note\nFor a better mobile experience (background support, Bluetooth support, etc.) we recommend using a\nnative app instead. We provide a [native Android SDK](/handbook/docs/dev-guide/dev-guide-android-sdk).\n:::\n\n### iOS\n\n| Browser | Support | Versions | Notes |\n|---|:---:|:---:|---|\n| Chrome | ✅ | | Same support as Safari as they share the engine |\n| Firefox | ✅ | | Same support as Safari as they share the engine |\n| Safari | ✅ | >= 14.3 | Best results with 15.4 |\n| Edge | ✅ | | Same support as Safari as they share the engine |\n\n:::note\nOn iOS all browsers share the same engine, Safari. As such all features and limitations on all iOS\nbrowsers are those of Safari.\n\nFor a better mobile experience (background support, CallKit integration, etc.) we recommend using a\nnative app instead. We provide a [native iOS SDK](/handbook/docs/dev-guide/dev-guide-ios-sdk).\n:::\n\n[^1]: This also applies to all Chromium based browsers such as Brave, (current) Edge, Opera, Vivaldi and others.\n"
  },
  {
    "path": "docs/user-guide/client-connection-status-indicators.md",
    "content": "---\nid: client connection status indicators\ntitle: Client Connection Status Indicators\n---\n\nThis document explains what the different connection quality indicators on the video thumbnails actually mean.\n\n## GOOD\n* With video enabled, when the send bitrate for the video stream is at least 50% of the target bitrate expected for the stream. Please refer to the target bitrates table below.\n* With video disabled or screen sharing is in progress, when the downstream packet loss is less than 6%.\n\n## NON-OPTIMAL\n* With video enabled, when the send bitrate for the video stream is at least 30% of the target bitrate expected for the stream. Please refer to the target bitrates table below.\n* With video disabled or screen sharing is in progress, when the downstream packet loss is between 6% and 8%.\n\n## POOR\n* With video enabled, when the send bitrate for the video stream is at least 10% of the target bitrate expected for the stream. Please refer to the target bitrates table below.\n* With video disabled or screen sharing is in progress, when the downstream packet loss is between 8% and 12%.\n\n## LOST\n* When the user stops receiving video for the remote endpoint even when the endpoint is not video muted and it is in LastN as indicated by the bridge’s LastNEndpointChangeEvent.\n* When the bridge sends an EndpointConnectivityStatusChangeEvent indicating that the remote endpoint is no longer active, i.e., when the bridge has not received media from the remote endpoint for more than 3 secs.\n\n## GHOST/NINJA\n* When the user stops receiving video for the remote endpoint even when the endpoint is not video muted and it is not in LastN as indicated by the bridge’s LastNEndpointChangeEvent. This means that the bridge decided to suspend the video for this user. Bridge takes into consideration the available downlink bandwidth for the receiving endpoint and the number of video streams requested using the channelLast setting.\n\n## Target bitrates expected for the video streams\n\nCodecType   | 180p (in Kbps) | 360p (in Kbps) | 720p (in Kbps)\n----------- | -------------- | -------------- | -------------------\nVP8         |      200       |     500        |      1500          \nVP9         |      100       |     300        |      1200          \n"
  },
  {
    "path": "docs/user-guide/jitsi-meet-for-google-calendar.md",
    "content": "---\nid: user-guide-jitsi-meet-for-google-calendar\ntitle: Jitsi Meet for Google Calendar\nsidebar_label: Jitsi Meet for Google Calendar\n---\n\nWelcome to the user guide!\n\nCheck back soon!\n"
  },
  {
    "path": "docs/user-guide/join-a-jitsi-meeting.md",
    "content": "---\nid: user-guide-join-jitsi-meeting\ntitle: Join a Jitsi Meeting\n---\n\n# Join by using a Jitsi link\n\nPeople can invite each other to Jitsi meetings by simply sending a link.\n\n1. If you have received such an invite link from a trusted source,\n   copy it into your browser's address bar and press <kbd>Enter</kbd> / <kbd>Return</kbd>.\n2. Your browser may first ask you to grant microphone and/or camera access.\n   If you trust the person who invited you, confirm this access request.\n   Please refer to the browser's documentation for details (e.g.\n   [Firefox](https://support.mozilla.org/kb/how-manage-your-camera-and-microphone-permissions#w_using-prompts-to-allow-or-block-camera-and-microphone-permissions-for-a-site), \n   [Chrome](https://support.google.com/chrome/answer/2693767)).\n3. If prompted, enter a name, which will be visible to other participants in the Jitsi Meeting room.\n4. (Optional) Adjust the camera and/or microphone settings via the `v` dropdown menu items.\n5. Click on `Join meeting`.\n6. If your meeing is taking place on https://meet.jit.si and you are the first to join you will be asked to authenticate or wait for a moderator. You can authenticate with a Google, Facebook or GitHub account.\n"
  },
  {
    "path": "docs/user-guide/keyboard-shortcuts.md",
    "content": "---\nid: keyboard-shortcuts\ntitle: Keyboard shortcuts\n---\n\n* <kbd>F</kbd> - Show or hide video thumbnails\n* <kbd>M</kbd> - Mute or unmute your microphone\n* <kbd>V</kbd> - Start or stop your camera\n* <kbd>A</kbd> - Manage video quality\n* <kbd>C</kbd> - Open or close the chat\n* <kbd>D</kbd> - Switch between camera and screen sharing\n* <kbd>P</kbd> - Show or hide the participants pane\n* <kbd>R</kbd> - Raise or lower your hand\n* <kbd>S</kbd> - View or exit full screen\n* <kbd>W</kbd> - Toggle tile view\n* <kbd>T</kbd> - Show participants stats\n* <kbd>Alt</kbd> <kbd>T</kbd> - Send thumbs up reaction\n* <kbd>Alt</kbd> <kbd>C</kbd> - Send clap reaction\n* <kbd>Alt</kbd> <kbd>L</kbd> - Send laugh reaction\n* <kbd>Alt</kbd> <kbd>O</kbd> - Send surprised reaction\n* <kbd>Alt</kbd> <kbd>B</kbd> - Send boo reaction\n* <kbd>Alt</kbd> <kbd>S</kbd> - Send silence reaction\n* <kbd>G</kbd> - Toggle GIPHY menu\n* <kbd>?</kbd> - Show or hide keyboard shortcuts\n* <kbd>SPACE</kbd> - Push to talk\n* <kbd>0</kbd> - Focus on your video\n* <kbd>1</kbd>-<kbd>9</kbd> - Focus on another person's video\n"
  },
  {
    "path": "docs/user-guide/share-a-jitsi-meeting.md",
    "content": "---\nid: user-guide-share-a-jitsi-meeting\ntitle: Share a Jitsi Meeting\nsidebar_label: Share a Jitsi Meeting\n---\n\nFirst, you need to [Join](https://jitsi.github.io/handbook/docs/user-guide/user-guide-join-jitsi-meeting) / [Start](https://jitsi.github.io/handbook/docs/user-guide/user-guide-start-a-jitsi-meeting) a Jitsi Meeting.\n\nNext:\n\n- Click on the **More actions** \"...\" Button.\n- Click on **Invite people**.\n- Select the desired option using which you want to share the meeting.\n"
  },
  {
    "path": "docs/user-guide/start-a-jitsi-meeting.md",
    "content": "---\nid: user-guide-start-a-jitsi-meeting\ntitle: Start a Jitsi Meeting\nsidebar_label: Start a Jitsi Meeting\n---\n\n## Desktop or Mobile Browser\n\n1. You need a browser (please note our separate information).\n2. Open the browser and in the address bar type, for example \"https://meet.jit.si\" (without \"\") and press <kbd>Enter</kbd>.\n3. The page opens as shown in the figure:\n\n![screenshot 1](../assets/user_guide_start_meeting.png \"screenshot 1\")\n\n4. Now enter a name for your conference (e.g. new meeting) in the \"Start new meeting\" field.\nNote: Please do not use any special characters, spaces or umlauts, as this can lead to problems.\nNote: Jitsi offers a functionality that automatically suggests names for the conferences. These can be overwritten.\n5. Click the blue `Go` button.\n6. The following window opens:\n\n![screenshot 2](../assets/user_guide_join_meeting.png \"screenshot 2\")\n\n7. It is possible that no picture of you will appear at first. To do this, the browser will ask you whether you want to allow camera access. Please confirm this by clicking on `allow` or `permit`. Sometimes you also have to click the camera button at the bottom of the screen first to activate the dialog for allowing camera access. Do the same with the microphone the first time you use Jitsi.\n8. Now enter your display name in the \"enter your name\" field.\n9. Click the blue `Join meeting` button.\n10. If you are connecting to https://meet.jit.si and you are first participant you will be asked to either authenticate or wait for a moderator. You can authenticate with a Google, Facebook or GitHub account.\n11. Have fun in your first conference.\n\n:::note\nIf you do not see a video image of yourself, check the following points:\nThe camera on your device is:\n- present (small lens at the top of the screen / an external webcam on the monitor),\n- activated (on some laptops you can actively switch the webcam on/off),\n- plugged in (only necessary for external webcams),\n- installed (some devices require the camera to be installed first).\n:::\n\n:::note\nIf you cannot transmit sound, check the following points:\nThe microphone on your device is:\n- available (especially with desktop devices, a microphone is never actually integrated. Here you need an external microphone or headset, which you connect to the appropriate ports on your PC),\n- activated (on some laptops with an integrated microphone or headsets there is a switch to activate / deactivate the microphone),\n- plugged in (only necessary for external microphones),\n- installed (on some old computers the microphone must be installed).\n:::\n"
  },
  {
    "path": "docs/user-guide/use-jitsi-meet-on-mobile.md",
    "content": "---\nid: user-guide-jitsi-meet-on-mobile\ntitle: Use Jitsi Meet on Mobile\nsidebar_label: Use Jitsi Meet on Mobile\n---\n\nWhen you Join a Jitsi Meeting using a Mobile Phone, there are always options to join the meeting using the Jitsi Meet App or Continue on the web. The most convenient option is to Join using the **Jitsi Meet App**.\n\nYou can download and Join / Create Meetings using the App as follows:\n\n- Download the Jitsi Meet App for [Android](https://play.google.com/store/apps/details?id=org.jitsi.meet) / [iOS](https://apps.apple.com/us/app/jitsi-meet/id1165103905) / [F-Droid](https://f-droid.org/en/packages/org.jitsi.meet/)\n- Open the app.\n- Join / Create a Meeting.\n"
  },
  {
    "path": "docusaurus.config.js",
    "content": "module.exports = {\n  title: \"Jitsi Meet\",\n  tagline: \"State-of-the-art video conferencing you can self-host.\",\n  url: \"https://jitsi.github.io\",\n  baseUrl: \"/handbook/\",\n  organizationName: \"jitsi\",\n  projectName: \"handbook\",\n  favicon: \"img/favicon.svg\",\n  onBrokenLinks: \"throw\",\n  onBrokenMarkdownLinks: \"throw\",\n  presets: [\n    [\n      \"@docusaurus/preset-classic\",\n      {\n        docs: {\n          showLastUpdateAuthor: false,\n          showLastUpdateTime: true,\n          editUrl: \"https://github.com/jitsi/handbook/edit/master/\",\n          path: \"docs\",\n          sidebarPath: require.resolve(\"./sidebars.js\"),\n        },\n        theme: {\n          customCss: [require.resolve(\"./src/css/custom.css\")],\n        },\n      },\n    ],\n  ],\n  plugins: [\n    [\n      '@docusaurus/plugin-client-redirects',\n      {\n        redirects: [\n          {\n            to: '/docs/category/user-guide',\n            from: [ '/docs/user-guide', '/docs/user-guide/user-guide-start' ],\n          },\n          {\n            to: '/docs/category/developer-guide',\n            from: [ '/docs/dev-guide', '/docs/dev-guide/dev-guide-start' ],\n          },\n          {\n            to: '/docs/devops-guide/',\n            from: '/docs/devops-guide/devops-guide-start',\n          },\n        ]\n      }\n    ]\n  ],\n  themeConfig: {\n    prism: {\n      additionalLanguages: [\"java\", \"markdown\", \"bash\", \"gradle\", \"lua\", \"dart\"],\n      lang: {\n        \"shell\": \"bash\"\n      },\n    },\n    algolia: {\n      appId: 'K2ODL876OV',\n      apiKey: 'fc233b31ee025aa87cf553bd9e7ce9e9',\n      indexName: 'jitsi',\n    },\n    navbar: {\n      title: \"Jitsi Meet Handbook\",\n      logo: {\n        src: \"img/logo.svg\",\n      },\n      items: [\n        {\n          to: \"docs/intro\",\n          label: \"Docs\",\n          position: \"left\",\n        },\n        {\n            to: \"docs/category/sdks\",\n            label: \"SDKs\",\n            position: \"left\",\n        },\n        {\n          to: \"docs/releases\",\n          label: \"Releases\",\n          position: \"left\",\n        },\n        {\n          href: \"https://community.jitsi.org\",\n          label: \"Community\",\n          position: \"left\",\n        },\n        {\n          href: \"https://jaas.8x8.vc\",\n          label: \"JaaS\",\n          position: \"left\",\n        },\n        {\n          href: 'https://github.com/jitsi',\n          position: 'right',\n          className: 'header-github-link',\n          'aria-label': 'GitHub repository',\n        },\n      ],\n    },\n    image: \"img/undraw_online.svg\",\n    footer: {\n      style: \"dark\",\n      links: [\n        {\n          title: \"Docs\",\n          items: [\n            {\n              label: \"Introduction\",\n              to: \"docs/intro\",\n            },\n            {\n              label: \"User Guide\",\n              to: \"docs/category/user-guide\",\n            },\n            {\n              label: \"Developer Guide\",\n              to: \"docs/category/developer-guide\",\n            },\n            {\n              label: \"Self-Hosting Guide\",\n              to: \"docs/devops-guide\",\n            },\n          ],\n        },\n        {\n          title: \"Community\",\n          items: [\n            {\n              label: \"Forum\",\n              href: \"https://community.jitsi.org\",\n            },\n            {\n              label: \"Twitter\",\n              href: \"https://twitter.com/jitsinews\",\n            },\n          ],\n        },\n        {\n          title: \"More\",\n          items: [\n            {\n              label: \"Blog\",\n              href: \"https://jitsi.org\",\n            },\n            {\n              label: \"GitHub\",\n              href: \"https://github.com/jitsi\",\n            },\n            {\n              label: \"JaaS: Jitsi as a Service\",\n              href: \"https://jaas.8x8.vc\"\n            },\n          ],\n        },\n      ],\n      logo: {\n        alt: \"8x8 Footer Logo\",\n        src: \"img/8x8-copyright-icon.svg\",\n        href: \"https://8x8.com\",\n      },\n      copyright: `Copyright © 8x8, Inc.`,\n    },\n  },\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@jitsi/handbook\",\n  \"private\": true,\n  \"scripts\": {\n    \"examples\": \"docusaurus-examples\",\n    \"start\": \"docusaurus start\",\n    \"build\": \"docusaurus build\",\n    \"serve\": \"docusaurus serve\",\n    \"publish-gh-pages\": \"docusaurus-publish\",\n    \"write-translations\": \"docusaurus-write-translations\",\n    \"version\": \"docusaurus-version\",\n    \"rename-version\": \"docusaurus-rename-version\",\n    \"swizzle\": \"docusaurus swizzle\",\n    \"deploy\": \"docusaurus deploy\",\n    \"docusaurus\": \"docusaurus\"\n  },\n  \"dependencies\": {\n    \"@docusaurus/core\": \"3.5.2\",\n    \"@docusaurus/plugin-client-redirects\": \"3.5.2\",\n    \"@docusaurus/preset-classic\": \"3.5.2\",\n    \"@mdx-js/react\": \"3.0.1\",\n    \"clsx\": \"2.1.1\",\n    \"prism-react-renderer\": \"2.4.0\",\n    \"react\": \"18.3.1\",\n    \"react-dom\": \"18.3.1\"\n  }\n}\n"
  },
  {
    "path": "sidebars.js",
    "content": "module.exports = {\n  docs: [\n    {\n      type: \"category\",\n      label: \"Getting Started\",\n      items: [\n        \"intro\",\n        \"architecture\",\n        \"security\",\n        \"faq\",\n      ],\n    },\n    {\n      type: \"category\",\n      label: \"Community\",\n      link: {\n        type: \"doc\",\n        id: \"community/community-intro\",\n      },\n      items: [\n        \"community/third-party-software\",\n      ],\n    },\n    {\n      type: \"category\",\n      label: \"User Guide\",\n      link: {\n        type: \"generated-index\",\n      },\n      items: [\n        \"user-guide/supported-browsers\",\n        \"user-guide/user-guide-join-jitsi-meeting\",\n        \"user-guide/user-guide-start-a-jitsi-meeting\",\n        \"user-guide/user-guide-share-a-jitsi-meeting\",\n        \"user-guide/user-guide-jitsi-meet-on-mobile\",\n        \"user-guide/user-guide-jitsi-meet-for-google-calendar\",\n        \"user-guide/keyboard-shortcuts\",\n        \"user-guide/user-guide-basic\",\n        \"user-guide/user-guide-advanced\",\n      ],\n    },\n    {\n      type: \"category\",\n      label: \"Developer Guide\",\n      link: {\n        type: \"generated-index\",\n      },\n      items: [\n        \"dev-guide/dev-guide-contributing\",\n        {\n          type: \"category\",\n          label: \"SDKs\",\n          link: {\n            type: \"generated-index\",\n          },\n          items: [\n            \"dev-guide/dev-guide-iframe\",\n            \"dev-guide/dev-guide-ljm-api\",\n            \"dev-guide/dev-guide-electron-sdk\",\n            \"dev-guide/dev-guide-react-sdk\",\n            \"dev-guide/dev-guide-android-sdk\",\n            \"dev-guide/dev-guide-ios-sdk\",\n            \"dev-guide/dev-guide-react-native-sdk\",\n            \"dev-guide/dev-guide-flutter-sdk\",\n          ]\n        },\n        {\n          type: \"category\",\n          label: \"Web\",\n          link: {\n            type: \"generated-index\",\n          },\n          items: [\n            \"dev-guide/dev-guide-web-jitsi-meet\",\n            \"dev-guide/dev-guide-ljm\",\n            \"dev-guide/dev-guide-web-integrations\",\n            {\n              type: \"category\",\n              label: \"IFrame API\",\n              link: {\n                type: \"doc\",\n                id: \"dev-guide/dev-guide-iframe\",\n              },\n              items: [\n                \"dev-guide/dev-guide-iframe-functions\",\n                \"dev-guide/dev-guide-iframe-commands\",\n                \"dev-guide/dev-guide-iframe-events\"\n              ]\n            },\n            \"dev-guide/dev-guide-react-sdk\",\n            \"dev-guide/dev-guide-ljm-api\",\n            \"dev-guide/dev-guide-windows\",\n          ],\n        },\n        {\n          type: \"category\",\n          label: \"Mobile\",\n          link: {\n            type: \"generated-index\",\n          },\n          items: [\n            \"dev-guide/dev-guide-mobile-jitsi-meet\",\n            \"dev-guide/mobile-feature-flags\",\n            \"dev-guide/dev-guide-android-sdk\",\n            \"dev-guide/dev-guide-ios-sdk\",\n            \"dev-guide/dev-guide-react-native-sdk\",\n            \"dev-guide/dev-guide-flutter-sdk\",\n          ],\n        },\n        \"dev-guide/dev-guide-configuration\",\n      ],\n    },\n    {\n      type: \"category\",\n      label: \"Self-Hosting Guide\",\n      link: {\n        type: \"doc\",\n        id: \"devops-guide/devops-guide-start\",\n      },\n      items: [\n        {\n          type: \"category\",\n          label: \"Deployment\",\n          link: {\n            type: \"generated-index\",\n          },\n          items: [\n            \"devops-guide/devops-guide-requirements\",\n            \"devops-guide/devops-guide-quickstart\",\n            \"devops-guide/devops-guide-opensuse\",\n            {\n              type: \"category\",\n              label: \"Docker\",\n              link: {\n                type: \"doc\",\n                id: \"devops-guide/devops-guide-docker\",\n              },\n              items: [\n                {\n                  type: \"doc\",\n                  id: \"devops-guide/devops-guide-log-analyser\",\n                  label: \"Log Analyser\",\n                },\n              ],\n            },\n            \"devops-guide/devops-guide-region\"\n          ],\n        },\n        {\n          type: \"category\",\n          label: \"Configuration\",\n          link: {\n            type: \"generated-index\",\n          },\n          items: [\n            \"devops-guide/authentication\",\n            \"devops-guide/secure-domain\",\n            \"devops-guide/token-authentication\",\n            \"devops-guide/ldap-authentication\",\n            \"devops-guide/devops-guide-scalable\",\n            \"devops-guide/reservation\",\n            \"devops-guide/turn\",\n            \"devops-guide/speakerstats\",\n            \"devops-guide/videosipgw\",\n            \"devops-guide/cloud-api\",\n            \"devops-guide/file-sharing\"\n          ],\n        },\n        \"devops-guide/devops-guide-videotutorials\",\n        \"devops-guide/faq\",\n      ],\n    },\n  ],\n  \"releases-sidebar\": [\n    {\n      type: \"doc\",\n      label: \"Releases\",\n      id: \"releases\",\n    },\n  ],\n};\n"
  },
  {
    "path": "src/css/custom.css",
    "content": ":root {\n  --ifm-color-primary-lightest: #3897e0;\n  --ifm-color-primary-lighter: #2188d6;\n  --ifm-color-primary-light: #2082cd;\n  --ifm-color-primary: #1d76ba;\n  --ifm-color-primary-dark: #1a6aa7;\n  --ifm-color-primary-darker: #19649e;\n  --ifm-color-primary-darkest: #145382;\n}\n\n.header-github-link:hover {\n  opacity: 0.6;\n}\n\n.header-github-link::before {\n  content: \"\";\n  width: 24px;\n  height: 24px;\n  display: flex;\n  background: url(\"data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E\")\n    no-repeat;\n}\n\n[data-theme=\"dark\"] .header-github-link::before {\n  background: url(\"data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E\")\n    no-repeat;\n}\n\n@media only screen and (min-device-width: 360px) and (max-device-width: 736px) {\n}\n\n@media only screen and (min-width: 1024px) {\n}\n\n@media only screen and (max-width: 1023px) {\n}\n\n@media only screen and (min-width: 1400px) {\n}\n\n@media only screen and (min-width: 1500px) {\n}\n\n.video-container {\n  height: 0;\n  margin: 0;\n  margin-bottom: 30px;\n  overflow: hidden;\n  padding-bottom: 56.25%;\n  padding-top: 30px;\n  position: relative;\n}\n\n.video-container iframe {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n}\n\nimg[alt=\"ios-screensharing\"] {\n  display: inline;\n}\n"
  },
  {
    "path": "src/pages/help.md",
    "content": "# Getting help\n\nJitsi is maintained by a dedicated group of enthusiasts.\n\nIf you need help with Jitsi [our community](https://community.jitsi.org) is the best place to start.\n\nYou can learn more about using Jitsi or developing applications with it by browsing our [docs](/handbook/docs/intro).\n"
  },
  {
    "path": "src/pages/index.js",
    "content": "import React from \"react\";\nimport clsx from \"clsx\";\nimport Layout from \"@theme/Layout\";\nimport Link from \"@docusaurus/Link\";\nimport useDocusaurusContext from \"@docusaurus/useDocusaurusContext\";\nimport useBaseUrl from \"@docusaurus/useBaseUrl\";\nimport styles from \"./styles.module.css\";\n\nconst features = [\n  {\n    title: <>Hey there Fellow Jitster!</>,\n    imageUrl: \"img/undraw_code_review.svg\",\n    description: (\n      <>\n        Jitsi Meet is a set of Open Source projects which empower users to\n        deploy secure, scalable and easy to use video conferencing platforms\n        with state-of-the-art video quality and features.\n\n        <br/><br/>\n\n        On This site you'll find documentation for all your Jitsi needs.\n\n        <br /><br/>\n\n        Get started <a href=\"docs/intro\">here</a>.\n      </>\n    ),\n  },\n  {\n    title: <>Batteries included</>,\n    imageUrl: \"img/undraw_real_time_sync.svg\",\n    description: (\n      <>\n        Jitsi Meet supports all common browsers and also mobile devices.\n        \n        <br /><br />\n\n        Our APIs allow developers to easily integrate Jitsi Meet into existing\n        applications, whether those are web based or native mobile apps.\n        \n        <br /><br />\n        \n        You can use our freely available instance at{\" \"}\n        <a href=\"https://meet.jit.si\" target=\"_blank\">\n          meet.jit.si\n        </a>{\" \"}\n        or self-host it yourself using our readily available Debian packages or\n        comprehensive Docker setup.\n      </>\n    ),\n  },\n  {\n    title: <>JaaS: Jitsi as a Service</>,\n    imageUrl: \"img/undraw_going_up.svg\",\n    description: (\n      <>\n        Looking for configuration flexibility without the complexity of\n        self-hosting and scalability management?\n        \n        <br /><br />\n        \n        Look no further than JaaS. 8x8 Jitsi as a Service (JaaS) is\n        an enterprise-ready video meeting platform that allows developers,\n        organizations and businesses to easily build and deploy video\n        solutions. With Jitsi as a Service we now give you all the power of\n        Jitsi running on our global platform so you can focus on building secure\n        and branded video experiences.\n        \n        <br /><br />\n        Check JaaS out{\" \"}\n        <a href=\"https://jaas.8x8.vc\" target=\"_blank\">\n          here\n        </a>\n        .\n      </>\n    ),\n  },\n];\n\nfunction VideoContainer() {\n  return (\n    <div className=\"container text--center margin-bottom--xl margin-top--lg\">\n      <div className=\"row\">\n        <div className=\"col\">\n          <h2>What is Jitsi?</h2>\n          <iframe\n            \n            src=\"https://www.youtube.com/embed/TB7LlM4erx8\"\n            title=\"What is Jitsi?\"\n            frameBorder=\"0\"\n            allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n            allowFullScreen\n          />\n        </div>\n      </div>\n    </div>\n  );\n}\n\nfunction Home() {\n  const context = useDocusaurusContext();\n  const { siteConfig = {} } = context;\n  return (\n    <Layout\n      title=\"Jitsi Meet\"\n      description=\"State-of-the-art video conferencing you can self-host.\"\n    >\n      <header className={clsx(\"hero hero--primary\", styles.heroBanner)}>\n        <div className=\"container\">\n          <h1 className=\"hero__title\">{siteConfig.title}</h1>\n          <p className=\"hero__subtitle\">{siteConfig.tagline}</p>\n          <div className={styles.buttons}>\n            <Link\n              className={clsx(\n                \"button button--secondary button--lg\",\n                styles.getStarted\n              )}\n              to={\"docs/intro\"}\n            >\n              Get started!\n            </Link>\n          </div>\n        </div>\n      </header>\n      <main>\n        <VideoContainer />\n        {features && features.length && (\n          <section className={styles.features}>\n            <div className=\"container\">\n              <div className=\"row\">\n                {features.map(({ imageUrl, title, description }, idx) => (\n                  <div key={idx} className={clsx(\"col col--4\", styles.feature)}>\n                    {imageUrl && (\n                      <div className=\"text--center margin-bottom--lg\">\n                        <img\n                          className={styles.featureImage}\n                          src={useBaseUrl(imageUrl)}\n                          alt={title}\n                        />\n                      </div>\n                    )}\n                    <h3>{title}</h3>\n                    <p>{description}</p>\n                  </div>\n                ))}\n              </div>\n            </div>\n          </section>\n        )}\n      </main>\n    </Layout>\n  );\n}\n\nexport default Home;\n"
  },
  {
    "path": "src/pages/styles.module.css",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * @format\n */\n\n/**\n * CSS files with the .module.css suffix will be treated as CSS modules\n * and scoped locally.\n */\n\n.heroBanner {\n  padding: 4rem 0;\n  text-align: center;\n  position: relative;\n  overflow: hidden;\n}\n\n@media screen and (max-width: 966px) {\n  .heroBanner {\n    padding: 2rem;\n  }\n}\n\n.buttons {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.features {\n  display: flex;\n  align-items: center;\n  padding: 2rem 0;\n  width: 100%;\n}\n\n.featureImage {\n  height: 180px;\n  width: 180px;\n}\n\n.banner {\n  font-weight: bold;\n  font-size: 20px;\n  padding: 20px;\n  max-width: 768px;\n  margin: 0 auto;\n  text-align: center;\n  color: white;\n}\n\n.banner a {\n  color: #f2f2f2;\n  text-decoration: underline;\n}\n\n.bannerWrapper {\n  padding: 0 0;\n  background-color: black;\n}\n\n/* The iframe is the element that is containing the YT video in the index.js file  */\niframe {\n  max-width: 900px;\n  width: calc(100% - 32px);\n  height: calc(50vh - 50px);\n}\n"
  },
  {
    "path": "static/.nojekyll",
    "content": ""
  }
]