diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml
new file mode 100644
index 000000000..6a3fad742
--- /dev/null
+++ b/.github/workflows/documentation.yml
@@ -0,0 +1,33 @@
+on:
+ push:
+ branches: [ master ]
+ paths-ignore:
+ - 'modoboa/**'
+ - 'frontend/**'
+ - 'doc/**'
+ pull_request:
+ branches: [ master ]
+ paths-ignore:
+ - 'modoboa/**'
+ - 'frontend/**'
+ - 'doc/**'
+
+jobs:
+ cache-and-install:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - uses: pnpm/action-setup@v4
+ name: Install pnpm
+ with:
+ version: 10
+ cache: true
+
+ - name: Install dependencies
+ run: cd doc-ng && pnpm install
+
+ - name: Build documentation
+ run: cd doc-ng && pnpm run build
diff --git a/.github/workflows/modoboa.yml b/.github/workflows/modoboa.yml
index fd102787f..6eec728ec 100644
--- a/.github/workflows/modoboa.yml
+++ b/.github/workflows/modoboa.yml
@@ -10,12 +10,22 @@ on:
- 'modoboa/**.po'
- 'frontend/**'
- 'doc/**'
+ - 'doc-ng/**'
+ - '.editorconfig'
+ - '.readthedocs.yml'
+ - '.pre-commit-config.yaml'
+ - '.gitignore'
pull_request:
branches: [ master ]
paths-ignore:
- 'modoboa/**.po'
- 'frontend/**'
- 'doc/**'
+ - 'doc-ng/**'
+ - '.editorconfig'
+ - '.readthedocs.yml'
+ - '.pre-commit-config.yaml'
+ - '.gitignore'
release:
branches: [ master ]
types: [ published ]
diff --git a/.gitignore b/.gitignore
index 4488700e0..3b3c04c36 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,6 +24,8 @@ node_modules
/frontend/webpack-stats.json
/frontend/src/translations.json
/frontend/.__mf__temp/
+doc-ng/.vitepress/cache/
+doc-ng/.vitepress/dist/
# TOX stuff
test_project/modoboa.db
diff --git a/.readthedocs.yml b/.readthedocs.yml
index 487c3c902..bab00cccd 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -1,10 +1,17 @@
version: 2
-sphinx:
- configuration: doc/conf.py
+
build:
- os: "ubuntu-22.04"
+ os: ubuntu-lts-latest
tools:
- python: "3.10"
-python:
- install:
- - requirements: doc/requirements.txt
+ nodejs: "latest"
+ jobs:
+ install:
+ - npm install vitepress
+ build:
+ html:
+ # The site was created by running `vitepress init`
+ # and following the official guide
+ # https://vitepress.dev/guide/getting-started
+ - ./node_modules/.bin/vitepress build doc-ng
+ - mkdir -p $READTHEDOCS_OUTPUT/
+ - mv doc-ng/dist $READTHEDOCS_OUTPUT/html
diff --git a/doc-ng/.vitepress/config.mjs b/doc-ng/.vitepress/config.mjs
new file mode 100644
index 000000000..55bd278ec
--- /dev/null
+++ b/doc-ng/.vitepress/config.mjs
@@ -0,0 +1,168 @@
+import { defineConfig } from 'vitepress'
+
+// https://vitepress.dev/reference/site-config
+export default defineConfig({
+ // Use Canonical URL, but only the path and with no trailing /
+ // End result is like: `/en/latest`
+ // https://docs.readthedocs.com/platform/stable/intro/vitepress.html#using-the-proper-base-path
+ base: process.env.READTHEDOCS_CANONICAL_URL
+ ? new URL(process.env.READTHEDOCS_CANONICAL_URL).pathname.replace(/\/$/, '')
+ : '',
+ title: 'Modoboa',
+ titleTemplate: ':title',
+ description: 'Modoboa documentation',
+ srcExclude: ['public/**/*.md'],
+ ignoreDeadLinks: [/^https?:\/\/localhost:*/],
+ metaChunk: true,
+ themeConfig: {
+ // https://vitepress.dev/reference/default-theme-config
+ nav: [
+ { text: 'Home', link: '/' },
+ {
+ text: 'Installation',
+ items: [
+ { text: 'Requirements', link: '/installation/requirements' },
+ { text: 'Manual Installation', link: '/installation/manual' },
+ { text: 'Modoboa Installer', link: '/installation/installer' },
+ { text: 'Upgrade Instance', link: '/installation/upgrade' },
+ { text: 'Configuration', link: '/configuration' },
+ ],
+ },
+ {
+ text: 'Contributing',
+ items: [
+ { text: 'Getting Started', link: '/contributing/getting_start' },
+ { text: 'Write a Plugin', link: '/contributing/plugin_api' },
+ { text: 'Translate', link: '/contributing/translation' },
+ { text: 'REST API', link: '/rest_api' },
+ { text: 'Contributors', link: '/contributors' },
+ ],
+ },
+ ],
+
+ sidebar: {
+ '/installation/': [
+ {
+ text: 'Manual',
+ items: [
+ { text: 'Modoboa', link: '/installation/modoboa' },
+ { text: 'WebServer', link: '/installation/webserver' },
+ { text: 'Dovecot', link: '/installation/dovecot' },
+ { text: 'Postfix', link: '/installation/postfix' },
+ { text: 'OpenDKIM', link: '/installation/opendkim' },
+ { text: 'Radicale', link: '/installation/radicale' },
+ { text: 'Amavis', link: '/installation/amavis' },
+ ],
+ },
+ {
+ text: 'Automatic',
+ items: [
+ { text: 'Modoboa Installer', link: '/installation/installer' },
+ ],
+ },
+ ],
+ '/contributing/': [
+ { text: 'Getting Started', link: '/contributing/getting_start' },
+ { text: 'Write a Plugin', link: '/contributing/plugin_api' },
+ { text: 'Translate', link: '/contributing/translate' },
+ ],
+ },
+ socialLinks: [
+ { icon: 'github', link: 'https://github.com/modoboa/modoboa' },
+ { icon: 'discord', link: 'https://discord.gg/WuQ3v3PXGR' },
+ {
+ icon: {
+ svg: ``,
+ },
+ link: 'https://app.transifex.com/tonio/modoboa/dashboard/',
+ },
+ { icon: 'linkedin', link: 'https://www.linkedin.com/company/102575109' },
+ ],
+ footer: {
+ message: 'Build with Vitepress',
+ copyright: `Copyright © 2010-${new Date().getUTCFullYear()} Antoine Nguyen`,
+ },
+ outline: {
+ label: 'Summmary',
+ },
+ docFooter: {
+ prev: false,
+ next: false,
+ },
+ search: {
+ provider: 'local',
+ },
+ lastUpdated: {
+ text: 'Update at',
+ formatOptions: {
+ dateStyle: 'full',
+ timeStyle: 'short',
+ },
+ },
+ },
+ sitemap: {
+ hostname: 'https://modoboa.readthedocs.io/en/latest',
+ },
+ markdown: {
+ lineNumbers: true,
+ image: {
+ lazyLoading: true,
+ },
+ },
+ cleanUrls: true,
+ outDir: './dist',
+ lastUpdated: true,
+ transformPageData(pageData) {
+ // console.log(pageData);
+
+ const baseUrl = process.env.READTHEDOCS_CANONICAL_URL
+ ? new URL(process.env.READTHEDOCS_CANONICAL_URL).pathname.replace(/\/$/, '')
+ : ''
+
+ const canonicalUrl = baseUrl + '/' + pageData.relativePath.replace(/\.md$/, "").replace(/^index$/, "");
+
+ let updtDate = new Date();
+ // If no commit for a page (dev in progress), lastUpdated == NaN
+ // Ensure lastUpdated is defined and a number before passing it to isNaN/new Date
+ if (typeof pageData.lastUpdated === 'number' && !isNaN(pageData.lastUpdated)) {
+ updtDate = new Date(pageData.lastUpdated);
+ }
+
+ pageData.frontmatter.head ??= [];
+ pageData.frontmatter.head.push([
+ "link",
+ { rel: "canonical", href: canonicalUrl },
+ ]);
+ pageData.frontmatter.head.push([
+ "meta",
+ { property: "og:title", content: pageData.frontmatter.title },
+ ]);
+ pageData.frontmatter.head.push([
+ "meta",
+ { property: "og:description", content: pageData.frontmatter.description },
+ ]);
+ pageData.frontmatter.head.push([
+ "meta",
+ { property: "og:type", content: 'article' },
+ ]);
+ pageData.frontmatter.head.push([
+ "meta",
+ { property: "og:url", content: canonicalUrl },
+ ]);
+
+ // TODO: Uncomment when modoboa image is available on the public folder
+ // pageData.frontmatter.head.push([
+ // "meta",
+ // { property: "og:image", content: 'https://xxxx/modoboa.png' },
+ // ]);
+
+ pageData.frontmatter.head.push([
+ "meta",
+ { property: "og:locale", content: 'en_GB' },
+ ]);
+
+ }
+})
diff --git a/doc-ng/configuration.md b/doc-ng/configuration.md
new file mode 100644
index 000000000..0e4530983
--- /dev/null
+++ b/doc-ng/configuration.md
@@ -0,0 +1,466 @@
+# Configuration
+
+## Online parameters
+
+Modoboa provides online panels to modify internal parameters. There are
+two available levels:
+
+- **Application level**: global parameters, define how the application
+ behaves. Available at `Modoboa > Parameters`
+- **User level**: per user customization. Available at `User > Settings >
+ Preferences`
+Regardless level, parameters are displayed using tabs, each tab
+corresponding to one application.
+
+## General parameters {#admin-params}
+
+The *admin* application exposes several parameters, they are presented
+below:
+
+
+| Name | Tab | Description | Default Value |
+| :-: | :-: | :-- | :-: |
+| Authentication type | General | The backend used for authentication | Local |
+| Default password scheme | General passwords | Scheme used to crypt mailbox | sha512crypt |
+| Rounds | General | Number of rounds (only used by [sha256crypt] and [sha512crypt]). Must be between 1000 and 999999999, inclusive. | 70000 |
+| Secret key | General | A key used to encrypt users password in sessions | random value |
+| Sender address | General | Email address used to send notifications. | |
+| Enable communication | General | Enable communication with Modoboa public API | yes |
+| Check new versions | General | Automatically checks if a newer version is available | yes |
+| Send statistics | General | Send statistics to Modoboa public API (counters and used extensions) | yes |
+| Top notifications check interval | General | Interval between two top notification checks (in seconds) | 30 |
+| Maximum log record age | General | The maximum age in days of a log record | 365 |
+| Items per page | General | Number of displayed items per page | 30 |
+| Default top redirection | General | The default redirection used when no application is specified | userprefs |
+| Enable MX checks | Admin | Check that every domain has a valid MX record | yes |
+| Valid MXs | Admin | A list of IP or network address every MX should match. A warning will be sent if a record does not respect this it. | |
+| Enable DNSBL checks | Admin | Check every domain against major DNSBL providers | yes |
+| DKIM keys storage directory | Admin | Path to a directory where DKIM generated keys will be stored | |
+| Default DKIM key length | Admin | The default size (in bits) for new keys | 2048 |
+| Handle mailboxes on filesystem | Admin | Rename or remove mailboxes on the filesystem when they get renamed or removed within Modoboa | no |
+| Mailboxes owner | Admin | The UNIX account who owns mailboxes on the filesystem | vmail |
+| Default domain quota | Admin | Default quota (in MB) applied to freshly created domains with no value specified. A value of 0 means no quota. | 0 |
+| Automatic account removal | Admin | When a mailbox is removed, also remove the associated account | no |
+| Automatic domain/mailbox creation | Admin | Create a domain and a mailbox when an account is automatically created | yes |
+
+::: warning
+If Dovecot is not running on the same host than Modoboa, you will have
+to define which password schemes are supported.
+
+To do so, open the `settings.py` file and add a `DOVECOT_SUPPORTED_SCHEMES`
+variable with the output of the command:
+```shell
+$ doveadm pw -l
+```
+:::
+
+::: info
+
+If you are not familiar with virtual domain hosting, you should take a look at
+[postfix's documentation](http://www.postfix.org/VIRTUAL_README.html).
+
+This [How to](https://help.ubuntu.com/community/PostfixVirtualMailBoxClamSmtpHowto)
+also contains useful information.
+:::
+
+::: tip
+A random secret key will be generated each time the *Parameters* page is
+refreshed and until you save parameters at least once.
+:::
+
+::: info
+Specific LDAP parameters are also available, see `LDAP
+authentication `.
+:::
+
+### Media files
+
+Modoboa uses a specific directory to upload files (ie. when the webmail
+is in use) or to create ones (ex: graphical statistics).
+
+This directory is named `media` and is located inside modoboa's
+installation directory (called `modoboa_site` in this documentation).
+
+To work properly, the system user which runs modoboa (`www-data`,
+`apache`, whatever) must have write access to this directory.
+
+### Customization
+
+## Custom logo
+
+You have the possibility to use a custom logo instead of the default one
+on the login page and inside generated PDF documents.
+
+To do so, open the `settings.py` file and add a `MODOBOA_CUSTOM_LOGO` variable.
+
+This variable must contain the relative URL of your logo under `MEDIA_URL`.
+
+For example:
+
+``` python
+MODOBOA_CUSTOM_LOGO = os.path.join(MEDIA_URL, "custom_logo.png")
+```
+
+Then copy your logo file into the directory indicated by `MEDIA_ROOT`.
+
+You can also customize the logo(s) used in the frontend application
+(left menu and creation forms).
+
+To do so, copy the corresponding files inside the frontend directory
+directly (defaults to `/srv/modoboa/instance/frontend`).
+
+Then, edit the `config.json` file located in this very same directory and add the following variables:
+
+```json
+{
+ "MENU_LOGO_PATH": "/cusrtom_logo.png", // [!code focus]
+ "CREATION_FORM_LOGO_PATH": "/custom_logo2.png" // [!code focus]
+}
+```
+
+### Host configuration
+
+::: tip
+This section is only relevant when Modoboa handles mailboxes renaming
+and removal from the filesystem,
+which requires that Dovecot is installed and running on this host.
+
+If it is installed at a non-standard directory,
+paths to its binaries can be set in the `settings.py` file
+with the `DOVECOT_LOOKUP_PATH` and `DOVEADM_LOOKUP_PATH` variables.
+:::
+
+To manipulate mailboxes on the filesystem, you must allow the user who
+runs Modoboa to execute commands as the user who owns mailboxes.
+
+To do so, edit the `/etc/sudoers` file and add the following inside:
+
+```txt
+ ALL=() NOPASSWD: ALL
+```
+
+Replace values between `<>` by the ones you use.
+
+### Time zone and language {#timezone_lang}
+
+Modoboa is available in many languages.
+
+To specify the default language to use, edit the `settings.py` file
+and modify the `LANGUAGE_CODE` variable:
+
+```python
+LANGUAGE_CODE = 'fr' # or 'en' for english, etc.
+```
+
+::: tip
+Each user has the possibility to define the language he prefers.
+:::
+
+In the same configuration file, specify the timezone to use
+by modifying the `TIME_ZONE` variable. For example:
+
+```python
+TIME_ZONE = 'Europe/Paris'
+```
+
+### Sessions management
+
+Modoboa uses [Django's session framework](https://docs.djangoproject.com/en/dev/topics/http/sessions/?from=olddocs)
+to store per-user information.
+
+Few parameters need to be set in the `settings.py` configuration file to make Modoboa behave as expected:
+
+```python
+SESSION_EXPIRE_AT_BROWSER_CLOSE = False # Default value
+```
+
+This parameter is optional but you must ensure it is set to `False` (the default value).
+
+The default configuration file provided by the `modoboa-admin.py` command is properly configured.
+
+### Logging authentication
+
+To trace login attempts to the web interface, Modoboa uses python
+[SysLogHandler](https://docs.python.org/3/library/logging.handlers.html#logging.handlers.SysLogHandler)
+
+so you can see them in your syslog authentication log file ([/var/log/auth.log] in most cases).
+
+Depending on your configuration, you may have to edit the `settings.py` file and add
+['address': \'/dev/log\'] to the logging section:
+
+```python
+'syslog-auth': {
+ 'class': 'logging.handlers.SysLogHandler',
+ 'facility': SysLogHandler.LOG_AUTH,
+ 'address': '/dev/log',
+ 'formatter': 'syslog'
+},
+```
+
+### External authentication
+
+## LDAP {#ldap_auth}
+
+Modoboa supports external LDAP authentication using the following extra components:
+
+* [Python LDAP client](http://www.python-ldap.org/)
+* [Django LDAP authentication backend](http://pypi.python.org/pypi/django-auth-ldap)
+
+If you want to use this feature, you must first install those components:
+
+```shell
+$ pip install python-ldap django-auth-ldap
+```
+
+Then, all you have to do is to modify the `settings.py` file.
+
+Add a new authentication backend to the [AUTHENTICATION_BACKENDS] variable, like this:
+
+```python
+AUTHENTICATION_BACKENDS = (
+ 'modoboa.lib.authbackends.LDAPBackend',
+ # 'modoboa.lib.authbackends.LDAPSecondaryBackend', # Useful for a fallback mechanism
+ 'django.contrib.auth.backends.ModelBackend',
+)
+```
+
+Finally, go to **Modoboa > Parameters > General** and set *Authentication type* to LDAP.
+
+From there, new parameters will appear to let you configure the way
+Modoboa should connect to your LDAP server.
+
+They are described just below:
+
+| Name | Description | Default Value |
+| :-: | :-- | :-: |
+| Server address | The IP address of the DNS name of the LDAP server |localhost |
+| Server port | The TCP port number used by the LDAP server | 389 |
+| Use a secure connection | Use an SSL/TLS connection to access the LDAP server | no |
+| Authentication method | Choose the authentication method to use | Direct bind |
+| User DN template (direct bind mode)| The template used to construct a user\'s DN. It should contain one placeholder (ie.`%(user)s`) | |
+| Bind BN | The distinguished name to use when binding to the LDAP server. Leave empty for an anonymous bind | |
+| Bind password | The password to use when binding to the LDAP server (with \'Bind DN\') | |
+| Search base | The distinguished name of the search base | |
+| Search filter | An optional filter string (e.g. \'(objectClass=person)\'). In order to be valid, it must be enclosed in parentheses. | (mail=%(user)s) |
+| Password attribute | The attribute used to store user passwords | userPassword |
+| Active Directory | Tell if the LDAP server is an no Active Directory one | |
+| Administrator groups | Members of those LDAP Posix groups will be created ad domain administrators. Use ';' characters to separate groups. | |
+| Group type | The type of group used by your PosixGroup LDAP directory. | |
+| Groups search base | The distinguished name of the search base used to find groups | |
+| Domain/mailbox creation | Automatically create a domain and a mailbox when a new user is created just after the first successful authentication. You will generally want to disable this feature when the relay domains extension is in use | yes |
+
+::: tip
+If you need additional parameters, you will find a detailed
+documentation [here](http://packages.python.org/django-auth-ldap/).
+:::
+
+Once the authentication is properly configured, the users defined in
+your `LDAP` directory will be able to connect to Modoboa, the associated
+domain and mailboxes will be automatically created if needed.
+
+The first time a user connects to Modoboa, a local account is created if
+the `LDAP username` is a valid email address. By default, this account
+belongs to the `SimpleUsers` group and it has a mailbox.
+
+To automatically create domain administrators, you can use the
+**Administrator groups** setting. If a LDAP user belongs to one the
+listed groups, its local account will belong to the `DomainAdmins`
+group.
+
+In this case, the username is not necessarily an email address.
+
+Users will also be able to update their `LDAP password` directly from
+Modoboa.
+
+::: warning
+Modoboa doesn't provide any synchronization mechanism once a user is
+registered into the database. Any modification done from the directory
+to a user account will not be reported to Modoboa (an email address
+change for example). Currently, the only solution is to manually delete
+the Modoboa record, it will be recreated on the next user login.
+:::
+
+## SMTP {#smtp_auth}
+
+It is possible to use an existing SMTP server as an authentication
+source.
+
+To enable this feature, edit the `settings.py` file and change the following setting:
+
+``` python
+AUTHENTICATION_BACKENDS = (
+ 'modoboa.lib.authbackends.SMTPBackend',
+ 'django.contrib.auth.backends.ModelBackend',
+)
+```
+
+SMTP server location can be customized using the following settings:
+
+``` python
+AUTH_SMTP_SERVER_ADDRESS = 'localhost'
+AUTH_SMTP_SERVER_PORT = 25
+AUTH_SMTP_SECURED_MODE = None # 'ssl' or 'starttls' are accepted
+```
+
+### LDAP synchronization {#ldap-sync}
+
+Modoboa can synchronize accounts with an LDAP directory (tested with
+OpenLDAP) but this feature is not enabled by default.
+
+To activate it, add `modoboa.ldapsync` to `MODOBOA_APPS` in the `settings.py` file:
+
+```python
+MODOBOA_APPS = (
+ 'modoboa',
+ 'modoboa.core',
+ 'modoboa.lib',
+ 'modoboa.admin',
+ 'modoboa.transport',
+ 'modoboa.relaydomains',
+ 'modoboa.limits',
+ 'modoboa.parameters',
+ 'modoboa.dnstools',
+ 'modoboa.ldapsync',
+)
+```
+
+and enable it from the admin panel.
+
+::: warning
+Make sure to install additional `requirements `
+otherwise it won't work.
+:::
+
+The following parameters are available and must be filled:
+
+| Name | Description | Default Value |
+| :-: | :-- | :-: |
+| Enable LDAP synchronization | Control LDAP synchronization state | no |
+| Bind DN | The DN of a user with write permission to create/update accounts | |
+| Bind password | The associated password | |
+| Account DN template | The template used to build account DNs (must contain a %(user)s placeholder ) |
+
+## Database maintenance
+
+### Cleaning the logs table
+
+Modoboa logs administrator specific actions into the database. A
+clean-up script is provided to automatically remove oldest records. The
+maximum log record age can be configured through the online panel.
+
+To use it, you can setup a cron job to run every night:
+
+```txt
+0 0 * * * /manage.py cleanlogs
+#
+# Or like this if you use a virtual environment:
+# 0 0 * * * /manage.py cleanlogs
+```
+
+### Cleaning the session table
+
+Django does not provide automatic purging. Therefore, it's your job to
+purge expired sessions on a regular basis.
+
+Django provides a sample clean-up script:
+
+* `django-admin.py clearsessions`. That script deletes any session in the
+ session table whose `expire_date` is in the past.
+
+For example, you could setup a cron job to run this script every night:
+
+```txt
+0 0 * * * /manage.py clearsessions
+#
+# Or like this if you use a virtual environment:
+# 0 0 * * * /manage.py clearsessions
+```
+
+### Cleaning inactive accounts {#inactive_accounts}
+
+Thanks to `lastlogin`{.interpreted-text role="ref"}, it is now possible
+to monitor inactive accounts. An account is considered inactive if no
+login has been recorded for the last 30 days (this value can be changed
+through the admin panel).
+
+A management command is available to disable or delete inactive
+accounts.
+
+For example, you could setup a cron job to run it every night:
+
+```txt
+0 0 * * * /manage.py clean_inactive_accounts
+#
+# Or like this if you use a virtual environment:
+# 0 0 * * * /manage.py clean_inactive_accounts
+```
+
+The default behaviour is to disable accounts. You can delete them using the `--delete` option.
+
+## DMARC reports
+
+::: warning
+A set of tools to use DMARC through Modoboa.
+
+This feature is still in BETA stage, for now it only parses XML
+aggregated reports and generate visual reports on a per-domain basis.
+:::
+
+### Installation
+
+::: info
+modoboa-installer can automatically set it up for you.
+:::
+
+Make sure to install the following additional system package according
+to your distribution:
+
+* Debian / Ubuntu: libmagic1
+* CentOS: file-devel
+
+### Integration with Postfix
+
+A management command is provided to automatically parse DMARC aggregated
+reports (rua) and feed the database.
+
+The execution of this command can be automated with the definition of a
+postfix service and a custom transport table.
+
+First, declare a new service in `/etc/postfix/master.cf`:
+
+```txt
+dmarc-rua-parser unix - n n - - pipe
+ flags= user=vmail:vmail argv= /manage.py import_aggregated_report --pipe
+```
+
+Define a new transport table inside `/etc/postfix/main.cf`:
+
+```txt
+transport_maps =
+ hash:/etc/postfix/dmarc_transport
+ # other transport maps...
+```
+
+Create a file called `/etc/postfix/dmarc_transport` with the following content:
+
+```txt
+ dmarc-rua-parser:
+```
+
+::: warning
+You must not declare this email address as an identity (user account or
+alias), else DMARC reports will be directed to your mailbox and won\'t
+be parsed.
+:::
+
+Hash the file using the following command:
+
+```shell
+$ postmap /etc/postfix/dmarc_transport
+```
+
+Finally, reload postfix:
+
+```shell
+$ service postfix reload
+```
\ No newline at end of file
diff --git a/doc-ng/contributing/getting_start.md b/doc-ng/contributing/getting_start.md
new file mode 100644
index 000000000..e0ffb1cf4
--- /dev/null
+++ b/doc-ng/contributing/getting_start.md
@@ -0,0 +1,150 @@
+---
+title: Getting stated with modoboa
+description: Discover how to develop and extend modoboa
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, getting started, backend, frontend, docker'
+---
+
+# Getting started
+
+You would like to work on Modoboa but you don\'t know where to start?
+You're at the right place! Browse this page to learn useful tips.
+
+## With Docker
+
+A docker image is available for developers.
+
+To use it, you must install docker and docker-compose
+
+* [docker](https://docs.docker.com/install/) and
+* [docker-compose](https://docs.docker.com/compose/install/) first.
+
+If not already done, clone the repo and open it:
+
+```shell
+$ git clone https://github.com/modoboa/modoboa.git
+$ cd modoboa
+```
+
+Then, just run the following command:
+
+```shell
+$ docker-compose up
+```
+
+Then if not done already, run this command to create an OIDC application in order to be able to log in from the frontend:
+
+``` bash
+$ docker exec modoboa-api '/bin/sh -c python3 /code/test_project/manage.py createapplication --name frontend --client-id "LVQbfIIX3khWR3nDvix1u9yEGHZUxcx53bhJ7FlD" --user 1 --algorithm RS256 --redirect-uris 'https://localhost:3000/login/logged' public authorization-code'
+$ docker exec modoboa-api '/bin/sh -c python3 /code/test_project/manage.py createapplication --name Dovecot --skip-authorization --client-id=dovecot --client-secret=Toto12345 confidential client-credentials'
+```
+
+It will start the docker environment and make a Modoboa instance available at `https://localhost:8000`
+and the new admin interface at `https://localhost:3000`.
+
+If you don't want to use docker or need a more complex development setup, go to the next section.
+
+## Without Docker
+
+### Prepare a virtual environment
+
+A [virtual environment](https://docs.python.org/fr/3/library/venv.html)
+is a good way to setup a development environment on your machine.
+
+To do so, run the following commands:
+
+```shell
+$ python3 -m venv
+$ source /bin/activate
+$ git clone https://github.com/modoboa/modoboa.git
+$ cd modoboa
+$ pip install -e .[dev]
+```
+
+This will create a symbolic link to your local copy so any modification
+you make will be automatically available in your environment,
+no need to copy them.
+
+### Deploy an instance for development
+
+::: warning
+Make sure to `create a database ` before running this step.
+
+The format of the database url is also described in this page.
+:::
+
+Now that you have setup a development environment, you can deploy a test instance and run it:
+
+```shell
+$ cd
+$ modoboa-admin.py deploy --dburl default: --domain localhost --devel instance
+$ python manage.py runserver
+```
+
+You're ready to go! You should be able to access Modoboa at `http://localhost:8000` using `admin:password` as credentials.
+
+## Frontend
+
+The 2.0 version of Modoboa introduces a completely new interface written with the [Vue.js](https://vuejs.org/) framework.
+
+The source files are located in the `frontend/`{.interpreted-text role="file"} directory.
+
+To set it up, you will need to install NodeJS and Yarn - to manage the
+dependencies.
+
+Then, navigate to the `frontend/` directory and run:
+
+```shell
+$ yarn install
+```
+
+You can now build it and serve it - while running your instance too to
+serve the API - with:
+
+```shell
+$ yarn serve
+```
+
+## Tests
+
+If you deployed an instance for development, you can launch the tests from it with:
+
+```shell
+$ python manage.py test modoboa
+```
+
+You could also test just some them, i.e.:
+
+```shell
+$ python manage.py test modoboa.core.tests.test_authentication
+```
+
+Alternatively, you can use [tox](https://tox.readthedocs.io)
+from the repository to run all the tests and check the coverage with:
+
+```shell
+$ tox
+```
+
+You could limit the environment to a specific Python version with the `-e py` argument.
+
+Note that it is also possible to quickly run a test instance without any deployment - e.g. to preview some changes - by running:
+
+```shell
+$ tox -e serve
+```
+
+## Documentation
+
+The source files are located in the `/doc-ng/`[ folder and are
+written in markdown.
+
+They are formatted in HTML and compiled thanks to VitePress
+
+To build it and see the result, run:
+
+```shell
+$ npm run dev
+```
\ No newline at end of file
diff --git a/doc-ng/contributing/plugin_api.md b/doc-ng/contributing/plugin_api.md
new file mode 100644
index 000000000..52d1256dc
--- /dev/null
+++ b/doc-ng/contributing/plugin_api.md
@@ -0,0 +1,143 @@
+---
+title: plugin development guide for modoboa
+description: Discover how to develop a plugin for modoboa to extend functionnalities
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, plugin, extension, api, navigation, Django signals'
+---
+
+# Create a new plugin
+
+## Introduction
+
+Modoboa offers a plugin API to expand its capabilities.
+
+The current implementation provides the following possibilities:
+
+- Expand navigation by adding entry points to your plugin inside the GUI
+- Access and modify administrative objects (domains, mailboxes, etc.)
+- Register callback actions for specific events
+
+::: tip
+Plugins are nothing more than Django applications with an extra piece of
+code that integrates them into Modoboa.
+:::
+
+The `modo_extension.py` file will contain a complete description of the plugin:
+
+- Admin and user parameters
+- Custom menu entries
+
+The communication between both applications is provided by
+[Django signals](https://docs.djangoproject.com/en/5.2/topics/signals/).
+
+The following subsections describe the plugin architecture and explain
+how you can create your own.
+
+## The required glue
+
+To create a new plugin, just start a new django application like this
+(into Modoboa's directory):
+
+```shell
+$ python manage.py startapp
+```
+
+Then, you need to register this application using the provided API.
+
+Just copy/paste the following example into the `modo_extension.py` file of the future extension:
+
+```python
+from modoboa.core.extensions import ModoExtension, exts_pool
+
+
+class MyExtension(ModoExtension):
+ """My custom Modoboa extension."""
+
+ name = "myext"
+ label = "My Extension"
+ version = "0.1"
+ description = "A description"
+ url = "myext_root_location" # optional, name is used if not defined
+
+ def load(self):
+ """This method is called when Modoboa loads available and activated plugins.
+
+ Declare parameters and register events here.
+ """
+ pass
+
+ def load_initial_data(self):
+ """Optional: provide initial data for your extension here."""
+ pass
+
+exts_pool.register_extension(MyExtension)
+```
+
+Once done, simply add your extension's module name to the `MODOBOA_APPS`
+variable located inside `settings.py`.
+
+Finally, run the following commands:
+
+```shell
+$ python manage.py migrate
+$ python manage.py load_initial_data
+$ python manage.py collectstatic
+```
+
+### Parameters
+
+A plugin can declare its own parameters. There are two levels available:
+
+* `Global` parameters: used to configure the plugin, editable inside
+ the *Admin > Settings > Parameters* page
+* `User` parameters: per-user parameters (or preferences), editable
+ inside the *Options > Preferences* page
+
+#### Playing with parameters
+
+Parameters are defined using [Django forms](https://docs.djangoproject.com/en/1.9/topics/forms/)
+and Modoboa adds two special forms you can inherit depending on the level of parameter(s) you want to add:
+
+* `modoboa.parameters.forms.AdminParametersForm`: for general parameters
+* `modoboa.parameters.forms.UserParametersForm`: for user parameters
+
+To register new parameters, add the following line into the `load` method of your plugin class:
+
+```python
+from modoboa.parameters import tools as param_tools
+param_tools.registry.add(LEVEL, YourForm, ugettext_lazy("Title"))
+```
+Replace:
+
+* `LEVEL` (str): `"global"` or `"user"`.
+
+### Custom role permissions
+
+Modoboa uses Django's internal permission system.
+
+Administrative roles are nothing more than groups (`Group` instances).
+
+An extension can add new permissions to a group by listening to the `extra_role_permissions` signal. Here is an example:
+
+``` python
+from django.dispatch import receiver
+from modoboa.core import signals as core_signals
+
+PERMISSIONS = {
+ "Resellers": [
+ ("relaydomains", "relaydomain", "add_relaydomain"),
+ ("relaydomains", "relaydomain", "change_relaydomain"),
+ ("relaydomains", "relaydomain", "delete_relaydomain"),
+ ("relaydomains", "service", "add_service"),
+ ("relaydomains", "service", "change_service"),
+ ("relaydomains", "service", "delete_service")
+ ]
+}
+
+@receiver(core_signals.extra_role_permissions)
+def extra_role_permissions(sender, role, **kwargs):
+ """Add permissions to the Resellers group."""
+ return constants.PERMISSIONS.get(role, [])
+```
diff --git a/doc-ng/contributing/translation.md b/doc-ng/contributing/translation.md
new file mode 100644
index 000000000..42e6d68ce
--- /dev/null
+++ b/doc-ng/contributing/translation.md
@@ -0,0 +1,40 @@
+---
+title: translation guide for modoboa
+description: How to help to translate modoboa for your natural language
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, i18n, translation, transifex, makemessages, backend, frontend'
+---
+
+# Translation
+
+Modoboa needs you to translate the project ! Feel free to join the
+translation team.
+
+The project is using Transifex. To start with translation, you will need
+to create an account and join
+[modoboa's team](https://app.transifex.com/tonio/modoboa/dashboard/)
+
+## API
+
+Modoboa API code relies on Django's translation system.
+
+To update translation files after a change to the code, execute the following commands:
+
+```shell
+$ cd modoboa
+$ django-admin makemessages -a
+```
+
+## Frontend
+
+When editing the frontend, you may need to update localization files for
+transifex to pick up your changes.
+
+To do so, after finishing editing, go to the frontend directory and run
+the following command:
+
+```shell
+$ yarn gettext:extract
+```
diff --git a/doc-ng/contributors.md b/doc-ng/contributors.md
new file mode 100644
index 000000000..8ec727c83
--- /dev/null
+++ b/doc-ng/contributors.md
@@ -0,0 +1,17 @@
+---
+title: "Contributors list"
+description: "List of the contributor who participate to the modoboa's project"
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, contributor, project'
+---
+
+# Contributors
+
+- [6clones](https://www.6clones.fr/)
+- [Antidot](http://antidot.com)
+- [Bearstech](http://bearstech.com)
+- [CAP-REL](https://cap-rel.fr/)
+- [Dalnix](https://www.dalnix.se/)
+- [Mirounga](https://mirounga.fr)
diff --git a/doc-ng/index.md b/doc-ng/index.md
new file mode 100644
index 000000000..671ede471
--- /dev/null
+++ b/doc-ng/index.md
@@ -0,0 +1,43 @@
+---
+# https://vitepress.dev/reference/default-theme-home-page
+layout: home
+title: "Modoboa, the Mail hosting and management platform"
+description: "Web User Interface designed to work with Postfix and Dovecot"
+
+hero:
+ name: "Modoboa"
+ text: "Mail hosting and management platform"
+ tagline: "simplified Web User Interface designed to work with Postfix and Dovecot"
+ image:
+ src: modoboa_logo.png
+ alt: Modoboa Logo
+ actions:
+ - theme: brand
+ text: QuickStart
+ link: /installation/modoboa
+ - theme: alt
+ text: Contribute
+ link: /contributing/getting_start
+ - theme: alt
+ text: Translate
+ link: /contributing/translation
+ - theme: alt
+ text: Demo
+ link: https://demo.modoboa.org
+
+features:
+ - title: Sovereignty
+ details: Take back control of your emails to guarantee your security
+ - title: DKIM, DMARC, SPF
+ details: increase the deliverability of your emails
+ - title: API
+ details: To ease the integration with external sources (software or other), Modoboa provides a REST API
+---
+
+## Admin Dashboard
+
+
+
+## Webmail
+
+
\ No newline at end of file
diff --git a/doc-ng/installation/amavis.md b/doc-ng/installation/amavis.md
new file mode 100644
index 000000000..2876ba84d
--- /dev/null
+++ b/doc-ng/installation/amavis.md
@@ -0,0 +1,281 @@
+---
+title: manual step to install amavis component on modoboa
+description: This guide describe how to install amavis on the modoboa platform
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, installation, amavis, mysql, postgresql, quarantine'
+---
+
+# Amavis
+
+## Quick Amavis setup
+
+By default, amavis doesn\'t use a database. To configure this behaviour,
+you first need to create a dedicated database. This step is a bit manual
+since no *ready-to-use* SQL schema is provided by amavis. The
+information is located inside README files, one for
+[MySQL](http://www.amavis.org/README.sql-mysql.txt) and one for
+[PostgreSQL](http://www.amavis.org/README.sql-pg.txt).
+
+Then, you must tell amavis to use this database for lookups and
+quarantined messages storing. Here is a working configuration sample:
+
+```ini [amavis]
+@lookup_sql_dsn =
+ (['DBI::database=;host=;port=', '', ']']);
+
+@storage_sql_dsn =
+ (['DBI::database=;host=;port=', '', ']']);
+
+# PostgreSQL users NEED this parameter!
+# MySQL users only need this parameter is email addresses are stored
+# using the VARBINARY type.
+$sql_allow_8bit_address = 1;
+
+$virus_quarantine_method = 'sql:';
+$spam_quarantine_method = 'sql:';
+$banned_files_quarantine_method = 'sql:';
+$bad_header_quarantine_method = 'sql:';
+
+$virus_quarantine_to = 'virus-quarantine';
+$banned_quarantine_to = 'banned-quarantine';
+$bad_header_quarantine_to = 'bad-header-quarantine';
+$spam_quarantine_to = 'spam-quarantine';
+```
+
+Replace values between `<>` by yours. To know how to configure amavis to
+allow quarantined messages release, read this `section
+`{.interpreted-text role="ref"}.
+
+
+::: info
+Amavis configuration allows for separate lookup and storage databases
+but Modoboa doesn\'t support it yet.
+:::
+
+::: info
+`$sql_partition_tag` should remain undefined in `amavisd.conf`. Modoboa
+does not support the use of `sql_partition_tag`, setting this value can
+result in quarantined messages not showing or the wrong messages being
+released or learnt as ham/spam.
+:::
+
+## Connect Modoboa and Amavis
+
+You must tell to Modoboa where it can find the amavis database. Inside
+`settings.py`{.interpreted-text role="file"}, add a new connection to
+the `DATABASES` variable like this:
+
+```python{5-9} [settings.py]
+DATABASES = {
+ # Stuff before
+ #
+ "amavis": {
+ "ENGINE" : "",
+ "HOST" : "",
+ "NAME" : "",
+ "USER" : "",
+ "PASSWORD" : ""
+ }
+}
+```
+
+Replace values between `<>` with yours.
+
+### Cleanup
+
+Storing quarantined messages to a database can quickly become a
+perfomance killer. Modoboa provides a simple script to periodically
+purge the quarantine database. To use it, add the following line inside
+root\'s crontab:
+
+```txt
+0 0 * * * /manage.py qcleanup
+#
+# Or like this if you use a virtual environment:
+# 0 0 * * * /manage.py qcleanup
+```
+
+Replace `modoboa_site` with the path of your Modoboa instance.
+
+By default, messages older than 14 days are automatically purged. You
+can modify this value by changing the `MAX_MESSAGES_AGE` parameter in
+the online panel.
+
+## Release messages {#amavis_release}
+
+To release messages, first take a look at [this
+page](http://www.ijs.si/software/amavisd/amavisd-new-docs.html#quar-release).
+
+It explains how to configure amavisd-new to listen somewhere for the
+AM.PDP protocol. This protocol is used to send requests.
+
+Below is an example of a working configuration:
+
+```txt{1,2,10}
+$interface_policy{'SOCK'} = 'AM.PDP-SOCK';
+$interface_policy{'9998'} = 'AM.PDP-INET';
+
+$policy_bank{'AM.PDP-SOCK'} = {
+ protocol => 'AM.PDP',
+ auth_required_release => 0,
+};
+$policy_bank{'AM.PDP-INET'} = {
+ protocol => 'AM.PDP',
+ inet_acl => [qw( 127.0.0.1 [::1] )],
+};
+```
+
+Don't forget to update the `inet_acl` list if you plan to release from
+the network.
+
+Once amavisd-new is configured, just tell Modoboa where it can find the
+*release server* by modifying the following parameters in the online
+panel:
+
+| Name | Description | Default value |
+| ---------------------- | -------------------------------------------- | :----------------------- |
+| Amavis connection mode | Mode used to access the PDP server | unix |
+| PDP server address | PDP server address (if inet mode) | localhost |
+| PDP server port | PDP server port (if inet mode) | 9998 |
+| PDP server socket | Path to the PDP server socket (if unix mode) | /var/amavis/amavisd.sock |
+
+### Deferred release
+
+By default, simple users are not allowed to release messages themselves.
+They are only allowed to send release requests to administrators.
+
+As administrators are not always available or logged into Modoboa,
+a notification tool is available.
+
+It sends reminder e-mails to every administrators or domain administrators.
+To use it, add the following example line to root's crontab:
+
+```txt
+0 12 * * * /manage.py amnotify --baseurl=''
+#
+# Or like this if you use a virtual environment:
+# 0 12 * * * /manage.py amnotify --baseurl=''
+````
+
+You are free to change the frequency.
+
+::: info
+If you want to let users release their messages alone (not recommended),
+go to the admin panel.
+:::
+
+The following parameters are available to let you customize this feature:
+
+| Name | Description | Default value |
+| ---------------------------- | ---------------------------------------------- | -------------------------- |
+| Check requests interval | Interval between two release requests checks | 30 |
+| Allow direct release release | Allow users to directly release their messages | no |
+| Notifications sender | The e-mail address used to send notitications | |
+
+## Self-service mode {#selfservice}
+
+The *self-service* mode lets users act on quarantined messages without beeing authenticated. They can:
+
+- View messages
+- Remove messages
+- Release messages (or send release requests)
+
+To access a specific message, they only need the following information:
+
+- Message's unique identifier
+- Message's secret identifier
+
+This information is controlled by *amavis*, which is in charge of
+notifying users when new messages are put into quarantine.
+
+Each notification (one per message) must embark a direct link containing the
+required identifiers.
+
+To activate this feature, go the administration panel and set the
+**Enable self-service mode** parameter to yes.
+
+The last step is to customize the notification messages amavis sends.
+The most important is to embark a direct link. Take a look at the
+[README.customize](http://amavis.org/README.customize.txt) file to learn
+what you're allowed to do.
+
+Here is a link example:
+
+ https:///user/selfquarantine/%i/%R?secret_id=[:secret_id]
+
+## Manual SpamAssassin learning {#sa_manual_learning}
+
+It is possible to manually train [SpamAssassin](http://spamassassin.apache.org/)
+using the quarantine's content. By train, we mean:
+
+- Mark message(s) as spam (false negative(s))
+- Mark message(s) as non-spam (false positive(s))
+
+This feature is available to all users (from super administrators to
+simple users) but not enabled by default.
+
+### SpamAssassin configuration
+
+For better performance and to enable the per-user level, SpamAssassin
+must store bayes information into a SQL database.
+
+Create a new database and a new user/password (using your favorite
+database server) and edit the default configuration file
+(`/etc/spamassassin/local.cf`{.interpreted-text role="file"})
+to add the following lines inside:
+
+::: code-group
+
+``` perl [PostgreSQL]
+bayes_store_module Mail::SpamAssassin::BayesStore::PgSQL
+bayes_sql_dsn
+bayes_sql_username
+bayes_sql_password
+```
+
+``` perl [MySQL]
+bayes_store_module Mail::SpamAssassin::BayesStore::MySQL
+bayes_sql_dsn
+bayes_sql_username
+bayes_sql_password
+```
+
+:::
+
+Replace values between `<>` by yours. Possible values for `Driver` are
+`PgSQL` or `MySQL` (non exhaustive list). The syntax for `DSN` depends
+on the driver you choose. Please consult the official documentation.
+
+### Enable the feature through Modoboa
+
+Manual learning is disabled by default. You can activate it through the
+administration panel (*Modoboa \> Paremeters \> Amavis*). There two
+learning levels:
+
+1. Global: available to administrators only. A single (global) bayes
+ database is shared between everyone.
+2. Per domain: available to administrators and domain administrators.
+ Each domain can have a dedicated database.
+3. Per user: each user can create its own database to customize the way
+ SpamAssassin will detect spam.
+
+The domain and user levels are not activated by default, dedicated
+parameters are available through the panel.
+
+::: info
+Domain and user databases are only created the first time someone calls
+the learning feature through the quarantine.
+:::
+
+::: warning
+A bayes database needs to reach pre-defined thresholds before it can be
+used by SpamAssassin. The default values are **200** spams and **200**
+hams.
+:::
+
+You will find other paramaters related to this feature. You won\'t need
+to change them most of the time, unless SpamAssassin is hosted on a
+different machine than Modoboa. (in this case, `spamc` will be used
+instead of `sa-learn`).
\ No newline at end of file
diff --git a/doc-ng/installation/dovecot.md b/doc-ng/installation/dovecot.md
new file mode 100644
index 000000000..684e7765a
--- /dev/null
+++ b/doc-ng/installation/dovecot.md
@@ -0,0 +1,665 @@
+---
+title: manual step to install dovecot component on modoboa
+description: This guide describe how to install dovecot on the modoboa platform
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, installation, dovecot, mysql, postgresql, imapd'
+---
+
+# Dovecot
+
+## introduction
+
+Modoboa requires Dovecot 2+ to work so the following documentation is
+suitable for this combination.
+
+In this section, we assume dovecot's configuration resides in
+`/etc/dovecot`, all pathes will be relative to this directory.
+
+You can find configuration documentation [here](https://doc.dovecot.org/configuration_manual/).
+
+This proposed configuration is the one you can find with modoboa-installer.
+
+For up to date configuration,
+[take a look here](https://github.com/modoboa/modoboa-installer/tree/master/modoboa_installer/scripts/files/dovecot/conf.d)
+
+## Packages
+
+For debian and derivative, recommended packages are:
+
+```shell
+apt install dovecot-imapd dovecot-lmtpd dovecot-managesieved dovecot-sieve
+```
+
+## Mailboxes
+
+First, edit the `conf.d/10-mail.conf` and
+set the `mail_location` variable:
+
+```txt{2}
+# maildir
+mail_location = maildir:~/Maildir
+```
+
+Then, edit the `inbox` namespace and add the following line:
+
+```txt
+inbox = yes
+```
+
+In order for dovecot to automaticaly create usual folders on account
+creation, edit the `inbox` namespace of `15-mailboxes.conf`:
+
+```txt
+mailbox Drafts {
+ auto = subscribe
+ special_use = \Drafts
+}
+mailbox Junk {
+ auto = subscribe
+ special_use = \Junk
+}
+mailbox Sent {
+ auto = subscribe
+ special_use = \Sent
+}
+mailbox Trash {
+ auto = subscribe
+ special_use = \Trash
+}
+```
+
+Starting dovecot 2.2.20+, you can add `autoexpunge` parameter to delete older message, for instance:
+
+```txt
+mailbox Trash {
+ auto = subscribe
+ special_use = \Trash
+ autoexpunge=30d
+}
+```
+
+With dovecot 2.1+, it ensures all the special mailboxes will be automaticaly created for new accounts.
+
+For dovecot 2.0 and older, use the [autocreate](http://wiki2.dovecot.org/Plugins/Autocreate) plugin.
+
+### Operations on the file system {#fs_operations}
+
+::: warning
+Modoboa needs to access the `dovecot` binary to check its version. To
+find the binary path, we use the `which` command first and then try
+known locations (`/usr/sbin/dovecot` and
+`/usr/local/sbin/dovecot` ). If you
+installed dovecot in a custom location, please tell us where the binary
+is by using the `DOVECOT_LOOKUP_PATH` setting (see
+`settings.py` ).
+:::
+
+Three operation types are considered:
+
+1. Mailbox creation
+2. Mailbox renaming
+3. Mailbox deletion
+
+The first one is managed by Dovecot. The last two ones may be managed by
+Modoboa if it can access the file system where the mailboxes are stored
+(see `admin-params` to activate this
+feature).
+
+Those operations are treated asynchronously by a cron script. For
+example, when you rename an e-mail address through the web UI, the
+associated mailbox on the file system is not modified directly. Instead
+of that, a *rename* order is created for this mailbox. The mailbox will
+be considered unavailable until the order is executed (see
+`Postfix configuration `).
+
+Edit the crontab of the user who owns the mailboxes on the file system:
+
+```shell
+$ crontab -u -e
+```
+
+And add the following line inside:
+
+```txt
+* * * * * python /manage.py handle_mailbox_operations
+```
+
+::: warning
+The cron script must be executed by the system user owning the
+mailboxes.
+:::
+
+::: warning
+The user running the cron script must have access to the
+`settings.py` file of the modoboa
+instance.
+:::
+
+The result of each order is recorded into Modoboa's log.
+
+Go to *Modoboa > Logs* to consult them.
+
+## Authentication
+
+To make the authentication work, edit the
+`conf.d/10-auth.conf` and uncomment the
+following line at the end:
+
+```txt{2}
+#!include auth-system.conf.ext
+!include auth-sql.conf.ext
+#!include auth-ldap.conf.ext
+#!include auth-passwdfile.conf.ext
+#!include auth-checkpassword.conf.ext
+#!include auth-vpopmail.conf.ext
+#!include auth-static.conf.ext
+```
+
+Then, edit the `conf.d/auth-sql.conf.ext`
+file and add the following content inside:
+```txt
+passdb sql {
+ driver = sql
+ # Path for SQL configuration file, see example-config/dovecot-sql.conf.ext
+ args = /etc/dovecot/dovecot-sql.conf.ext
+}
+
+userdb sql {
+ driver = sql
+ args = /etc/dovecot/dovecot-sql.conf.ext
+}
+```
+
+Make sure to activate only one backend (per type) inside your
+configuration (just comment the other ones).
+
+Edit the `dovecot-sql.conf.ext` and modify the configuration according to your database engine.
+
+::: code-group
+
+``` shell [PostgreSQL]
+# For debian and derivative, you need to install postgres connector for dovecot::
+sudo apt install dovecot-pgsql
+```
+
+``` shell [MySQL]
+# For debian and derivative, you need to install mysql connector for dovecot::
+sudo apt install dovecot-mysql
+```
+
+``` shell [SQLite]
+# For debian and derivative, you need to install sqlite connector for dovecot::
+sudo apt install dovecot-sqlite
+```
+
+:::
+
+::: code-group
+``` txt [PostgreSQL]
+driver = pgsql
+
+connect = host= dbname= user= password=
+
+default_pass_scheme = CRYPT
+
+user_query = \
+ SELECT '%{home_dir}/%%d/%%n' AS home, %mailboxes_owner_uid as uid, \
+ %mailboxes_owner_gid as gid, '*:bytes=' || mb.quota || 'M' AS quota_rule \
+ FROM admin_mailbox mb \
+ INNER JOIN admin_domain dom ON mb.domain_id=dom.id \
+ INNER JOIN core_user u ON u.id=mb.user_id \
+ WHERE (mb.is_send_only IS NOT TRUE OR '%s' NOT IN ('imap', 'pop3', 'lmtp')) \
+ AND mb.address='%%n' AND dom.name='%%d'
+
+password_query = \
+ SELECT email AS user, password, '%{home_dir}/%%d/%%n' AS userdb_home, \
+ %mailboxes_owner_uid AS userdb_uid, %mailboxes_owner_gid AS userdb_gid, \
+ CONCAT('*:bytes=', mb.quota, 'M') AS userdb_quota_rule \
+ FROM core_user u \
+ INNER JOIN admin_mailbox mb ON u.id=mb.user_id \
+ INNER JOIN admin_domain dom ON mb.domain_id=dom.id \
+ WHERE (mb.is_send_only IS NOT TRUE OR '%s' NOT IN ('imap', 'pop3'))\
+ AND email='%%u' AND is_active AND dom.enabled
+
+iterate_query = SELECT email AS user FROM core_user WHERE is_active
+```
+
+``` txt [MySQL]
+driver = mysql
+
+connect = host= dbname= user= password=
+
+default_pass_scheme = CRYPT
+
+user_query = \
+ SELECT '%{home_dir}/%%d/%%n' AS home, %mailboxes_owner_uid as uid, \
+ %mailboxes_owner_gid as gid, \
+ CONCAT('*:bytes=', mb.quota, 'M') AS quota_rule \
+ FROM admin_mailbox mb \
+ INNER JOIN admin_domain dom ON mb.domain_id=dom.id \
+ INNER JOIN core_user u ON u.id=mb.user_id \
+ WHERE (mb.is_send_only=0 OR '%s' NOT IN ('imap', 'pop3', 'lmtp')) \
+ AND mb.address='%%n' AND dom.name='%%d'
+
+password_query = \
+ SELECT email AS user, password, '%{home_dir}/%%d/%%n' AS userdb_home, \
+ %mailboxes_owner_uid AS userdb_uid, %mailboxes_owner_gid AS userdb_gid, \
+ CONCAT('*:bytes=', mb.quota, 'M') AS userdb_quota_rule \
+ FROM core_user u \
+ INNER JOIN admin_mailbox mb ON u.id=mb.user_id \
+ INNER JOIN admin_domain dom ON mb.domain_id=dom.id \
+ WHERE (mb.is_send_only=0 OR '%s' NOT IN ('imap', 'pop3')) \
+ AND u.email='%%u' AND u.is_active=1 AND dom.enabled=1
+
+iterate_query = SELECT email AS user FROM core_user WHERE is_active
+```
+
+```txt [SQLite]
+driver = sqlite
+
+connect =
+
+default_pass_scheme = CRYPT
+
+password_query = SELECT email AS user, password FROM core_user u INNER JOIN admin_mailbox mb ON u.id=mb.user_id INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE u.email='%Lu' AND u.is_active=1 AND dom.enabled=1
+
+user_query = SELECT '/%Ld/%Ln' AS home, as uid, as gid, ('*:bytes=' || mb.quota || 'M') AS quota_rule FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE mb.address='%Ln' AND dom.name='%Ld'
+
+iterate_query = SELECT email AS user FROM core_user
+```
+:::
+
+
+::: info
+Replace values between `<>` with yours.
+:::
+
+## OAuth 2 authentication
+
+You first need to register Dovecot as an authorized consumer of the
+OAuth2 authentication service provided by Modoboa.
+
+To do so, create an application with the following commands:
+
+``` shell
+cd
+python manage.py createapplication --name=Dovecot --skip-authorization --client-id=dovecot confidential client-credentials
+```
+
+On success, you should see an output similar to:
+
+``` txt
+New application Dovecot created successfully.
+client_secret: XXXX
+```
+
+To enable OAuth2 authentication in Dovecot, edit the `conf.d/10-auth.conf`
+file and update the auth_mechanisms line as follows:
+
+``` txt
+auth_mechanisms = plain login oauthbearer xoauth2
+```
+
+Then, add the following line at the end:
+
+``` txt
+!include auth-oauth2.conf.ext
+```
+
+Then, create a file named `conf.d/auth-oauth2.conf.ext` with the following content:
+
+``` txt
+passdb {
+ driver = oauth2
+ mechanisms = xoauth2 oauthbearer
+ args = /etc/dovecot/conf.d/dovecot-oauth2.conf.ext
+}
+```
+
+Finally, create a file named `conf.d/dovecot-oauth2.conf.ext` with the following content:
+
+``` txt
+introspection_mode = post
+introspection_url = https://dovecot:@/api/o/introspect/
+username_attribute = username
+tls_ca_cert_file = /etc/ssl/certs/ca-certificates.crt
+active_attribute = active
+active_value = true
+```
+
+Replace `` with the value you obtained earlier.
+
+## LDAP
+
+To make the LDAP authentication work, edit the `conf.d/10-auth.conf`
+and uncomment the following line at the end:
+
+``` txt
+!include auth-ldap.conf.ext
+```
+
+Then edit the `conf.d/auth-ldap.conf.ext` and edit the passdb section as following.
+
+You should comment the userdb section, which will be managed by SQL with modoboa database:
+
+``` txt
+passdb {
+ driver = ldap
+
+ # Path for LDAP configuration file, see example-config/dovecot-ldap.conf.ext
+ args = /etc/dovecot/dovecot-ldap.conf.ext
+}
+
+#userdb {
+ #driver = ldap
+ #args = /etc/dovecot/dovecot-ldap.conf.ext
+
+ # Default fields can be used to specify defaults that LDAP may override
+ #default_fields = home=/home/virtual/%u
+#}
+```
+
+Your own dovecot LDAP configuration file is now `/etc/dovecot/dovecot-ldap.conf.ext`.
+
+You can add your default LDAP conf in it, following the
+[official documentation](https://doc.dovecot.org/configuration_manual/authentication/ldap/).
+
+### Synchronize dovecot LDAP conf with modoboa LDAP conf
+
+To make dovecot LDAP configuration synchronized with modoboa LDAP configuration,
+you should create a dedicated dovecot conf file.
+
+At the end of your dovecot configuration file (`dovecot-ldap.conf.ext` ), add the following line:
+
+``` txt
+!include_try dovecot-modoboa.conf.ext
+```
+
+Then, set modoboa parameter *Enable Dovecot LDAP sync* to *Yes*.
+Then set the *Dovecot LDAP config file* following the previous step
+(*/etc/dovecot/dovecot-modoboa.conf.ext* in the example)
+
+The last step is to add the command **update_dovecot_conf** to the cron
+job of modoboa. Then, each time your modoboa LDAP configuration is
+updated, your dovecot LDAP configuration will also be.
+
+## LMTP
+
+[Local Mail Transport Protocol](http://en.wikipedia.org/wiki/Local_Mail_Transfer_Protocol)
+is used to let Postfix deliver messages to Dovecot.
+
+First, make sure the protocol is activated by looking at the `protocols`
+setting (generally inside `dovecot.conf`).
+
+It should be similar to the following example:
+
+```txt
+protocols = imap pop3 lmtp
+```
+
+Then, open the `conf.d/10-master.conf` ,
+look for `lmtp` service definition and add the following content inside:
+
+```txt
+service lmtp {
+ # stuff before
+ unix_listener /var/spool/postfix/private/dovecot-lmtp {
+ mode = 0600
+ user = postfix
+ group = postfix
+ }
+ # stuff after
+}
+```
+
+We assume here that Postfix is *chrooted* within
+`/var/spool/postfix` .
+
+Finally, open the `conf.d/20-lmtp.conf` and modify it as follows:
+
+```txt
+protocol lmtp {
+ postmaster_address = postmaster@
+ mail_plugins = $mail_plugins quota sieve
+}
+```
+
+Replace `` by the appropriate value.
+
+::: info
+If you don't plan to apply quota or to use filters, just adapt the
+content of the `mail_plugins` setting.
+:::
+
+## Quota
+
+Modoboa lets adminstrators define per-domain and/or per-account limits
+(quota). It also lists the current quota usage of each account. Those
+features require Dovecot to be configured in a specific way.
+
+Inside `conf.d/10-mail.conf` ,
+add the `quota` plugin to the default activated ones:
+
+```txt
+mail_plugins = quota
+```
+
+Inside `conf.d/10-master.conf` , update
+the `dict` service to set proper permissions:
+
+```txt
+service dict {
+ # If dict proxy is used, mail processes should have access to its socket.
+ # For example: mode=0660, group=vmail and global mail_access_groups=vmail
+ unix_listener dict {
+ mode = 0600
+ user =
+ #group =
+ }
+}
+```
+
+Inside `conf.d/20-imap.conf` , activate
+the `imap_quota` plugin:
+
+```txt{4}
+protocol imap {
+ # ...
+
+ mail_plugins = $mail_plugins imap_quota
+
+ # ...
+}
+```
+
+Inside `dovecot.conf` , activate the quota
+SQL dictionary backend:
+
+```txt{2}
+dict {
+ quota = :/etc/dovecot/dovecot-dict-sql.conf.ext
+}
+```
+
+Inside `conf.d/90-quota.conf` , activate
+the *quota dictionary* backend:
+
+```txt
+plugin {
+ quota = dict:User quota::proxy::quota
+}
+```
+
+It will tell Dovecot to keep quota usage in the SQL dictionary.
+
+Finally, edit the `dovecot-dict-sql.conf.ext`{.interpreted-text
+role="file"} file and put the following content inside:
+
+```txt
+connect = host= dbname= user= password=
+# SQLite users
+# connect = /path/to/the/database.db
+
+map {
+ pattern = priv/quota/storage
+ table = admin_quota
+ username_field = username
+ value_field = bytes
+}
+map {
+ pattern = priv/quota/messages
+ table = admin_quota
+ username_field = username
+ value_field = messages
+}
+```
+
+### PostgreSQL users
+
+#### Database schema update
+
+The `admin_quota` table is created by Django but unfortunately it
+doesn't support `DEFAULT` constraints (it only simulates them when the
+ORM is used).
+
+As PostgreSQL is a bit strict about constraint violations,
+you must execute the following query manually:
+
+```sql
+db=> ALTER TABLE admin_quota ALTER COLUMN bytes SET DEFAULT 0;
+db=> ALTER TABLE admin_quota ALTER COLUMN messages SET DEFAULT 0;
+````
+
+#### Trigger
+
+As indicated on [Dovecot's doc](https://doc.dovecot.org/2.4.2/core/plugins/quota.html#quota-plugin-quota),
+you need a trigger to properly update the quota.
+
+A working copy of this trigger is available on
+[Github](https://raw.githubusercontent.com/modoboa/modoboa-installer/master/modoboa_installer/scripts/files/dovecot/install_modoboa_postgres_trigger.sql).
+
+Download this file and copy it on the server running postgres.
+
+Then, execute the following commands:
+
+```shell
+$ su - postgres
+$ psql [modoboa database] < /path/to/modoboa_postgres_trigger.sql
+$ exit
+```
+
+Replace `[modoboa database]` by the appropriate value.
+
+### Forcing recalculation
+
+For existing installations, *Dovecot* (\> 2) offers a command to
+recalculate the current quota usages. For example, if you want to update
+all usages, run the following command:
+
+```shell
+$ doveadm quota recalc -A
+```
+
+Be carefull, it can take a while to execute.
+
+## ManageSieve/Sieve
+
+Modoboa lets users define filtering rules from the web interface.
+
+To do so, it requires *ManageSieve* to be activated on your server.
+
+Inside `conf.d/20-managesieve.conf`, make sure the following lines are uncommented:
+
+```txt
+protocols = $protocols sieve
+
+service managesieve-login {
+ # ...
+}
+
+service managesieve {
+ # ...
+}
+
+protocol sieve {
+ # ...
+}
+```
+
+Finally, configure the `sieve` plugin by editing the `conf.d/90-sieve.conf` file.
+Put the follwing caontent inside:
+
+```txt
+plugin {
+ # Location of the active script. When ManageSieve is used this is actually
+ # a symlink pointing to the active script in the sieve storage directory.
+ sieve = ~/.dovecot.sieve
+
+ #
+ # The path to the directory where the personal Sieve scripts are stored. For
+ # ManageSieve this is where the uploaded scripts are stored.
+ sieve_dir = ~/sieve
+}
+```
+
+Restart Dovecot.
+
+Now, you can go to the `postfix` section to finish the installation.
+
+## Last-login tracking
+
+To update the `last_login` attribute of an account after a succesful
+IMAP or POP3 login, you can configure a [post-login
+script](https://wiki.dovecot.org/PostLoginScripting).
+
+Open `conf.d/10-master.conf` add the following configuration
+(`imap` and `pop3` services are already defined,
+you just need to update them):
+
+```txt
+service imap {
+ executable = imap postlogin
+}
+
+service pop3 {
+ executable = pop3 postlogin
+}
+
+service postlogin {
+ executable = script-login /usr/local/bin/postlogin.sh
+ user = modoboa
+ unix_listener postlogin {
+ }
+}
+```
+
+Then, you must create a script named
+`/usr/local/bin/postlogin.sh` .
+
+According to your database engine, the content will differ.
+
+::: code-group
+
+``` bash [PostgreSQL]
+#!/bin/sh
+
+psql -c "UPDATE core_user SET last_login=now() WHERE username='$USER'" > /dev/null
+
+exec "$@"
+```
+
+``` bash [MySQL]
+#!/bin/sh
+
+DBNAME=XXX
+DBUSER=XXX
+DBPASSWORD=XXX
+
+echo "UPDATE core_user SET last_login=now() WHERE username='$USER'" | mysql -u $DBUSER -p$DBPASSWORD $DBNAME
+
+exec "$@"
+```
+
+:::
\ No newline at end of file
diff --git a/doc-ng/installation/installer.md b/doc-ng/installation/installer.md
new file mode 100644
index 000000000..32f0de028
--- /dev/null
+++ b/doc-ng/installation/installer.md
@@ -0,0 +1,62 @@
+---
+title: Guide to install modoboa with the automatic installer
+description: This guide describe how to install modoboa with the automatic installer
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, installation, webserver, nginx, apache, wsgi'
+---
+
+# Automatic Installer
+
+## Introduction
+
+If you start from scratch and want to deploy a complete mail server,
+
+you will love the [modoboa installer](https://github.com/modoboa/modoboa-installer)!
+
+It is the easiest and the quickest way to setup a fully functional server
+(modoboa, postfix, dovecot, amavis and more) on one machine.
+
+## How to start
+
+::: warning
+For now, only Debian based Linux distributions are supported.
+
+We do our best to improve compatibility but if you use another `Linux`
+or a `UNIX` system, you will have to install Modoboa `manually `.
+:::
+
+To use it, just run the following commands in your terminal:
+
+``` shell
+$ git clone https://github.com/modoboa/modoboa-installer
+$ cd modoboa-installer
+$ sudo ./run.py
+```
+
+::: warning
+if you get this warning
+
+```console
+'/usr/bin/env: 'python': No such file or directory',
+```
+
+do make sure sure python is installed on your server.
+
+Sometimes python is installed but the installer can't detect it or which python version to run,
+especially on a debian based system, check the `PATH` variable for the current user.
+
+Then run this command first.
+
+``` shell
+$ sudo apt-get install python3-virtualenv python3-pip
+```
+
+:::
+
+For the sake of simplicity, you can also install the `python-is-python3` package.
+
+It allows you to use `python` command and point to the python3 runtime.
+
+Wait a few minutes and you're done 😎
\ No newline at end of file
diff --git a/doc-ng/installation/manual.md b/doc-ng/installation/manual.md
new file mode 100644
index 000000000..3a531fde0
--- /dev/null
+++ b/doc-ng/installation/manual.md
@@ -0,0 +1,22 @@
+---
+title: manual step to made modoboa installation
+description: This guide describe all steps to install modoboa
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, installation, postfix, dovecot, opendkim, amavis, radicale, nginx'
+---
+
+# Manual installation
+
+For those who need a manual installation or who just want to setup a specific part,
+
+here are the steps you must follow:
+
+* [Modoboa](modoboa)
+* [Web Server](webserver)
+* [Dovecot](dovecot)
+* [Postfix](postfix)
+* [OpenDKIM](opendkim)
+* [Radicale](radicale)
+* [Amavis](amavis)
diff --git a/doc-ng/installation/modoboa.md b/doc-ng/installation/modoboa.md
new file mode 100644
index 000000000..eeaac2919
--- /dev/null
+++ b/doc-ng/installation/modoboa.md
@@ -0,0 +1,326 @@
+---
+title: manual step to install core component on modoboa
+description: This guide describe how to install core on the modoboa platform
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, installation, core, mysql, postgresql, cron'
+---
+
+# Modoboa
+
+This section describes the installation of the web interface (a [Django](https://www.djangoproject.com/) project).
+
+This proposed configuration is the one you can find with `modoboa-installer`.
+
+For up to date configuration, [take a look here](https://github.com/modoboa/modoboa-installer/tree/master/modoboa_installer/scripts/files/modoboa/)
+
+## Prepare the system
+
+First of all, we recommend the following context:
+
+* A dedicated system user
+* A [virtual environment](https://packaging.python.org/tutorials/installing-packages/#creating-virtual-environments)
+ to install the application because it will isolate it (and its dependencies) from the rest of your system
+
+The following example illustrates how to realize this on Debian-based distributions using [virtualenv](https://virtualenv.pypa.io/en/stable/):
+
+``` shell
+$ sudo apt-get install virtualenv python3-pip
+$ sudo useradd modoboa # create a dedicated user
+$ sudo -u modoboa -i bash # log in as the newly created user
+$ virtualenv --python python3 ./env # create the virtual environment
+$ source ./env/bin/activate # activate the virtual environment
+```
+
+::: warning
+Modoboa depends on external tools and some of them require compilation
+so you need a compiler and a few C libraries.
+:::
+
+::: tip INFORMATION
+Make sure to install the following system packages according to your distribution:
+
+* Debian / Ubuntu
+ * build-essential
+ * python3-dev
+ * libxml2-dev
+ * libxslt-dev
+ * libjpeg-dev
+ * librrd-dev
+ * rrdtool
+ * libffi-dev
+ * libssl-dev pkg-config
+ * libcairo2-dev
+
+* CentOS / Redhat
+ * gcc
+ * gcc-c++
+ * python3-devel
+ * libxml2-devel
+ * libxslt-devel
+ * libjpeg-turbo-devel
+ * rrdtool-devel rrdtool
+ * libffi-devel
+:::
+
+::: info
+
+Alternatively, you could rely on your distribution packages for the
+Modoboa dependencies which require compilation
+* e.g. `rrdtool` - if the version is compatible.
+ In this case, you have to create your virtual environment
+ with the `--system-site-packages` option, and the required
+ system packages will be:
+
+ * python3-wheel
+ * python3-rrdtool
+ * rrdtool
+:::
+
+Then, install Modoboa by running:
+
+``` console
+(env)$ pip install modoboa
+```
+
+## Database
+
+::: warning
+This documentation does not cover the installation of a database server
+but only the setup of a functional database that Modoboa will use.
+:::
+
+Thanks to Django, Modoboa is compatible with the following databases:
+
+- PostgreSQL
+- MySQL / MariaDB
+- SQLite
+
+Since the last one does not require particular actions, only the first
+two ones are described.
+
+You should also read the notes for those database backends on the
+[official Django documentation](https://docs.djangoproject.com/en/stable/ref/databases/).
+
+### PostgreSQL
+
+Install the corresponding Python binding:
+
+``` shell
+(env)$ pip install psycopg[binary]>=3.1
+```
+
+::: info
+Ubuntu and Debian repositories contain python-psycopgX package but
+starting modoboa 2.2.0 minimum version required is 3.1 and it has not
+yet it these distro yet Prior modoboa version can run with `python3-psycopg2`.
+:::
+
+Then, create a user and a database.
+
+For example, to create the `modoboa` database owned by a `modoboa` user,
+run the following commands on your PostgreSQL server as `root` or admin user:
+
+``` bash
+$ sudo -l -u postgres createuser --no-createdb modoboa
+$ sudo -l -u postgres createdb --owner=modoboa modoboa
+```
+
+### MySQL / MariaDB
+
+Install the corresponding Python binding:
+
+``` console
+(env)$ pip install mysqlclient
+```
+
+::: tip
+Alternatively, you can install the `python3-mysqldb` package instead on
+Debian-based distributions if your virtual environment was created with
+`--system-site-packages` option.
+:::
+
+:::: info NOTE
+MariaDB 10.2 (and newer) require mysqlclient 1.3.11 (or newer).
+::::
+
+Then, create a user and a database. For example, to create the `modoboa`
+database owned by a `modoboa` user, run the following SQL commands:
+
+``` sql
+CREATE DATABASE modoboa;
+CREATE USER 'modoboa'@'localhost' IDENTIFIED BY 'my-strong-password-here';
+GRANT ALL PRIVILEGES ON modoboa.* TO 'modoboa'@'localhost';
+```
+
+## Deploy an instance {#deployment}
+
+`modoboa-admin.py` is a command line tool that lets you deploy a
+*ready-to-use* Modoboa site. To create a new instance into `./instance`,
+you just have to run the following command:
+
+``` shell
+(env)$ modoboa-admin.py deploy instance --collectstatic \
+ --domain --dburl default:
+```
+
+::: info
+
+You can install additional extensions during the deploy process. To do
+so, use the `--extensions` option which accepts a list of names as
+argument (`--extensions ext1 ext2 ...`). If you want to install all
+extensions, just use the `all` keyword like this `--extensions all`.
+
+If you choose to install extensions one at a time, you will have to add
+their names in settings.py to `MODOBOA_APPS`. Also ensure that you have
+the line `from modoboa_amavis.settings import *` at the end of this file
+and possibly perform more steps. Check the documentation/readme of each
+extensions for specific Instructions.
+
+The list of available extensions can be found on the `index
+page <../index>`{.interpreted-text role="doc"}. Instructions to install
+them are available on each extensions page.
+:::
+
+::: tip TIPS
+
+You can specify more than one database connection using the `--dburl`
+option. Multiple connections are differentiated by a prefix.
+
+The primary connection must use the `default:` prefix (as shown in the
+example above). For the [amavis](http://modoboa-amavis.readthedocs.org)
+extension, use the `amavis:` prefix. For example:
+`--dburl default: amavis:`.
+
+A database url should meet the following syntax
+`://[user:pass@][host:port]/dbname` **OR**
+`sqlite:////full/path/to/your/database/file.sqlite`.
+
+Fox example, if you were using postgres, you could setup your command
+like this:
+`modoboa-admin.py deploy instance_name --collectstatic --domain example.com --dburl default:postgres://user:pass@[localhost]/modoboa`
+:::
+
+The command will ask you a few questions, answer them and you\'re done.
+
+If you need a **silent installation** (e.g. if you\'re using Salt-Stack,
+Ansible or whatever), it's possible to supply the database credentials
+as command line arguments.
+
+You can consult the complete option list by running the following command:
+
+``` shell
+(env)$ modoboa-admin.py help deploy
+```
+
+## Cron jobs
+
+A few recurring jobs must be configured to make Modoboa works as expected.
+
+Create a new file, for example `/etc/cron.d/modoboa` and put the following content inside:
+
+``` shell
+#
+# Modoboa specific cron jobs
+#
+PYTHON=
+INSTANCE=
+
+# Operations on mailboxes
+* * * * * $PYTHON $INSTANCE/manage.py handle_mailbox_operations
+
+# Generate DKIM keys (they will belong to the user running this job)
+* * * * * root umask 077 && $PYTHON $INSTANCE/manage.py modo manage_dkim_keys
+
+# Sessions table cleanup
+0 0 * * * root $PYTHON $INSTANCE/manage.py clearsessions
+# Logs table cleanup
+0 0 * * * root $PYTHON $INSTANCE/manage.py cleanlogs
+# Logs parsing
+*/15 * * * * root $PYTHON $INSTANCE/manage.py logparser &> /dev/null
+0 * * * * modoboa $PYTHON $INSTANCE/manage.py update_statistics
+# DNSBL checks
+*/30 * * * * modoboa $PYTHON $INSTANCE/manage.py modo check_mx
+# Public API communication
+0 * * * * modoboa $PYTHON $INSTANCE/manage.py communicate_with_public_api
+```
+
+::: tip TIPS
+
+**🥵 potential high load configuration**
+Please note that above crontab might not be ideal on high load systems.
+If you receive a fairly high amount of emails per day, you may want to
+run modoboas logparser tasks *once per night*.
+
+This has the down side that the statistic graph and message log within
+the UI are updated once per day only.
+:::
+
+## Policy daemon {#policy_daemon}
+
+Modoboa comes with a built-in
+[Policy Daemon for Postfix](http://www.postfix.org/SMTPD_POLICY_README.html).
+Current features are:
+
+* Define daily sending limits for domains and/or accounts
+
+A [redis server](https://redis.io/) is required to run this new daemon.
+
+You can launch it manually using the following command:
+
+``` shell
+(env) $ python manage.py policy_daemon
+```
+
+But we recommend an automatic start using `systemd` or `supervisor`.
+
+Here is a configuration example for `supervisor`:
+
+``` ini
+[program:policyd]
+autostart=true
+autorestart=true
+command= /manage.py policy_daemon
+directory=
+redirect_stderr=true
+user=modoboa
+numprocs=1
+```
+
+It will listen by default on `127.0.0.1` and port `9999`.
+
+The policy daemon won't do anything unless you tell
+`postfix ` to use it.
+
+## RQ daemon
+
+Modoboa uses [RQ](https://python-rq.org) as a job handler for async task.
+
+A [redis server](https://redis.io/) is required to run this new daemon.
+
+A worker needs to be launched in the venv.
+
+You can launch it manually using the following command:
+
+``` shell
+(env)> rq worker high default low
+```
+
+But we recommend an automatic start using `systemd` or `supervisor`.
+
+Here is a configuration example for `supervisor`:
+
+``` ini
+[program:modoboa-dkim-worker]
+autostart=true
+autorestart=true
+command= /manage.py rqworker dkim
+directory=
+user=
+redirect_stderr=true
+numprocs=1
+stopsignal=TERM
+```
+
+Now you can continue to the [webserver](webserver) section.
\ No newline at end of file
diff --git a/doc-ng/installation/opendkim.md b/doc-ng/installation/opendkim.md
new file mode 100644
index 000000000..7c1218cd0
--- /dev/null
+++ b/doc-ng/installation/opendkim.md
@@ -0,0 +1,88 @@
+---
+title: manual step to install opendkim component on modoboa
+description: This guide describe how to install opendkim on the modoboa platform
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, installation, opendkim, mysql, postgresql, dkim'
+---
+
+# OpenDKIM
+
+Modoboa can generate [DKIM](https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail) keys
+for the hosted domains but it won't sign or check messages.
+
+To do that, you need a dedicated software like [OpenDKIM](http://opendkim.org/).
+
+::: warning
+The cron job in charge of creating DKIM keys must be run using the same
+user than `OpenDKIM` (ie. opendkim in most cases).
+:::
+
+## Database
+
+Since keys related information is stored in Modoboa's database, you
+need to tell OpenDKIM how it can access it.
+
+First, make sure to install the required additional packages on your
+system (`libopendbx1-*` on debian based distributions or `opendbx-*` on
+CentOS, the complete name depends on your database engine).
+
+Then, insert the following SQL view into Modoboa\'s database:
+
+::: code-group
+
+```sql [PostgreSQL]
+CREATE OR REPLACE VIEW dkim AS (
+ SELECT id, name as domain_name, dkim_private_key_path AS private_key_path,
+ dkim_key_selector AS selector
+ FROM admin_domain WHERE enable_dkim
+);
+```
+
+```sql [MySQL]
+CREATE OR REPLACE VIEW dkim AS (
+ SELECT id, name as domain_name, dkim_private_key_path AS private_key_path,
+ dkim_key_selector AS selector
+ FROM admin_domain WHERE enable_dkim=1
+);
+```
+:::
+
+## Configuration
+
+You should find OpenDKIM\'s configuration file at `/etc/opendkim.conf`.
+
+Add the following content to it:
+
+``` txt
+KeyTable dsn:://:@//table=dkim?keycol=id?datacol=domain_name,selector,private_key_path
+SigningTable dsn:://:@//table=dkim?keycol=domain_name?datacol=id
+Socket inet:12345@localhost
+```
+
+Replace values between `<>` by yours. Accepted values for `driver` are
+`pgsql` or `mysql`. Make sure the user you specify has read permission
+on the view created previously.
+
+If you run a debian based system, make sure to adjust the following
+setting in the `/etc/default/opendkim` file:
+
+``` txt
+SOCKET=inet:12345@127.0.0.1
+```
+
+Eventually, `reload OpenDKIM`.
+
+## Postfix integration
+
+Add the following lines to the `/etc/postfix/main.cf` file:
+
+``` txt{1,2}
+smtpd_milters = inet:127.0.0.1:12345
+non_smtpd_milters = inet:127.0.0.1:12345
+milter_default_action = accept
+milter_content_timeout = 30s
+```
+
+and `reload postfix`.
\ No newline at end of file
diff --git a/doc-ng/installation/postfix.md b/doc-ng/installation/postfix.md
new file mode 100644
index 000000000..f8523af92
--- /dev/null
+++ b/doc-ng/installation/postfix.md
@@ -0,0 +1,112 @@
+---
+title: manual step to install postfix component on modoboa
+description: This guide describe how to install postfix on the modoboa platform
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, installation, postfix, mysql, postgresql, smtp, alias, relay, virtual'
+---
+
+# Postfix
+
+This section gives an example about building a simple virtual hosting
+configuration with *Postfix*.
+Refer to the [official documentation](http://www.postfix.org/VIRTUAL_README.html)
+for more explanation.
+
+## Map files
+
+You first need to create configuration files (or map files) that will be
+used by Postfix to lookup into Modoboa tables.
+
+To automaticaly generate the requested map files and store them in a
+directory, run the following command:
+
+``` bash
+$ cd
+$ python manage.py generate_postfix_maps --destdir
+```
+
+`` is the directory where the files will be stored. Answer
+the few questions and you\'re done.
+
+## Configuration
+
+Use the following configuration in the `/etc/postfix/main.cf` file.
+
+For TLS configuration, you can look at the
+[used configuration](https://github.com/modoboa/modoboa-installer/blob/master/modoboa_installer/scripts/files/postfix/main.cf.tpl)
+
+for [modoboa installer](https://github.com/modoboa/modoboa-installer). (this is just one possible configuration):
+
+```txt
+# Stuff before
+unknown_local_recipient_reject_code = 550
+unverified_recipient_reject_code = 550
+
+# appending .domain is the MUA's job.
+append_dot_mydomain = no
+
+## Proxy maps
+proxy_read_maps =
+ proxy:unix:passwd.byname
+ proxy::/etc/postfix/sql-domains.cf
+ proxy::/etc/postfix/sql-domain-aliases.cf
+ proxy::/etc/postfix/sql-aliases.cf
+ proxy::/etc/postfix/sql-relaydomains.cf
+ proxy::/etc/postfix/sql-maintain.cf
+ proxy::/etc/postfix/sql-relay-recipient-verification.cf
+ proxy::/etc/postfix/sql-sender-login-map.cf
+ proxy::/etc/postfix/sql-spliteddomains-transport.cf
+ proxy::/etc/postfix/sql-transport.cf
+
+## Virtual transport settings
+virtual_transport = lmtp:unix:private/dovecot-lmtp
+
+virtual_mailbox_domains = proxy::/etc/postfix/sql-domains.cf
+virtual_alias_domains = proxy::/etc/postfix/sql-domain-aliases.cf
+virtual_alias_maps = proxy::/etc/postfix/sql-aliases.cf
+
+## Relay domains
+relay_domains = proxy::/etc/postfix/sql-relaydomains.cf
+transport_maps =
+ proxy::/etc/postfix/sql-transport.cf
+ proxy::/etc/postfix/sql-spliteddomains-transport.cf
+
+smtpd_recipient_restrictions =
+ permit_mynetworks
+ permit_sasl_authenticated
+ check_recipient_access
+ proxy::/etc/postfix/sql-maintain.cf
+ proxy::/etc/postfix/sql-relay-recipient-verification.cf
+ reject_unverified_recipient
+ reject_unauth_destination
+ reject_non_fqdn_sender
+ reject_non_fqdn_recipient
+ reject_non_fqdn_helo_hostname
+
+smtpd_sender_login_maps = proxy::/etc/postfix/sql-sender-login-map.cf
+
+# Stuff after
+```
+
+Replace `` by the name of the database you use, and `Restart Postfix`.
+
+## Policy daemon
+
+If you want to enable the built-in policy daemon, add the following
+content to the `/etc/postfix/main.cf` file:
+
+```txt{3}
+ smtpd_recipient_restrictions =
+ # ...
+ check_policy_service inet:localhost:9999
+ # ...
+```
+
+And `reload postfix`.
+
+::: warning
+The `check_policy_service` line must be placed before the
+`permit_mynetworks` one, otherwise the daemon won't be called.
+:::
diff --git a/doc-ng/installation/radicale.md b/doc-ng/installation/radicale.md
new file mode 100644
index 000000000..be864bf2a
--- /dev/null
+++ b/doc-ng/installation/radicale.md
@@ -0,0 +1,151 @@
+---
+title: manual step to install radicale component on modoboa
+description: This guide describe how to install radicale on the modoboa platform
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, installation, radicale, oauth2'
+---
+
+# Radicale
+
+This page is displaying how to install and use radicale when either
+having installed modoboa manually or with the dedicated installer.
+
+## OAuth 2 authentication
+
+::: tip
+These steps are automatic if you use an up-to-date modoboa-installer to
+install/upgrade.
+:::
+
+### Preparing the usage of radicale (manual install only)
+
+::: warning
+It is assumed you have not installed in the past radicale from your
+package manager and your modoboa installation is working correctly.
+
+If so, you may skip this section and proceed to the next one.
+:::
+
+You will need
+to create directories for the radicale user, needed files and set-up the
+account (location may change depending on your OS and initial set-up).
+
+``` shell
+$ sudo mkdir /var/lib/radicale /etc/radicale
+$ sudo touch /etc/radicale/{rights,config}
+$ sudo adduser --system --home /var/lib/radicale/ --no-create-home --gecos "Radicale CalDAV server" --group --disabled-password --quiet radicale
+$ sudo chown -R radicale /etc/radicale
+$ sudo chown /var/lib/radicale
+```
+
+### Creation of the virtual environment (manual install only)
+
+``` shell
+$ sudo -u radicale virtualenv /var/lib/radicale/env
+```
+
+### Installing radicale oauth plugin
+
+You need to install `radicale-modoboa-auth-oauth2` python package inside
+radicale venv. If you used the installer you can use the following
+commands:
+
+``` shell
+$ sudo -u radicale -i bash
+$ cd ~
+$ env/bin/pip install radicale-modoboa-auth-oauth2
+```
+
+For manual installations, this will do the job:
+
+``` shell
+$ sudo -u radicale /var/lib/radicale/env/bin/pip install radicale-modoboa-auth-oauth2
+```
+
+Please note because the plugin offered by modoboa's team is listing
+radicale as a dependency you do not need to explicitely install
+radicale.
+
+### Oauth application creation
+
+You first need to register Radicale as an authorized consumer of the
+OAuth2 authentication service provided by Modoboa. To do so, create an
+application with the following commands:
+
+``` shell
+$ cd
+$ python manage.py createapplication --name=Radicale --skip-authorization --client-id=radicale confidential client-credentials
+```
+
+On success, you should see an output similar to:
+
+```console
+New application Radicale created successfully.
+client_secret: XXXX
+```
+
+To enable OAuth2 authentication in Radicale, edit the `/etc/radicale/config` file
+and update the [type] in [\[auth\]] to:
+```txt
+type = radicale_modoboa_auth_oauth2
+````
+
+Then, add the following line in \`\[auth\]\`:
+
+```txt
+oauth2_introspection_endpoint = https://radicale:@/api/o/introspect/
+````
+
+Replace `` with the value you obtained earlier.
+
+Your `/etc/radicale/config`{.interpreted-text role="file"} should look
+like this (listening address and port may vary):
+
+```ini
+[server]
+hosts = 0.0.0.0:5232,[::]:5232
+
+[auth]
+type = radicale_modoboa_auth_oauth2
+oauth2_introspection_endpoint = https://radicale:@/api/o/introspect/
+
+[rights]
+type = from_file
+file = /etc/radicale/rights
+
+[storage]
+filesystem_folder = /var/lib/radicale/collections
+````
+
+With that set-up, radicale should be working when managing calendars
+through modoboa's web interface but [not]{#not}\_ when using other
+clients like Thunderbird.
+
+### Modifications for using external clients (optional)
+
+To ensure other clients can identify, you need to modify radicale by
+adding in the config file under the \[auth\] section:
+
+```txt
+dovecot_socket = /run/dovecot/auth-radicale
+```
+
+and in `10-master.conf` for dovecot:
+
+```txt
+unix_listener auth-radicale {
+ mode = 0660
+ user = radicale
+ group = dovecot
+}
+```
+
+Then, restart both `modoboa` and `radicale` to enjoy managing calendars (ICS type) with your favorite client.
+
+### Reverse proxy for radicale (optional)
+
+At this stage, no encryption is provided, and you may use a reverse proxy as described in
+
+[Radicale's documentation](https://radicale.org/v3.html#reverse-proxy).
\ No newline at end of file
diff --git a/doc-ng/installation/requirements.md b/doc-ng/installation/requirements.md
new file mode 100644
index 000000000..6a1c3e4ed
--- /dev/null
+++ b/doc-ng/installation/requirements.md
@@ -0,0 +1,35 @@
+---
+title: Requirements to install Modoboa on a server
+description: Minimal requirements to install Modoboa on a server
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, installation, requirements, cpu, ram, disk, python'
+---
+
+# Requirements
+
+You will need a Server to perform well with at least:
+
+- CPU: 4
+- RAM: 4GB
+- Disk: 10GB
+- Python 3.10+
+- Postgres 14+ or MariaDB 10.5+ or MySQL 8.0.11+ or sqlite3
+
+::: warning
+
+**👀 Heads up for modoboa versions 1.15 and later**
+
+Python 2 support has been dropped with modoboa version 1.15. If you
+still have Python 2 installed on your system either uninstall it or
+force modoboa user to run with Python 3.
+:::
+
+If you want to run virus scan on your server you will need **4GB** of
+RAM.
+
+You have 2 possibilities to install Modoboa
+
+* [Manual Installation](manual)
+* [Automatic Install](installer)
diff --git a/doc-ng/installation/upgrade.md b/doc-ng/installation/upgrade.md
new file mode 100644
index 000000000..00cfd9e24
--- /dev/null
+++ b/doc-ng/installation/upgrade.md
@@ -0,0 +1,2122 @@
+---
+title: Guide to update a Modoboa instance to the last version
+description: This guide perform how to upgrade your current Modoboa instance to the last version available
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, installation, upgrade, procedure'
+---
+
+# Upgrade
+
+## Modoboa
+
+::: tip
+In this doc, `by default` means that you used [modoboa installer](https://github.com/modoboa/modoboa-installer)
+to install modoboa and that your didn't change your configuration.
+:::
+
+::: warning
+The new version you are going to install may need to modify your database.
+
+Before you start, make sure to backup everything !!!
+::::
+
+Most of the time, upgrading your installation to a newer Modoboa version only requires a few actions.
+
+In every case, you will need to apply the specific actions if the version you are installing requires it,
+and then apply the general ones.
+
+In case you use a dedicated user and/or a virtualenv, do not forget to use them:
+
+``` bash
+$ sudo -u -i bash
+$ source /bin/activate
+```
+
+::: info Be Careful
+Check the `specific_upgrade_instructions`
+before running the following commands.
+:::
+
+Then, run the following commands:
+
+``` shell
+$ pip install modoboa==
+$ cd
+$ python manage.py migrate
+$ python manage.py collectstatic
+$ python manage.py check --deploy
+$ python manage.py load_initial_data
+```
+
+Then when everything has been updated, restart modoboa's services:
+
+``` shell
+$ sudo systemctl restart uwsgi
+$ sudo systemctl restart supervisor
+```
+
+Finally, restart your web server.
+
+Sometimes, you might need to upgrade postfix map files too.
+
+To do so, just run the `generate_postfix_maps` command on the same directory than
+the one used for installation (`/etc/postfix` by default).
+
+Make sure to use root privileges and run the following command:
+
+``` bash
+> python manage.py generate_postfix_maps --destdir
+```
+
+Then, `reload postfix`.
+
+## Modoboa installer
+
+::: warning
+It is important to keep the installer updated because it works on rolling release.
+:::
+
+To do that, go to modoboa-installer directory then:
+
+``` shell
+$ git fetch && git pull
+```
+
+If some error appears, you may try this:
+
+``` shell
+$ git reset --hard origin/master
+```
+
+## Extensions
+
+::: warning
+Do not open an issue after an upgrade if you have not updated your extensions !
+:::
+
+If a new version is available for an extension you're using, it is recommanded to install it.
+
+It may even be required. Upgrading an extensions is pretty and the procedure is almost
+the same than the one used for Modoboa.
+
+In case you use a dedicated user and/or a virtualenv, **do not forget** to use them:
+
+``` bash
+$ sudo -u -i bash
+$ source /bin/activate
+```
+
+Then, run the following commands:
+
+``` bash
+$ pip install ==
+$ cd
+$ python manage.py migrate
+$ python manage.py collectstatic
+$ python manage.py check --deploy
+```
+
+Finally, restart your web server.
+
+It is a generic upgrade procedure which will be enough most of the time
+but it is generally a good idea to check the associated documentation.
+
+## Rebuild Virtual Environment
+
+If you need to rebuild you virtualenv, you can check this [part of the documentation](/rebuild_venv)
+
+## Specific instructions
+
+The section below containt specific migration steps to upgrade Modoboa instance.
+
+## Version 2.6.1
+
+### Required changes to `settings.py`
+
+* Add the following lines to `LOGGING` variable:
+
+``` python
+'handlers': {
+ ...
+ 'syslog': {
+ 'class': 'logging.handlers.SysLogHandler',
+ 'facility': SysLogHandler.LOG_SYSLOG,
+ 'formatter': 'syslog'
+ },
+ ...
+},
+'loggers': {
+ ...
+ 'modoboa.dns': {
+ 'handlers': ['syslog'],
+ 'level': 'INFO',
+ 'propagate': False
+ },
+ ...
+}
+```
+
+## Version 2.6.0
+
+New built-in auto-configuration service.
+
+### Required changes to `settings.py`
+
+* Add the following line to `MODOBOA_APPS` variable:
+
+ ``` python
+ 'modoboa.autoconfig'
+ ```
+
+* Add the following hostnames to `ALLOWED_HOSTS` variable:
+
+ ``` python
+ 'autoconfig.',
+ 'autodiscover.',
+ ```
+
+* Add the following variable:
+
+ ``` python
+ EMAIL_CLIENT_CONNECTION_SETTINGS = {
+ "imap": {
+ "HOSTNAME": "",
+ "SOCKET_TYPE": "SSL",
+ "PORT": 993,
+ },
+ "smtp": {"HOSTNAME": "", "SOCKET_TYPE": "STARTTLS", "PORT": 587},
+ }
+ ```
+
+* Remove `rest_framework.authentication.SessionAuthentication` from
+ `REST_FRAMEWORK` variable:
+
+ ``` python
+ REST_FRAMEWORK = {
+ # Stuff before...
+ "DEFAULT_AUTHENTICATION_CLASSES": (
+ "oauth2_provider.contrib.rest_framework.OAuth2Authentication",
+ "rest_framework.authentication.TokenAuthentication",
+ ),
+ # Stuff after...
+ }
+ ```
+
+## Version 2.5.0
+
+Amavis plugin integrated into Modoboa core repository.
+
+If you are using Amavis content filter and the quarantine plugin,
+the following upgrade instructions, otherwise proceed with regular upgrade instructions.
+
+### Pre update
+
+* If you are using Postegresql (default option with the installer), run
+ as a root user:
+
+ ``` bash
+ $ sudo -u postgres -i psql -d "modoboa" -c "UPDATE django_content_type SET app_label='amavis' WHERE app_label='modoboa_amavis'"
+ $ sudo -u postgres -i psql -d "modoboa" -c "UPDATE django_migrations SET app='amavis' WHERE app='modoboa_amavis'"
+ ```
+
+* If you are using Mysql, run as a root user:
+
+ ``` bash
+ $ echo "UPDATE django_content_type SET app_label='amavis' WHERE app_label='modoboa_amavis'" | mysql -u root -p modoboa
+ $ echo "UPDATE django_migrations SET app='amavis' WHERE app='modoboa_amavis'" | mysql -u root -p modoboa
+ ```
+
+### Required changes to `settings.py`
+
+* Rename the following extensions in the `MODOBOA_APPS` variable:
+
+ - `modoboa_amavis` -\> `modoboa.amavis`
+
+* Add the following variables:
+
+ ``` python
+ DATABASE_ROUTERS = ["modoboa.amavis.dbrouter.AmavisRouter"]
+
+ AMAVIS_DEFAULT_DATABASE_ENCODING = "LATIN1" # or any value matching your database config
+ ```
+
+* Remove configuration block related to `modoboa_amavis` plugin (these
+ should be at the end of the file)
+
+### Post update
+
+* Run the following command:
+ ```shell
+ $ pip uninstall modoboa_amavis
+ ```
+
+## Version 2.4.2
+
+### Changes to `settings.py`
+
+- Make sure `MODOBOA_APPS` variable contains the following line:
+
+ ``` python
+ 'modoboa.webmail'
+ ```
+
+## Version 2.4.0
+
+::: warning
+The `modoboa-amavis` extension has not been migrated into the core Modoboa package yet.
+
+If you are using it, **you cannot install version 2.4.0 yet**.
+:::
+
+::: tip INFORMATION
+Legacy web interface has been removed and almost all extensions have been integrated to the core Modoboa package.
+:::
+
+### Pre update
+
+* If you are using Postegresql (default option with the installer), run
+ as a root user:
+
+ ``` shell
+ $ sudo -u postgres -i psql -d "modoboa" -c "UPDATE django_content_type SET app_label='autoreply' WHERE app_label='postfix_autoreply'"
+ $ sudo -u postgres -i psql -d "modoboa" -c "UPDATE django_migrations SET app='autoreply' WHERE app='postfix_autoreply'"
+ $ sudo -u postgres -i psql -d "modoboa" -c "UPDATE django_content_type SET app_label='contacts' WHERE app_label='modoboa_contacts'"
+ $ sudo -u postgres -i psql -d "modoboa" -c "UPDATE django_migrations SET app='contacts' WHERE app='modoboa_contacts'"
+ $ sudo -u postgres -i psql -d "modoboa" -c "UPDATE django_content_type SET app_label='calendars' WHERE app_label='modoboa_radicale'"
+ $ sudo -u postgres -i psql -d "modoboa" -c "UPDATE django_migrations SET app='calendars' WHERE app='modoboa_radicale'"
+ ```
+
+* If you are using Mysql, run as a root user:
+
+ ``` bash
+ $ echo "UPDATE django_content_type SET app_label='autoreply' WHERE app_label='postfix_autoreply'" | mysql -u root -p modoboa
+ $ echo "UPDATE django_migrations SET app='autoreply' WHERE app='postfix_autoreply'" | mysql -u root -p modoboa
+ $ echo "UPDATE django_content_type SET app_label='contacts' WHERE app_label='modoboa_contacts'" | mysql -u root -p modoboa
+ $ echo "UPDATE django_migrations SET app='contacts' WHERE app='modoboa_contacts'" | mysql -u root -p modoboa
+ $ echo "UPDATE django_content_type SET app_label='calendars' WHERE app_label='modoboa_radicale'" | mysql -u root -p modoboa
+ $ echo "UPDATE django_migrations SET app='calendars' WHERE app='modoboa_radicale'" | mysql -u root -p modoboa
+ ```
+
+::: warning
+MariaDB 10.7+ users might have login issues (see this [discussion](https://github.com/modoboa/modoboa/discussions/3582)).
+
+For those impacted, run the followed SQL queries:
+
+``` shell
+$ echo "ALTER TABLE `oauth2_provider_idtoken` MODIFY `jti` uuid;" | mysql -u root -p modoboa
+$ echo "ALTER TABLE `oauth2_provider_refreshtoken` MODIFY `token_family` uuid;" | mysql -u root -p modoboa
+```
+:::
+
+### Required changes to `settings.py`
+
+* Rename the following extensions in the `MODOBOA_APPS` variable:
+
+ * `modoboa.postfix_autoreply` -\> `modoboa.autoreply`
+ * `modoboa_contacts` -\> `modoboa.contacts`
+ * `modoboa_radicale` -\> `modoboa.calendars`
+ * `modoboa_webmail` -\> `modoboa.webmail`
+ * `modoboa_rspamd` -\> `modoboa.rspamd`
+
+* Remove configuration blocks related to `modoboa_contacts` and `modoboa_radicale` plugins (these should be at the end of the file)
+
+* Remove `AjaxLoginRedirect` from `MIDDLEWARE` variable:
+
+ ``` python
+ MIDDLEWARE = (
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.locale.LocaleMiddleware',
+ 'x_forwarded_for.middleware.XForwardedForMiddleware',
+ 'corsheaders.middleware.CorsMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+ 'modoboa.core.middleware.LocalConfigMiddleware',
+ 'modoboa.lib.middleware.CommonExceptionCatcher',
+ 'modoboa.lib.middleware.RequestCatcherMiddleware',
+ )
+ ```
+
+* Remove `NEW_ADMIN_URL` variable if found
+
+* Remove `new_admin_url` and `top_notifications` context processors
+
+* Remove `ckeditor` related apps from `INSTALLED_APPS` variable and
+ `CKEDITOR_*` settings
+
+* Remove `STATICFILE_DIRS` variable
+
+### Radicale configuration changes
+
+You must enable oauth2 authentication in Radicale to make the calendar and contact feature work.
+
+Follow this `guide `.
+
+### Post update
+
+* Run the following command:
+ ``` shell
+ $ python manage.py load_initial_data
+ ````
+* Update your `webserver ` configuration
+
+* Remove `autoreply` service definition from postfix's `master.cf` file if you were using it
+
+* Make sure `/etc/uwsgi/sites-enabled/` contains `max-requests=5000` and that
+ `process` is superior or equal to 4.
+
+ Don't forget to restart uwsgi service after the configuration change.
+
+* Then proceed with regular upgrade instructions
+
+## Version 2.3.3
+
+::: warning
+The `crypt ({CRYPT})` password scheme has been marked as deprecated and
+will be removed from Modoboa starting with version **2.4.0**.
+
+If you are still using this weak scheme, you must change to something safer **ASAP**.
+
+Installations still using this scheme won't be able to install Modoboa 2.4 and upper...
+:::
+
+Update your `settings.py` file with following content:
+
+``` python
+SILENCED_SYSTEM_CHECKS = [
+ "security.W019", # modoboa uses iframes to display e-mails
+ "ckeditor.W001", # CKEditor 4.22.1 warning
+]
+```
+
+This will silence a [warning sent by django-ckeditor](https://github.com/modoboa/modoboa/issues/3316).
+
+## Version 2.3.0
+
+::: warning
+For this particular version, it is really important that you apply new migrations **AFTER** the following instructions.
+
+If you don't, you'll get into trouble\...
+:::
+
+### Pre update
+
+::: tip INFORMATION
+To check that you were actually using modoboa-postfix-autoreply you can
+check if this command returns something:
+
+``` bash
+$ cat /srv/modoboa/instance/instance/settings.py | grep "modoboa_postfix_autoreply"
+```
+:::
+
+If you were using [modoboa-postfix-autoreply](https://github.com/modoboa/modoboa-postfix-autoreply)
+extension, you need to perfome a few commands:
+
+* If you are using Postegresql (default option with the installer), run as a root user:
+
+ ``` shell
+ $ sudo -u postgres -i psql -d "modoboa" -c "UPDATE django_content_type SET app_label='postfix_autoreply' WHERE app_label='modoboa_postfix_autoreply'"
+ $ sudo -u postgres -i psql -d "modoboa" -c "UPDATE django_migrations SET app='postfix_autoreply' WHERE app='modoboa_postfix_autoreply'"
+ ```
+
+* If you are using Mysql, run as a root user:
+
+ ``` shell
+ $ echo "UPDATE django_content_type SET app_label='postfix_autoreply' WHERE app_label='modoboa_postfix_autoreply'" | mysql -u root -p modoboa
+ $ echo "UPDATE django_migrations SET app='postfix_autoreply' WHERE app='modoboa_postfix_autoreply'" | mysql -u root -p modoboa
+ ```
+
+### Required changes to `settings.py`
+
+Some changes are required to your `settings.py` file.
+
+These changes will not be performed by the installer.
+
+The location of this file is `/srv/modoboa/instance/instance` by default.
+
+* Append `'import environ'` after `'import os'`:
+
+ ``` python
+ import os
+ import environ
+ ```
+
+* Append the following lines after the line starting with `'BASE_DIR'`:
+
+ ``` python
+ # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
+ BASE_DIR = os.path.realpath(os.path.dirname(os.path.dirname(__file__)))
+ env = environ.Env()
+ environ.Env.read_env(os.path.join(BASE_DIR, '.env'))
+ ```
+
+* Add `oauth2_provider` and `corsheaders` to the `INSTALLED_APPS` list:
+
+ ``` python
+ INSTALLED_APPS = (
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.sites',
+ 'django.contrib.staticfiles',
+ 'reversion',
+ 'ckeditor',
+ 'ckeditor_uploader',
+ 'oauth2_provider', # [!code ++]
+ 'corsheaders', # [!code ++]
+ 'rest_framework',
+ 'rest_framework.authtoken',
+ 'drf_spectacular',
+ 'phonenumber_field',
+ 'django_otp',
+ 'django_otp.plugins.otp_totp',
+ 'django_otp.plugins.otp_static',
+ 'django_rename_app',
+ 'django_rq',
+ )
+ ```
+
+* The `modoboa-postfix-autoreply` and `modoboa-sievefilters` plugins have been merged into the core.
+
+ Add `'modoboa.postfix_autoreply'` and `modoboa.sievefilters` to `MODOBOA_APPS`:
+
+ ``` python
+ MODOBOA_APPS = (
+ 'modoboa',
+ 'modoboa.core',
+ 'modoboa.lib',
+ 'modoboa.admin',
+ 'modoboa.transport',
+ 'modoboa.relaydomains',
+ 'modoboa.limits',
+ 'modoboa.parameters',
+ 'modoboa.dnstools',
+ 'modoboa.policyd',
+ 'modoboa.maillog',
+ 'modoboa.dmarc',
+ 'modoboa.pdfcredentials',
+ 'modoboa.imap_migration',
+ 'modoboa_postfix_autoreply', # [!code --]
+ 'modoboa_sievefilters', # [!code --]
+ 'modoboa.postfix_autoreply', # [!code ++]
+ 'modoboa.sievefilters', # [!code ++]
+ )
+ ```
+
+ ::: warning
+ Remove any reference to `'modoboa_postfix_autoreply'` AND `modoboa_sievefilters` in this same variable.
+ :::
+
+* Due to the changes to the authentication flow, you need to replace MIDDLEWARE with the following:
+
+ ``` python
+ MIDDLEWARE = (
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.locale.LocaleMiddleware',
+ 'x_forwarded_for.middleware.XForwardedForMiddleware',
+ 'corsheaders.middleware.CorsMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+ 'modoboa.core.middleware.LocalConfigMiddleware',
+ 'modoboa.lib.middleware.AjaxLoginRedirect',
+ 'modoboa.lib.middleware.CommonExceptionCatcher',
+ 'modoboa.lib.middleware.RequestCatcherMiddleware',
+ )
+ ```
+
+* Replace `'modoboa.core.drf_authentication.JWTAuthenticationWith2FA'` by `'oauth2_provider.contrib.rest_framework.OAuth2Authentication'`
+ at the top of the list of `DEFAULT_AUTHENTICATION_CLASSES` (inside `REST_FRAMEWORK` section):
+
+ ``` python
+ 'DEFAULT_AUTHENTICATION_CLASSES': (
+ 'modoboa.core.drf_authentication.JWTAuthenticationWith2FA', # [!code --]
+ 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', # [!code ++]
+ 'rest_framework.authentication.TokenAuthentication',
+ 'rest_framework.authentication.SessionAuthentication',
+ ),
+ ```
+
+Save your modifications at this point and run the `post upgrade commands ` now.
+
+Then, apply this last modification to your settings:
+
+* Run the commands indicated `in the first section ` then add the following content after `MEDIA_ROOT`:
+
+ ``` python
+ # oAuth2 settings
+ OAUTH2_PROVIDER = {
+ 'OIDC_ENABLED': True,
+ 'OIDC_RP_INITIATED_LOGOUT_ENABLED': True,
+ 'OIDC_RP_INITIATED_LOGOUT_ALWAYS_PROMPT': True,
+ 'OIDC_RSA_PRIVATE_KEY': env.str('OIDC_RSA_PRIVATE_KEY', multiline=True),
+ 'SCOPES': {
+ 'openid': 'OpenID Connect scope',
+ 'read': 'Read scope',
+ 'write': 'Write scope',
+ 'introspection': 'Introspect token scope',
+ },
+ 'DEFAULT_SCOPES': ['openid', 'read', 'write'],
+ }
+
+ # If CORS fail, you might want to try to set it to True
+ #CORS_ORIGIN_ALLOW_ALL = False
+ ```
+
+### Docevot configuration changes
+
+You must enable oauth2 authentication in Dovecot to make the filters editor work in the new UI.
+Follow this [guide](https://doc.dovecot.org/2.4.2/core/config/auth/databases/oauth2.html).
+
+### Post update
+
+Run this command to be sure to have everything set.
+
+``` bash
+$ python manage.py modo repair
+```
+
+You need to set the `buffer-size` to `8192` in uwsgi config.
+
+Edit the value in `/etc/uwsgi/apps-available/automx_instance.ini`
+to match at least 8192 or add it if it was not set:
+
+``` ini
+buffer-size = 8192
+```
+
+### SORBS DNS
+
+::: tip INFORMATION
+The installer will take care of that if you use it to upgrade.
+:::
+
+::: warning
+[SORBS DNS has been deprecated](https://www.mail-archive.com/mailop@mailop.org/msg22064.html).
+
+If you were using them with postfix, you need to update your configuration by removing any reference to it.
+
+For example:
+
+``` txt
+# Use some DNSBL
+postscreen_dnsbl_sites =
+ zen.spamhaus.org=127.0.0.[2..11]*3
+ bl.spameatingmonkey.net=127.0.0.2*2
+ bl.spamcop.net=127.0.0.2
+```
+:::
+
+## Version 2.2.3
+
+* Reduced the number of call to doveadm
+
+You need to setup a new worker :
+
+1. Create a new supervisord config (`/etc/supervisor/conf.d/modoboa-base-worker.conf` by default) :
+
+``` ini
+[program:modoboa-base-worker]
+autostart=true
+autorestart=true
+command={%python env path%} {% manage.py instance path%} rqworker modoboa
+directory={%modoboa home dir%}
+user=%{modoboa user}
+numprocs=1
+stopsignal=TERM
+```
+
+* `python env path` : Python executable located in your virtual environment created for modoboa.
+You will find it here `/srv/modoboa/venv/bin/python` by default.
+* `manage.py instance path`: Path to manage.py of your modoboa instance.
+ You will find it here : `/srv/modoboa/instance/manage.py`by default.
+* `Modoboa home dir`: Home dir of the user running modooba.
+ You will find it here `/srv/modoboa/` by default.
+* `modoboa user` : User managing dkim signing (modoboa by default).
+
+You can help you with `/etc/supervisor/conf.d/policyd.conf` (by default).
+
+Then restart supervisor.
+
+```shell
+$ supervisorctl reread && supervisorctl update` on Debian.
+```
+
+2. Update the RQ_QUEUES section bellow the `#REDIS` section:
+
+``` python
+# RQ
+RQ_QUEUES = {
+ ...
+ },
+ 'modoboa': {
+ 'HOST': REDIS_HOST,
+ 'PORT': REDIS_PORT,
+ 'DB': 0,
+ },
+}
+```
+3. Add the CACHE section bellow the `# RQ` one:
+
+``` python
+# CACHE
+CACHES = {
+ "default": {
+ "BACKEND": 'django.core.cache.backends.redis.RedisCache',
+ "LOCATION": REDIS_URL
+ }
+}
+```
+
+## Version 2.2.0
+
+::: tip INFORMATION
+If you use the installer to perform the upgrade, you NEED to update it.
+See above for instructions. After updating the installer and modoboa
+with it, you only need to edit `urls.py` and `settings.py`
+:::
+
+* Django has been updated to 4.2LTS. Please upgrade all your extensions alongside modoboa
+* Support for Python 3.7 has been dropped, minimum Python version is now 3.8
+* Support for Postgres 11 has been dropped, minimum Postgres version is now 12
+
+You need to change the first line in `urls.py` and replace
+[url] with [path] (in `/srv/modoboa/instance/instance/urls.py` by default):
+
+``` python
+# from django.conf.urls import include, url
+from django.urls import include, path
+
+urlpatterns = [
+ path(r'', include('modoboa.urls')),
+]
+```
+
+::: tip
+If you use Postgresql, you need to install pyscopg3+:
+
+``` bash
+pip install psycopg[binary]>=3.1
+```
+:::
+
+RQ has been added. This should replace the use of cron jobs in the
+future.
+
+For now, only `manage_dkim_keys` has been migrated.
+
+This will make the dkim key generation asynchronous
+but the task will be started as soon as the generation is required.
+
+Follow these instructions to perform the update in case you used
+supervisord for the installation (this apply if you used `modoboa-installer`):
+
+1. Edit settings.py (`/srv/modoboa/instance/instance/settings.py` by default) and add:
+
+``` python
+INSTALLED_APPS = (
+ ...,
+ 'django_rq',
+)
+```
+
+and add the RQ section bellow the `#REDIS` section:
+
+``` python
+# RQ
+RQ_QUEUES = {
+ 'dkim': {
+ 'HOST': REDIS_HOST,
+ 'PORT': REDIS_PORT,
+ 'DB': 0,
+ },
+}
+```
+
+Then by default you will need to restart `uwsgi` service.
+
+```shell
+$ systemctl restart uwsgi
+```
+
+2. Create a new supervisord config (`/etc/supervisor/conf.d/modoboa-worker.conf` by default) :
+
+``` ini
+[program:modoboa-worker]
+autostart=true
+autorestart=true
+command={%python env path%} {% manage.py instance path%} rqworker dkim
+directory={%modoboa home dir%}
+user=%{dkim manager user}
+numprocs=1
+stopsignal=TERM
+```
+
+* `python env path` : Python executable located in your virtual environment created for modoboa.
+ You will find it here `/srv/modoboa/venv/bin/python` by default.
+* `manage.py instance path`: Path to manage.py of your modoboa instance.
+ You will find it here : `/srv/modoboa/instance/manage.py`by default.
+* `Modoboa home dir`: Home dir of the user running modooba.
+ You will find it here `/srv/modoboa/` by default.
+* `dkim manager user` : User managing dkim signing (opendkim by default).
+
+You can help you with `/etc/supervisor/conf.d/policyd.conf` (by default).
+
+Then restart supervisor.
+
+```shell
+$ supervisorctl reread && supervisorctl update
+```
+
+Admins now have the option to setup a send-only mailbox.
+
+Send-only mailboxes do not have access to IMAP.
+
+You need to change your dovecot configuration to enable it.
+
+::: warning
+modoboa-webmail may not work with send-only user for now.
+:::
+
+If you use `Postgres`: Change `user_query` and `password_query` in `/etc/dovecot/dovecot-sql.conf.ext`:
+
+```sql
+user_query = SELECT '%{home_dir}/%%d/%%n' AS home, %mailboxes_owner_uid as uid, %mailboxes_owner_gid as gid, '*:bytes=' || mb.quota || 'M' AS quota_rule FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN core_user u ON u.id=mb.user_id WHERE (mb.is_send_only IS NOT TRUE OR '%s' NOT IN ('imap', 'pop3', 'lmtp')) AND mb.address='%%n' AND dom.name='%%d'
+
+password_query = SELECT email AS user, password, '%{home_dir}/%%d/%%n' AS userdb_home, %mailboxes_owner_uid AS userdb_uid, %mailboxes_owner_gid AS userdb_gid, CONCAT('*:bytes=', mb.quota, 'M') AS userdb_quota_rule FROM core_user u INNER JOIN admin_mailbox mb ON u.id=mb.user_id INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE (mb.is_send_only IS NOT TRUE OR '%s' NOT IN ('imap', 'pop3')) AND email='%%u' AND is_active AND dom.enabled
+```
+
+You basically simply need:
+
+* add `(mb.is_send_only IS NOT TRUE OR '%s' NOT IN ('imap', 'pop3', 'lmtp')) AND` for `user_query` after `WHERE`
+* add`(mb.is_send_only IS NOT TRUE OR '%s' NOT IN ('imap', 'pop3')) AND` for `password_query` after `WHERE`.
+
+If you use `MySQL`: Change `user_query` and `password_query` in `/etc/dovecot/dovecot-sql-master.ext`:
+
+```sql
+ user_query = SELECT '%{home_dir}/%%d/%%n' AS home, %mailboxes_owner_uid as uid, %mailboxes_owner_gid as gid, CONCAT('*:bytes=', mb.quota, 'M') AS quota_rule FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN core_user u ON u.id=mb.user_id WHERE (mb.is_send_only=0 OR '%s' NOT IN ('imap', 'pop3', 'lmtp')) AND mb.address='%%n' AND dom.name='%%d'
+
+ password_query = SELECT email AS user, password, '%{home_dir}/%%d/%%n' AS userdb_home, %mailboxes_owner_uid AS userdb_uid, %mailboxes_owner_gid AS userdb_gid, CONCAT('*:bytes=', mb.quota, 'M') AS userdb_quota_rule FROM core_user u INNER JOIN admin_mailbox mb ON u.id=mb.user_id INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE (mb.is_send_only=0 OR '%s' NOT IN ('imap', 'pop3')) AND u.email='%%u' AND u.is_active=1 AND dom.enabled=1
+```
+
+You basically simply need
+* add `(mb.is_send_only=0 OR '%s' NOT IN ('imap', 'pop3', 'lmtp')) AND` for `user_query` after `WHERE`
+* `(mb.is_send_only=0 OR '%s' NOT IN ('imap', 'pop3'))` for `password_query` after `WHERE`.
+
+## Version 2.1.0
+
+::: warning
+For this particular version, it is really important that you apply new
+migrations **AFTER** the following instructions. If you don\'t, you\'ll
+get into trouble\...
+:::
+
+The `modoboa-dmarc`, `modoboa-pdfcredentials` and `modoboa-imapmigration` plugins have been merged into the core.
+
+Add `'django_rename_app,'` To `INSTALLED_APPS`:
+
+``` python
+INSTALLED_APPS = (
+...,
+'django_rename_app', # [!code ++]
+)
+```
+
+Add `'modoboa.dmarc'`, `'modoboa.pdfcredentials'` and `'modoboa.imap_migration'` to `MODOBOA_APPS`:
+
+``` python
+MODOBOA_APPS = (
+ 'modoboa',
+ 'modoboa.core',
+ 'modoboa.lib',
+ 'modoboa.admin',
+ 'modoboa.transport',
+ 'modoboa.relaydomains',
+ 'modoboa.limits',
+ 'modoboa.parameters',
+ 'modoboa.dnstools',
+ 'modoboa.policyd',
+ 'modoboa.maillog',
+ 'modoboa_dmarc', # [!code --]
+ 'modoboa_pdfcredentials', # [!code --]
+ 'modoboa_imap_migration', # [!code --]
+ 'modoboa.dmarc', # [!code ++]
+ 'modoboa.pdfcredentials', # [!code ++]
+ 'modoboa.imap_migration', # [!code ++]
+)
+```
+
+And remove any reference to `'modoboa_dmarc'`, `'modoboa_pdfcredentials'` or `'modoboa_imap_migration'` in this samevariable.
+
+You need to add `'modoboa.imap_migration.auth_backends.IMAPBackend',` at the end of `AUTHENTICATION_BACKENDS`:
+
+``` python
+AUTHENTICATION_BACKENDS = (
+ 'django.contrib.auth.backends.ModelBackend',
+ 'modoboa.imap_migration.auth_backends.IMAPBackend', # [!code ++]
+)
+```
+
+::: info
+It won't affect login if you don't use `imap_migration` feature.
+
+It is therefor highly recommended to add this authentication backend.
+:::
+
+After upgrading modoboa, run the following commands from your virtual
+environment:
+
+``` shell
+$ python manage.py rename_app modoboa_dmarc dmarc
+$ python manage.py rename_app modoboa_imap_migration imap_migration
+```
+
+## Version 2.0.4
+
+The following modifications must be applied to the `settings.py` file:
+
+* Add `'modoboa.core.context_processors.new_admin_url',` at the end of [context_processors] list:
+
+ ``` python
+ TEMPLATES = [
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [],
+ 'APP_DIRS': True,
+ 'OPTIONS': {
+ 'context_processors': [
+ 'django.template.context_processors.debug',
+ 'django.template.context_processors.request',
+ 'django.contrib.auth.context_processors.auth',
+ 'django.template.context_processors.i18n',
+ 'django.template.context_processors.media',
+ 'django.template.context_processors.static',
+ 'django.template.context_processors.tz',
+ 'django.contrib.messages.context_processors.messages',
+ 'modoboa.core.context_processors.top_notifications',
+ 'modoboa.core.context_processors.new_admin_url', # [!code ++]
+ ],
+ 'debug': False,
+ },
+ },
+ ]
+ ```
+
+* Add the following variable:
+
+``` python
+PID_FILE_STORAGE_PATH = '/var/run'
+```
+
+* Update `LOGGING` variable to define an address for `syslog-auth` handler:
+
+ ``` python{5}
+ 'syslog-auth': {
+ 'class': 'logging.handlers.SysLogHandler',
+ 'facility': SysLogHandler.LOG_AUTH,
+ 'formatter': 'syslog',
+ 'address': '/dev/log'
+ },
+ ```
+
+* You now have the possibility to customize the url of the new-admin interface:
+ To do so please head up to `the custom configuration chapter ` (advanced user).
+
+* Add `DEFAULT_THROTTLE_RATES` to `REST_FRAMEWORK`:
+
+``` python{2-10}
+REST_FRAMEWORK = {
+ 'DEFAULT_THROTTLE_RATES': {
+ 'user': '300/minute',
+ 'ddos': '5/second',
+ 'ddos_lesser': '200/minute',
+ 'login': '10/minute',
+ 'password_recovery_request': '12/hour',
+ 'password_recovery_totp_check': '25/hour',
+ 'password_recovery_apply': '25/hour'
+ },
+ 'DEFAULT_AUTHENTICATION_CLASSES': (
+ 'modoboa.core.drf_authentication.JWTAuthenticationWith2FA',
+ 'rest_framework.authentication.TokenAuthentication',
+ 'rest_framework.authentication.SessionAuthentication',
+ ),
+ 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
+ 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning',
+}
+```
+
+* You can edit the `DEFAULT_THROTTLE_RATES` to whatever value suits you:
+
+ * `user` is for every endpoint, it is per user or per ip if not logged.
+ * `ddos` is per api endpoint and per user or per ip if not logged.
+ * `ddos_lesser` is for per api endpoint and per user or per ip if not logged.
+ This is for api endpoint that arelighter.
+ * `login` the number of time an ip can attempt to log.
+ The counter will reset on login success.
+ * `password_*` is for the recovery, it is divided per step in the recovery process.
+
+## Version 2.0.3
+
+Update the new following setting in the `settings.py` file:
+
+``` python
+DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
+```
+
+## Version 2.0.0
+
+Add `drf_spectacular` and `phonenumber_field` to `INSTALLED_APPS` in the `settings.py` file, as follows:
+
+``` python
+INSTALLED_APPS = (
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.sites',
+ 'django.contrib.staticfiles',
+ 'reversion',
+ 'ckeditor',
+ 'ckeditor_uploader',
+ 'rest_framework',
+ 'rest_framework.authtoken',
+ 'drf_spectacular', # [!code ++]
+ 'phonenumber_field', # [!code ++]
+ 'django_otp',
+ 'django_otp.plugins.otp_totp',
+ 'django_otp.plugins.otp_static',
+)
+```
+
+Modify the `REST_FRAMEWORK` setting as follows:
+
+``` python
+REST_FRAMEWORK = {
+ 'DEFAULT_AUTHENTICATION_CLASSES': (
+ 'modoboa.core.drf_authentication.JWTAuthenticationWith2FA',
+ 'rest_framework.authentication.TokenAuthentication',
+ 'rest_framework.authentication.SessionAuthentication',
+ ),
+ 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
+ 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning',
+}
+```
+
+Add the new following settings:
+
+``` python
+SPECTACULAR_SETTINGS = {
+ 'SCHEMA_PATH_PREFIX': r'/api/v[0-9]',
+ 'TITLE': 'Modoboa API',
+ 'VERSION': None,
+ 'SERVE_PERMISSIONS': ['rest_framework.permissions.IsAuthenticated'],
+}
+
+PHONENUMBER_DB_FORMAT = 'INTERNATIONAL'
+
+DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
+```
+
+#### Migration issue for Postgres/OpenDKIM users
+
+The [migration will probably fail](https://github.com/modoboa/modoboa/issues/2508)
+because of the additional view created for OpenDKIM.
+
+To make it work, you first need to drop the view:
+
+``` shell
+$ sudo su - postgres
+$ psql
+psql (12.10 (Ubuntu 12.10-0ubuntu0.20.04.1))
+Type "help" for help.
+
+postgres=# \c modoboa
+You are now connected to database "modoboa" as user "postgres".
+modoboa-# \d+ dkim
+ View "public.dkim"
+ Column | Type | Collation | Nullable | Default | Storage | Description
+------------------+------------------------+-----------+----------+---------+----------+-------------
+ id | integer | | | | plain |
+ domain_name | character varying(100) | | | | extended |
+ private_key_path | character varying(254) | | | | extended |
+ selector | character varying(30) | | | | extended |
+
+View definition:
+ SELECT admin_domain.id,
+ admin_domain.name AS domain_name,
+ admin_domain.dkim_private_key_path AS private_key_path,
+ admin_domain.dkim_key_selector AS selector
+ FROM admin_domain
+ WHERE admin_domain.enable_dkim;
+
+modoboa=# drop view dkim;
+DROP VIEW
+```
+
+Then, run the migration as usual:
+
+``` bash
+python manage.py migrate
+```
+
+Finally, recreate the view:
+
+``` bash
+modoboa=# CREATE OR REPLACE VIEW dkim AS
+modoboa-# SELECT admin_domain.id,
+modoboa-# admin_domain.name AS domain_name,
+modoboa-# admin_domain.dkim_private_key_path AS private_key_path,
+modoboa-# admin_domain.dkim_key_selector AS selector
+modoboa-# FROM admin_domain
+modoboa-# WHERE admin_domain.enable_dkim;
+CREATE VIEW
+modoboa=# grant select on dkim to opendkim;
+GRANT
+```
+
+#### New admin interface
+
+::: tip INFORMATION
+This new release brings a new admin interface written with Vue.js framework.
+:::
+
+It is a work in progress and all features are not yet
+implemented - i.e. extensions integration - but you could give it a try.
+It uses a new API version but the old one is still available.
+
+You will need to copy the frontend files in the folder you specified in
+your web server configuration.
+
+If you used the installer, the folder should be `/srv/modoboa/instance/frontend`:
+
+``` bash
+mkdir /srv/modoboa/instance/frontend
+cp -r /srv/modoboa/env/lib/pythonX.X/site-packages/modoboa/frontend_dist/* /srv/modoboa/instance/frontend
+```
+
+Then, edit the `/srv/modoboa/instance/frontend/config.json` and update the `API_BASE_URL`
+setting according to the hostname of your server:
+
+``` json
+{
+ "API_BASE_URL": "https:///api/v2"
+}
+```
+
+Finally, update the configuration of your web server to serve the frontend files.
+
+For NGINX, you should add the following in the `server` block:
+
+``` nginx
+location ^~ /new-admin {
+ alias /srv/modoboa/instance/frontend/;
+ index index.html;
+
+ expires -1;
+ add_header Pragma "no-cache";
+ add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
+
+ try_files $uri $uri/ /index.html = 404;
+}
+```
+
+## Version 1.17.0
+
+Modoboa now supports Two-Factor authentication using TOTP applications.
+
+To enable it, install the following new requirement in your virtualenv:
+
+``` bash
+(env)> pip install django-otp qrcode
+```
+
+Then, open the `settings.py` file and add `django_otp` packages to `INSTALLED_APPS`:
+
+``` python
+INSTALLED_APPS = (
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.sites',
+ 'django.contrib.staticfiles',
+ 'reversion',
+ 'ckeditor',
+ 'ckeditor_uploader',
+ 'rest_framework',
+ 'rest_framework.authtoken',
+ 'django_otp', # [!code ++]
+ 'django_otp.plugins.otp_totp', # [!code ++]
+ 'django_otp.plugins.otp_static', # [!code ++]
+)
+```
+
+Add new middlewares to `MIDDLEWARE`. It has to be after the SessionMiddleware and before the CommonMiddleware:
+
+``` python
+MIDDLEWARE = (
+ 'x_forwarded_for.middleware.XForwardedForMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.locale.LocaleMiddleware', # [!code ++]
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django_otp.middleware.OTPMiddleware',
+ 'modoboa.core.middleware.TwoFAMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+ 'modoboa.core.middleware.LocalConfigMiddleware',
+ 'modoboa.lib.middleware.AjaxLoginRedirect',
+ 'modoboa.lib.middleware.CommonExceptionCatcher',
+ 'modoboa.lib.middleware.RequestCatcherMiddleware',
+)
+```
+
+And add the following new setting:
+
+``` python
+# 2FA
+OTP_TOTP_ISSUER = ""
+```
+
+## Version 1.16.0
+
+A new `policy daemon ` has been added.
+
+Make sure to have a Redis instance running on your server.
+
+Add `'modoboa.policyd'` to `MODOBOA_APPS`:
+
+``` python
+MODOBOA_APPS = (
+ 'modoboa',
+ 'modoboa.core',
+ 'modoboa.lib',
+ 'modoboa.admin',
+ 'modoboa.transport',
+ 'modoboa.relaydomains',
+ 'modoboa.limits',
+ 'modoboa.parameters',
+ 'modoboa.dnstools',
+ 'modoboa.policyd' # [!code ++]
+)
+```
+
+Add the following settings to your `settings.py` file:
+
+::: code-group
+``` python{1-3} [TCP/IP]
+REDIS_HOST = ''
+REDIS_PORT = 6379
+REDIS_QUOTA_DB = 0
+REDIS_URL = 'redis://{}:{}/{}'.format(REDIS_HOST, REDIS_PORT, REDIS_QUOTA_DB)
+```
+
+``` python [UNIX Socket]
+REDIS_HOST = '/path/to/redis/socket'
+REDIS_PORT = 6379
+REDIS_QUOTA_DB = 0
+REDIS_URL = 'unix://{}?db={}'.format(REDIS_HOST, REDIS_QUOTA_DB)
+```
+:::
+
+Once done, you can start the policy daemon using the following commands:
+
+``` shell
+$ python manage.py policy_daemon
+```
+
+Don't forget to configure `_policyd_config ` if you want to use this feature.
+
+The `modoboa-stats` plugin has been merged into the core.
+
+Add `'modoboa.maillog'` to `MODOBOA_APPS`:
+
+``` python
+MODOBOA_APPS = (
+ 'modoboa',
+ 'modoboa.core',
+ 'modoboa.lib',
+ 'modoboa.admin',
+ 'modoboa.transport',
+ 'modoboa.relaydomains',
+ 'modoboa.limits',
+ 'modoboa.parameters',
+ 'modoboa.dnstools',
+ 'modoboa.policyd',
+ 'modoboa_stats', # [!code --]
+ 'modoboa.maillog', # [!code ++]
+)
+```
+
+And remove any reference to `modoboa_stats` in this same variable.
+
+## Version 1.15.0
+
+::: tip IMPORTANT
+This version drops Python 2 support so don't forget to update all the extensions you use.
+:::
+
+::: warning
+If you upgrade an existing python 2 installation, you will need to
+create a new Python 3 virtual environment.
+
+You can remove the existing virtual environment and replace it by the new one
+so you won't have to modify your configuration files.
+:::
+
+Add the following new setting:
+
+``` python
+DISABLE_DASHBOARD_EXTERNAL_QUERIES = False
+```
+
+Reload uwsgi/gunicorn/apache depending on your setup.
+
+Finally, Make sure to use root privileges and run the following command:
+
+``` shell
+$ python manage.py generate_postfix_maps --destdir
+```
+
+Then, reload postfix.
+
+## Version 1.14.0
+
+This release introduces an optional LDAP synchronization process.
+
+If you want to use it, please follow the `dedicated procedure` #ldap-sync .
+
+## Version 1.13.1
+
+Upgrade postfix maps files as follows:
+
+``` shell
+$ python manage.py generate_postfix_maps --destdir --force-overwrite
+```
+
+## Version 1.13.0
+
+Add `'modoboa.dnstools'` to `MODOBOA_APPS`:
+
+``` python
+MODOBOA_APPS = (
+ 'modoboa',
+ 'modoboa.core',
+ 'modoboa.lib',
+ 'modoboa.admin',
+ 'modoboa.transport',
+ 'modoboa.relaydomains',
+ 'modoboa.limits',
+ 'modoboa.parameters',
+ 'modoboa.dnstools', # [!code ++]
+)
+```
+
+Add the following new settings:
+
+``` python
+CSRF_COOKIE_SECURE = True
+SESSION_COOKIE_SECURE = True
+```
+
+### modoboa-postfix-autoreply 1.5.0
+
+Edit the `/etc/postfix/main.cf` file and remove the `sql-autoreplies-transport.cf` map from the `transport_maps` if present.
+
+Remove the corresponding `proxy_read_maps` entry if relevant.
+
+Reload postfix.
+
+## Version 1.10.0
+
+::: warning
+Upgrade installed extensions BEFORE running `check` or `migrate` commands.
+:::
+
+Upgrade all your installed plugins to the following versions:
+
+::: warning
+If you use the amavis plugin, make sure to include its configuration as follows into `settings.py` :
+
+``` python
+from modoboa_amavis import settings as modoboa_amavis_settings
+modoboa_amavis_settings.apply(globals())
+```
+:::
+
+| Name | Version |
+| :--- | :-----: |
+| modoboa-amavis | 1.2.0 |
+| modoboa-contacts | 0.5.0 |
+| modoboa-dmarc | 1.1.0 |
+| modoboa-imap-migration | 1.2.0 |
+| modoboa-pdfcredentials | 1.3.0 |
+| modoboa-postfix-autoreply | 1.4.0 |
+| modoboa-radicale | 1.2.0 |
+| modoboa-sievefilters | 1.4.0 |
+| modoboa-stats | 1.4.0 |
+| modoboa-webmail | 1.4.0 |
+
+Edit the `settings.py` file and apply the following modifications.
+
+Add `'modoboa.transport'` to `MODOBOA_APPS`:
+
+``` python
+MODOBOA_APPS = (
+ 'modoboa',
+ 'modoboa.core',
+ 'modoboa.lib',
+ 'modoboa.admin',
+ 'modoboa.transport', # [!code ++]
+ 'modoboa.relaydomains',
+ 'modoboa.limits',
+ 'modoboa.parameters',
+)
+```
+
+Replace the following line:
+
+``` python
+MIDDLEWARE_CLASSES = ( # [!code --]
+MIDDLEWARE = ( # [!code ++]
+```
+
+Update postfix map files as follows:
+
+``` shell
+$ rm -f /modoboa-postfix-maps.chk
+$ python manage.py generate_postfix_maps --force --destdir
+```
+
+Then, modify postfix's configuration as follows:
+
+```txt
+smtpd_sender_login_maps =
+ :/sql-sender-login-map.cf
+
+transport_maps =
+ :/sql-transport.cf
+ :/sql-spliteddomains-transport.cf
+ # other map files...
+````
+
+Replace `` and `` by your values.
+
+If `transport_maps` contains `sql-relaydomains-transport.cf`, remove it.
+
+::: warning
+If you make use of postfix's [proxymap server](http://www.postfix.org/proxymap.8.html),
+you must also update the `proxy_read_maps` setting.
+:::
+
+Reload postfix.
+
+Add the following cron job in order to generate DKIM keys:
+
+```txt
+# Generate DKIM keys (they will belong to the user running this job)
+* * * * * root $PYTHON $INSTANCE/manage.py modo manage_dkim_keys
+```
+
+## Version 1.9.0
+
+If you want to manage inactive accounts, look at `inactive_accounts` .
+
+## Version 1.8.3
+
+Edit the `settings.py` file and replace the following line:
+
+``` python
+BASE_DIR = os.path.dirname(os.path.dirname(__file__)) # [!code --]
+BASE_DIR = os.path.realpath(os.path.dirname(os.path.dirname(__file__))) # [!code ++]
+```
+
+## Version 1.8.0
+
+Modoboa now relies on [Django's builtin password validation system](https://docs.djangoproject.com/en/1.10/topics/auth/passwords/#module-django.contrib.auth.password_validation)
+to validate user passwords, instead of `django-passwords`.
+
+Remove `django-passwords` from your system:
+
+``` shell
+$ sudo -u -i bash
+$ source /bin/activate
+$ pip uninstall django-passwords
+```
+
+Edit the `settings.py` file and remove the following content:
+
+``` python
+# django-passwords
+
+PASSWORD_MIN_LENGTH = 8 # [!code --]
+
+PASSWORD_COMPLEXITY = { # [!code --]
+ "UPPER": 1, # [!code --]
+ "LOWER": 1, # [!code --]
+ "DIGITS": 1 # [!code --]
+} # [!code --]
+```
+
+Add the following lines:
+
+``` python
+# Password validation rules
+AUTH_PASSWORD_VALIDATORS = [ # [!code ++]
+ { # [!code ++]
+ 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', # [!code ++]
+ }, # [!code ++]
+ { # [!code ++]
+ 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', # [!code ++]
+ }, # [!code ++]
+ { # [!code ++]
+ 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', # [!code ++]
+ }, # [!code ++]
+ { # [!code ++]
+ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', # [!code ++]
+ }, # [!code ++]
+ { # [!code ++]
+ 'NAME': 'modoboa.core.password_validation.ComplexityValidator', # [!code ++]
+ 'OPTIONS': { # [!code ++]
+ 'upper': 1, # [!code ++]
+ 'lower': 1, # [!code ++]
+ 'digits': 1, # [!code ++]
+ 'specials': 0 # [!code ++]
+ } # [!code ++]
+ }, # [!code ++]
+] # [!code ++]
+```
+
+## Version 1.7.2
+
+API documentation has evolved (because of the upgrade to Django Rest Framework 3.6)
+and CKeditor is now embedded by default (thanks to the `django-ckeditor` package).
+
+Some configuration changes are required.
+
+Edit your `settings.py` file and apply the following modifications:
+
+* Update the `INSTALLED_APPS` variable as follows:
+
+``` python
+INSTALLED_APPS = (
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.sites',
+ 'django.contrib.staticfiles',
+ 'reversion',
+ 'ckeditor', # [!code ++]
+ 'ckeditor_uploader', # [!code ++]
+ 'rest_framework', # [!code ++]
+ 'rest_framework.authtoken', # [!code ++]
+)
+```
+
+* Update the `REST_FRAMEWORK` variable as follows:
+
+``` python
+REST_FRAMEWORK = {
+ 'DEFAULT_AUTHENTICATION_CLASSES': (
+ 'rest_framework.authentication.TokenAuthentication',
+ 'rest_framework.authentication.SessionAuthentication',
+ ),
+}
+```
+
+* Remove the `SWAGGER_SETTINGS` variable
+* Add the following content
+
+``` python
+# CKeditor
+
+CKEDITOR_UPLOAD_PATH = "uploads/"
+
+CKEDITOR_IMAGE_BACKEND = "pillow"
+
+CKEDITOR_RESTRICT_BY_USER = True
+
+CKEDITOR_BROWSE_SHOW_DIRS = True
+
+CKEDITOR_ALLOW_NONIMAGE_FILES = False
+
+CKEDITOR_CONFIGS = {
+ 'default': {
+ 'allowedContent': True,
+ 'toolbar': 'Modoboa',
+ 'width': None,
+ 'toolbar_Modoboa': [
+ ['Bold', 'Italic', 'Underline'],
+ ['JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'],
+ ['BidiLtr', 'BidiRtl', 'Language'],
+ ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent'],
+ ['Undo', 'Redo'],
+ ['Link', 'Unlink', 'Anchor', '-', 'Smiley'],
+ ['TextColor', 'BGColor', '-', 'Source'],
+ ['Font', 'FontSize'],
+ ['Image', ],
+ ['SpellChecker']
+ ],
+ },
+}
+```
+
+Don't forget to run the following command:
+
+``` bash
+$ python manage.py collectstatic
+```
+
+## Version 1.7.1
+
+If you used 1.7.0 for a fresh installation, please run the following commands:
+
+``` shell
+$ sudo -u -i bash
+$ source /bin/activate
+$ cd
+$ python manage.py load_initial_data
+```
+
+## Version 1.7.0
+
+This version requires Django \>= 1.10 so you need to make some modifications.
+
+It also brings internal API changes which are not
+backward compatible so installed extensions must be upgraded too.
+
+First of all, deactivate all installed extensions (edit the `settings.py` file
+and comment the corresponding lines in `MODOBOA_APPS`).
+
+Edit the `urls.py` file of your local
+instance and replace its content by the following one:
+
+``` python
+from django.conf.urls import include, url
+
+urlpatterns = [
+ url(r'', include('modoboa.urls')),
+]
+```
+
+Edit the `settings.py` and apply the
+following changes:
+
+* Add `'modoboa.parameters'` to `MODOBOA_APPS`:
+
+``` python
+MODOBOA_APPS = (
+ 'modoboa',
+ 'modoboa.core',
+ 'modoboa.lib',
+ 'modoboa.admin',
+ 'modoboa.relaydomains',
+ 'modoboa.limits',
+ 'modoboa.parameters',
+ # Modoboa extensions here.
+)
+```
+
+* Add `'modoboa.core.middleware.LocalConfigMiddleware'` to
+ `MIDDLEWARE_CLASSES`:
+
+``` python
+MIDDLEWARE_CLASSES = (
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.locale.LocaleMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+ 'modoboa.core.middleware.LocalConfigMiddleware',
+ 'modoboa.lib.middleware.AjaxLoginRedirect',
+ 'modoboa.lib.middleware.CommonExceptionCatcher',
+ 'modoboa.lib.middleware.RequestCatcherMiddleware',
+)
+```
+
+* Modoboa used to provide a custom authentication backend
+ (`modoboa.lib.authbackends.SimpleBackend`) but it has been removed.
+ Replace it as follows:
+
+``` python
+AUTHENTICATION_BACKENDS = (
+ # Other backends before...
+ 'django.contrib.auth.backends.ModelBackend',
+)
+```
+
+* Remove `TEMPLATE_CONTEXT_PROCESSORS` and replace it by:
+
+``` python
+TEMPLATES = [
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [],
+ 'APP_DIRS': True,
+ 'OPTIONS': {
+ 'context_processors': [
+ 'django.template.context_processors.debug',
+ 'django.template.context_processors.request',
+ 'django.contrib.auth.context_processors.auth',
+ 'django.template.context_processors.i18n',
+ 'django.template.context_processors.media',
+ 'django.template.context_processors.static',
+ 'django.template.context_processors.tz',
+ 'django.contrib.messages.context_processors.messages',
+ 'modoboa.core.context_processors.top_notifications',
+ ],
+ 'debug': False,
+ },
+ },
+]
+```
+
+Run the following commands (load virtualenv if you use one):
+
+``` bash
+$ sudo -u -i bash
+$ source /bin/activate
+$ cd
+$ python manage.py migrate
+$ python manage.py collectstatic
+```
+
+Finally, upgrade your extensions and reactivate them.
+
+-------------------------------------------------------
+Name Version
+--------------------------- ---------------------------
+modoboa-amavis 1.1.0
+
+modoboa-dmarc 1.0.0
+
+modoboa-imap-migration 1.1.0
+
+modoboa-pdfcredentials 1.1.0
+
+modoboa-postfix-autoreply 1.2.0
+
+modoboa-radicale 1.1.0
+
+modoboa-sievefilters 1.1.0
+
+modoboa-stats 1.1.0
+
+modoboa-webmail 1.1.0
+
+Command line shortcuts:
+
+``` bash
+$ pip install modoboa-amavis==1.1.0
+$ pip install modoboa-dmarc==1.0.0
+$ pip install modoboa-imap-migration==1.1.0
+$ pip install modoboa-pdfcredentials==1.1.0
+$ pip install modoboa-postfix-autoreply==1.2.0
+$ pip install modoboa-radicale==1.1.0
+$ pip install modoboa-sievefilters==1.1.0
+$ pip install modoboa-stats==1.1.0
+$ pip install modoboa-webmail==1.1.0
+```
+
+And please make sure you use the latest version of the `django-versionfield2` package:
+
+``` bash
+$ pip install -U django-versionfield2
+```
+
+### Notes about quota changes and resellers
+
+Reseller users now have a quota option in Resources tab. This is the
+quota that a reseller can share between all its domains.
+
+There are two quotas for a domain in the new version:
+
+1. Quota &
+2. Default mailbox quota.
+
+\[1\]. Quota: quota shared between mailboxes This quota is shared
+between all the mailboxes of this domain. This value cannot exceed
+reseller\'s quota and hence cannot be 0(unlimited) if reseller has
+finite quota.
+
+\[2\]. Default mailbox quota: default quota applied to mailboxes This
+quota is the default quota applied to new mailboxes. This value cannot
+exceed Quota\[1\] and hence cannot be 0(unlimited) if Quota\[1\] is
+finite.
+
+## Version 1.6.1
+
+First of all, update postfix map files as follows:
+
+``` bash
+$ python manage.py generate_postfix_maps --destdir --force-overwrite
+```
+
+Then, modify postfix's configuration as follows:
+
+```txt
+smtpd_sender_login_maps =
+ :/sql-sender-login-mailboxes.cf
+ :/sql-sender-login-aliases.cf
+ :/sql-sender-login-mailboxes-extra.cf
+```
+
+Replace `` and `` by your values.
+
+Finally, reload postfix.
+
+This release also deprecates some internal functions. As a result,
+several extensions has been updated to maintain the compatibility. If
+you enabled the notification service, you\'ll find the list of available
+updates directly in your Modoboa console.
+
+For the others, here is the list:
+
+-------------------------------------------------------
+Name Version
+--------------------------- ---------------------------
+modoboa-amavis 1.0.10
+modoboa-postfix-autoreply 1.1.7
+modoboa-radicale 1.0.5
+modoboa-stats 1.0.9
+
+Command line shortcut:
+
+``` bash
+$ pip install modoboa-amavis==1.0.10
+$ pip install modoboa-postfix-autoreply==1.1.7
+$ pip install modoboa-radicale==1.0.5
+$ pip install modoboa-stats==1.0.9
+```
+
+## Version 1.6.0
+
+::: warning
+You have to upgrade extensions due to [core.User] model
+attribute change ([user.group] to [user.role] ).
+Otherwise, you will have an internal error after upgrade. In particular:
+
+* [modoboa-amavisd](https://github.com/modoboa/modoboa-amavis/commit/35df4e48b124e56df930cda8c013af0c1fcaabf3),
+* [modoboa-stats](https://github.com/modoboa/modoboa-stats/commit/aa4a39ce65eb306ad6dec30a54eb58945b120274),
+* [modoboa-postfix-autoreply](https://github.com/modoboa/modoboa-postfix-autoreply/commit/20f98c8d1c0c0dbd420f47aefcbb0290022414a4)
+ are concerned.
+:::
+
+An interesting feature brougth by this version is the capability to make
+different checks about MX records.
+
+For example, Modoboa can query main [DNSBL](https://en.wikipedia.org/wiki/DNSBL) providers for every defined domain.
+
+With this, you will quickly know if one the domains you manage is listed or not.
+
+To activate it, add the following line to your crontab:
+
+```txt
+*/30 * * * * python /manage.py modo check_mx
+````
+
+The communication with Modoboa public API has been reworked. Instead of
+sending direct synchronous queries (for example to check new versions),
+a cron job has been added. To activate it, add the following line to
+your crontab:
+
+```txt
+0 * * * * python /manage.py communicate_with_public_api
+```
+
+Please also note that public API now uses TLS so you must update your configuration as follows:
+
+```python
+MODOBOA_API_URL = 'https://api.modoboa.org/1/'
+```
+
+Finally, it is now possible to declare additional sender addresses on a per-account basis.
+
+You need to update your postfix configuration in order to use this functionality.
+Just edit the `main.cf` file and change the following parameter:
+
+```txt
+smtpd_sender_login_maps =
+ :/etc/postfix/sql-sender-login-mailboxes.cf
+ :/etc/postfix/sql-sender-login-aliases.cf
+ :/etc/postfix/sql-sender-login-mailboxes-extra.cf
+```
+
+## Version 1.5.0
+
+The API has been greatly improved and a documentation is now available.
+
+To enable it, add `'rest_framework_swagger'` to the `INSTALLED_APPS` variable in `settings.py` as follows:
+
+```python
+INSTALLED_APPS = (
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.sites',
+ 'django.contrib.staticfiles',
+ 'reversion',
+ 'rest_framework.authtoken',
+ 'rest_framework_swagger', # [!code ++]
+)
+```
+
+Then, add the following content into `settings.py` , just after the `REST_FRAMEWORK` variable:
+
+```python
+SWAGGER_SETTINGS = { # [!code ++]
+ "is_authenticated": False, # [!code ++]
+ "api_version": "1.0", # [!code ++]
+ "exclude_namespaces": [], # [!code ++]
+ "info": { # [!code ++]
+ "contact": "contact@modoboa.com", # [!code ++]
+ "description": ("Modoboa API, requires a valid token."), # [!code ++]
+ "title": "Modoboa API", # [!code ++]
+ } # [!code ++]
+} # [!code ++]
+```
+
+You're done. The documentation is now available at the following address:
+
+```txt
+https:// your instance address /docs/api/
+```
+
+Finally, if you find a `TEMPLATE_CONTEXT_PROCESSORS` variable in your `settings.py` file,
+make sure it looks like this:
+
+```python
+TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + [
+ 'modoboa.core.context_processors.top_notifications',
+]
+```
+
+## Version 1.4.0
+
+::: warning
+Please make sure to use Modoboa 1.3.5 with an up-to-date database before an upgrade to 1.4.0.
+:::
+
+::: warning
+Do not follow the regular upgrade procedure for this version.
+:::
+
+Some extension have been moved back into the main repository. The main
+reason for that is that using Modoboa without them doesn\'t make sense.
+
+First of all, you must rename the following applications listed inside
+the `MODOBOA_APPS` variable:
+
+| Old name | New name |
+| :-------------------------: | :--------------------: |
+| modoboa_admin | modoboa.admin |
+| modoboa_admin_limits | modoboa.limits |
+| modoboa_admin_relaydomains | modoboa.relaydomains |
+
+Then, apply the following steps:
+
+1. Uninstall old extensions:
+ ```shell
+ $ pip uninstall modoboa-admin modoboa-admin-limits modoboa-admin-relaydomains
+ ```
+2. Install all extension updates using pip (check the *Modoboa >Information* page)
+3. Manually migrate database:
+ ```shell
+ $ cd
+ $ python manage.py migrate auth
+ $ python manage.py migrate admin 0001 --fake
+ $ python manage.py migrate admin
+ $ python manage.py migrate limits 0001 --fake
+ $ python manage.py migrate relaydomains 0001 --fake
+ $ python manage.py migrate
+ ```
+4. Finally, update static files:
+ ```shell
+ $ python manage.py collectstatic
+ ```
+This version also introduces a REST API. To enable it:
+
+1. Add `'rest_framework.authtoken'` to the `INSTALLED_APPS` variable
+2. Add the following configuration inside `settings.py`:
+ ```python
+ # Rest framework settings
+
+ REST_FRAMEWORK = {
+ 'DEFAULT_AUTHENTICATION_CLASSES': (
+ 'rest_framework.authentication.TokenAuthentication',
+ ),
+ 'DEFAULT_PERMISSION_CLASSES': (
+ 'rest_framework.permissions.IsAuthenticated',
+ )
+ }
+ ```
+3. Run the following command:
+ ```shell
+ $ python manage.py migrate
+ ```
+
+## Version 1.3.5
+
+To enhance security, Modoboa now checks the [strength of user passwords](https://github.com/dstufft/django-passwords) .
+
+To use this feature, add the following configuration into the `settings.py` file:
+
+```python
+# django-passwords
+
+PASSWORD_MIN_LENGTH = 8
+
+PASSWORD_COMPLEXITY = {
+ "UPPER": 1,
+ "LOWER": 1,
+ "DIGITS": 1
+}
+```
+
+## Version 1.3.2
+
+Modoboa now uses the *atomic requests* mode to preserve database consistency
+([reference](https://docs.djangoproject.com/en/1.7/topics/db/transactions/#tying-transactions-to-http-requests)).
+
+To enable it, update the `DATABASES` variable in `settings.py` as follows:
+
+```python
+DATABASES = {
+ "default": {
+ # stuff before...
+ "ATOMIC_REQUESTS": True
+ },
+ "amavis": {
+ # stuff before...
+ "ATOMIC_REQUESTS": True
+ }
+}
+```
+
+## Version 1.3.0
+
+This release does not bring awesome new features but it is a necessary bridge to the future of Modoboa.
+
+All extensions now have their own git repository and the deploy process has been updated to reflect this change.
+
+Another important update is the use of Django 1.7.
+
+Besides its new features, the migration system has been reworked and is now more robust than before.
+
+Before we begin with the procedure, here is a table showing old extension names and their new name:
+
+Old name New package name New module name
+------------------------------------------ ---------------------------- ----------------------------
+modoboa.extensions.admin modoboa-admin modoboa_admin
+
+modoboa.extensions.limits modoboa-admin-limits modoboa_admin_limits
+
+modoboa.extensions.postfix_autoreply modoboa-postfix-autoreply modoboa_postfix_autoreply
+
+modoboa.extensions.postfix_relay_domains modoboa-admin-relaydomains modoboa_admin_relaydomains
+
+modoboa.extensions.radicale modoboa-radicale modoboa_radicale
+
+modoboa.extensions.sievefilters modoboa-sievefilters modoboa_sievefilters
+
+modoboa.extensions.stats modoboa-stats modoboa_stats
+
+modoboa.extensions.webmail modoboa-webmail modoboa_webmail
+
+Here are the required steps:
+
+1. Install the extensions using pip (look at the second column in the table above):
+ ```shell
+ $ pip install
+ ````
+2. Remove `south` from `INSTALLED_APPS`
+3. Rename old extension names inside `MODOBOA_APPS` (look at the third column in the table above)
+4. Remove `modoboa.lib.middleware.ExtControlMiddleware` from `MIDDLEWARE_CLASSES`
+5. Change `DATABASE_ROUTERS` to:
+ ```python
+ DATABASE_ROUTERS = ["modoboa_amavis.dbrouter.AmavisRouter"]
+ ```
+6. Run the following commands:
+ ```shell
+ $ cd
+ $ python manage.py migrate
+ ```
+7. Reply `yes` to the question
+8. Run the following commands:
+ ```shell
+ $ python manage.py load_initial_data
+ $ python manage.py collectstatic
+ ```
+9. The cleanup job has been renamed in Django, so you have to modify your crontab entry:
+ ```txt
+ - 0 0 * * * /manage.py cleanup
+ + 0 0 * * * /manage.py clearsessions
+ ```
+
+## Version 1.2.0
+
+A new notification service let administrators know about new Modoboa versions.
+
+To activate it, you need to update the `TEMPLATE_CONTEXT_PROCESSORS` variable like this:
+
+```python
+from django.conf import global_settings
+
+TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
+ 'modoboa.core.context_processors.top_notifications',
+)
+```
+
+and to define the new `MODOBOA_API_URL` variable:
+
+```python
+MODOBOA_API_URL = 'http://api.modoboa.org/1/'
+```
+
+The location of external static files has changed. To use them, add a new path to the `STATICFILES_DIRS`:
+
+```python
+ # Additional locations of static files
+ STATICFILES_DIRS = (
+ # Put strings here, like "/home/html/static" or "C:/www/django/static".
+ # Always use forward slashes, even on Windows.
+ # Don't forget to use absolute paths, not relative paths.
+ "/bower_components",
+ )
+```
+
+Run the following commands to define the hostname of your instance:
+
+```shell
+$ cd
+$ python manage.py set_default_site
+```
+
+If you plan to use the Radicale extension:
+
+1. Add `'modoboa.extensions.radicale'` to the `MODOBOA_APPS` variable
+2. Run the following commands:
+ ```shell
+ $ cd
+ $ python manage.py syncdb
+ ```
+
+::: warning
+You also have to note that the `sitestatic` directory has moved
+from `` to `` (it's probably the parent directory).
+
+You have to adapt your web server configuration to reflect this change.
+:::
\ No newline at end of file
diff --git a/doc-ng/installation/webserver.md b/doc-ng/installation/webserver.md
new file mode 100644
index 000000000..5e9fdab5e
--- /dev/null
+++ b/doc-ng/installation/webserver.md
@@ -0,0 +1,356 @@
+---
+title: manual step to install webserver component on modoboa
+description: This guide describe how to install webserver on the modoboa platform
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, installation, webserver, nginx, apache, wsgi'
+---
+
+# Web server {#webserver}
+
+::: info
+The following instructions are meant to help you get your site up and
+running quickly. However it is not possible for the people contributing
+documentation to Modoboa to test every single combination of web server,
+wsgi server, distribution, etc. So it is possible that **your**
+installation of uwsgi or nginx or Apache or what-have-you works
+differently. Keep this in mind.
+:::
+
+## Apache2
+
+First, make sure that `mod_wsgi` is installed on your server.
+
+Create a new virtualhost in your Apache configuration and put the
+following content inside:
+
+```txt
+WSGIPythonHome /var/www/modoboa/env
+WSGIPythonPath /var/www/modoboa/instance
+
+
+ ServerName
+
+ DocumentRoot {{ modoboa_instance_path }}
+
+ Alias /media/ {{ modoboa_instance_path }}/media/
+
+ Options FollowSymLinks
+ Require all granted
+
+
+ Alias /sitestatic/ {{ modoboa_instance_path }}/sitestatic/
+
+ Options FollowSymLinks
+ Require all granted
+
+
+ WSGIScriptAliasMatch ^/(api|accounts) {{ modoboa_instance_path }}/instance/wsgi.py
+ WSGIApplicationGroup %{GLOBAL}
+
+ # Pass Authorization header to enable API usage:
+ WSGIPassAuthorization On
+
+ Alias / {{ modoboa_instance_path }}/frontend
+
+ Options FollowSymLinks
+ Require all granted
+
+
+ LogLevel info
+
+```
+
+This is just one possible configuration.
+
+Replace values between `<>` with yours. If you don't use a [virtualenv](http://virtualenv.readthedocs.org/en/latest/),
+just remove the last part of the `WSGIDaemonProcess` directive. .. note:
+
+You may need to add:
+
+``` python [ {{ modoboa_instance_path }}/instance/wsgi.py]
+import sys
+
+sys.path.append('{{ modoboa_instance_path }}')
+sys.path.append('{{ modoboa_venv_path }}/bin')
+sys.path.append('{{ modoboa_venv_path }}/lib/python3.X/site-packages')
+```
+
+in :file:` {{ modoboa_instance_path }}/instance/wsgi.py`.
+
+::: tip
+You will certainly need more configuration in order to launch Apache.
+:::
+
+Now, you can go the `dovecot` section to continue the installation.
+
+## Nginx
+
+This section covers two different ways of running Modoboa behind
+[Nginx](http://nginx.org/) using a WSGI application server.
+
+Choose the one you prefer between
+[Green Unicorn](http://gunicorn.org/) or [uWSGI](https://github.com/unbit/uwsgi).
+
+In both cases, you\'ll need to download and [install nginx](http://wiki.nginx.org/Install).
+
+### uWSGI
+
+The following setup is meant to get you started quickly. You should read
+the documentation of both nginx and uwsgi to understand how to optimize
+their configuration for your site.
+
+The Django documentation includes the following warning regarding uwsgi:
+
+::: warning
+Use uwsgi 1.2.6 or newer. If you do not, you *will* run into problems.
+Modoboa will fail in obscure ways.
+:::
+
+To use this setup, first [download and install uwsgi](http://uwsgi-docs.readthedocs.org/en/latest/WSGIquickstart.html).
+
+Recommend packages for debian and derivatives:
+
+```shell
+apt install uwsgi uwsgi-plugin-python3
+```
+
+Here is a sample nginx configuration:
+
+``` nginx
+upstream modoboa {
+ server unix:{{ uwsgi_socket_path }} fail_timeout=0;
+}
+
+server {
+ listen 80;
+ listen [::]:80;
+ server_name {{ hostname }};
+ rewrite ^ https://$server_name$request_uri? permanent;
+}
+
+server {
+ listen 443 ssl http2;
+ listen [::]:443 ssl http2;
+ server_name {{ hostname }};
+ root {{ modoboa_instance_path }};
+
+ ssl_certificate ;
+ ssl_certificate_key ;
+ ssl_protocols TLSv1.2 TLSv1.3;
+ ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384";
+ ssl_prefer_server_ciphers on;
+ ssl_session_cache shared:SSL:10m;
+ ssl_verify_depth 3;
+ ssl_dhparam /etc/nginx/dhparam.pem;
+
+ client_max_body_size 10M;
+
+ access_log /var/log/nginx/modoboa-access.log;
+ error_log /var/log/nginx/modoboa-error.log;
+
+ location /sitestatic/ {
+ try_files $uri $uri/ =404;
+ }
+
+ location /media/ {
+ try_files $uri $uri/ =404;
+ }
+
+ location ~ ^/(api|accounts|autodiscover) {
+ include uwsgi_params;
+ uwsgi_param UWSGI_SCRIPT instance.wsgi:application;
+ uwsgi_pass modoboa;
+ }
+
+ location / {
+ alias {{ modoboa_instance_path }}/frontend/;
+ index index.html;
+
+ expires -1;
+ add_header Pragma "no-cache";
+ add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
+
+ try_files $uri $uri/ /index.html = 404;
+ }
+}
+```
+
+'uwsgi_socket_path', `modoboa instance name`, `hostname`,
+`modoboa_instance_path` and `ssl...` must be replaced by the value
+you used when `you deployed your instance deployment`
+
+In case you want to use enable auto-configuration functionality,
+
+create a second virtualhost configuration the hostname should look like `autoconfig.your domain here`
+
+```txt
+ server {
+ listen 80;
+ listen [::]:80;
+ server_name autoconfig.;
+ root {{ modoboa_instance_path }};
+
+ access_log /var/log/nginx/autoconfig-access.log;
+ error_log /var/log/nginx/autoconfig-error.log;
+
+ location ~ ^/(mail/config-v1.1.xml|mobileconfig) {
+ include uwsgi_params;
+ uwsgi_param UWSGI_SCRIPT instance.wsgi:application;
+ uwsgi_pass modoboa;
+ }
+}
+```
+
+`{{ modoboa_instance_path }}` and `your_domain` must be replaced by the
+value you used when `you deployed your instance `{.interpreted-text role="ref"}.
+
+If you do not plan to use SSL then change the listen directive to `listen 80;` and delete each of the following directives:
+
+```txt
+ ssl on;
+ keepalive_timeout 70;
+ ssl_certificate {{ssl certificate for your site}};
+ ssl_certificate_key {{ssl certificate key for your site}};
+ uwsgi_param UWSGI_SCHEME https;
+```
+
+If you do plan to use SSL, you\'ll have to generate a certificate and a
+key. [This article](http://wiki.nginx.org/HttpSslModule#Generate_Certificates)
+contains information about how to do it.
+
+Make sure to replace the `...` in the sample configuration with
+appropriate values. Here are some explanations for the cases that may
+not be completely self-explanatory:
+
+* `hostname`: This is the URL which will be the root of your Modoboa site at your
+ domain. For instance, if your Modoboa installation is reachable at
+ at `https://foo/modoboa` then `hostname` is `/modoboa`. In this
+ case you probably also have to set the `alias` directives to point
+ to where Modoboa\'s sitestatic and media directories are because
+ otherwise nginx won\'t be able to find them.
+
+ If Modoboa is at the root of your domain, then `hostname` is an
+ empty string and can be deleted from the configuration above. In
+ this case, you probably do not need the `alias` directives.
+* `uwsgi_socket_path`: The location where uwsig is listening. It could be a unix domain
+ socket or an address:port combination. Ubuntu configures uwsgi so that the port is:
+ ```txt
+ unix:/run/uwsgi/app/ app name /socket
+ ```
+ where `app name` is the name of the application.
+
+Your uwsgi configuration should be:
+
+``` ini{3,4}
+[uwsgi]
+# Not needed when using uwsgi from pip
+# plugins = python
+chdir =
+module = .wsgi:application
+master = true
+harakiri = 60
+processes = 4
+vhost = true
+no-default-app = true
+buffer-size = 8192
+max-requests = 5000
+single-interpreter = true
+```
+
+The plugins directive should be turned on if you use a uwsgi
+installation that requires it. If uwsgi was installed from pip, it does
+not require it. In the configuration above:
+
+* `modoboa_instance_path`: The directory where `manage.py` resides.
+ This directory is the parent of `modoboa's settings dir`
+* `name`: The name that you passed to `modoboa-admin.py deploy`
+ when you created your Modoboa instance, usually `instance`.
+
+Now, you can go the `dovecot` section to continue the installation.
+
+### Green Unicorn
+
+Firstly, [Download and install gunicorn](http://gunicorn.org/install.html).
+
+Then, use the following sample gunicorn configuration
+(create a new file named `gunicorn.conf.py` inside Modoboa's root dir):
+
+```ini gunicorn.conf.py
+backlog = 2048
+bind = "unix:/var/run/gunicorn/modoboa.sock"
+pidfile = "/var/run/gunicorn/modoboa.pid"
+daemon = True
+debug = False
+workers = 2
+logfile = "/var/log/gunicorn/modoboa.log"
+loglevel = "info"
+```
+
+
+To start gunicorn, execute the following commands:
+
+``` shell
+$ cd {{ modoboa dir }}
+$ gunicorn -c gunicorn.conf.py {{ APP/INSTANCE Name }}.wsgi:application
+```
+
+Now the nginx part. Just create a new virtual host and use the following
+configuration:
+
+``` nginx
+upstream modoboa {
+ server unix:/var/run/gunicorn/modoboa.sock fail_timeout=0;
+}
+
+server {
+ listen 443 ssl;
+ ssl on;
+ keepalive_timeout 70;
+
+ server_name {{ host fqdn }};
+ root {{ modoboa_instance_path }};
+
+ access_log /var/log/nginx/{{ host fqdn }}.access.log;
+ error_log /var/log/nginx/{{ host fqdn }}.error.log;
+
+ ssl_certificate {{ ssl certificate for your site }};
+ ssl_certificate_key {{ ssl certificate key for your site }};
+
+ location /sitestatic/ {
+ autoindex on;
+ }
+
+ location /media/ {
+ autoindex on;
+ }
+
+ location / {
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header Host $http_host;
+ proxy_redirect off;
+ proxy_set_header X-Forwarded-Protocol ssl;
+ proxy_pass http://modoboa;
+ }
+}
+```
+
+If you do not plan to use SSL then change the listen directive to `listen 80;`
+and delete each of the following directives:
+
+```txt
+ssl on;
+keepalive_timeout 70;
+ssl_certificate ;
+ssl_certificate_key ;
+proxy_set_header X-Forwarded-Protocol ssl;
+```
+
+If you do plan to use SSL, you\'ll have to generate a certificate and a key.
+
+[This article](http://wiki.nginx.org/HttpSslModule#Generate_Certificates) contains information about how to do it.
+
+Paste this content to your configuration (replace values between `<>` with yours) and restart nginx.
+
+Now, you can go the [Dovecot](./dovecot) section to continue the installation.
\ No newline at end of file
diff --git a/doc-ng/moving.md b/doc-ng/moving.md
new file mode 100644
index 000000000..da59f7d9f
--- /dev/null
+++ b/doc-ng/moving.md
@@ -0,0 +1,179 @@
+---
+title: Migrate to modoboa
+description: Migrate from actual provider to modoboa
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, imap, migration, constraint, offlineimap'
+---
+
+
+# Moving to Modoboa
+
+You have an existing platform and you\'d like to move to Modoboa, the
+following tools could help you.
+
+## Using IMAP protocol {#imap}
+
+::: info
+[Modoboa](https://modoboa.org/) provides a way to transfer accounts
+hosted on a different platform/provider to your personal server using
+the IMAP protocol and a tool called
+[OfflineIMAP](https://www.offlineimap.org/doc/installation.html).
+:::
+
+It works as follows:
+
+- You first declare what needs to be migrated, basically the domain(s)
+ you own and where they can be found. By doing that, you\'ll allow
+ accounts hosted on the platform/provider you\'re migrating from to log
+ into your Modoboa server
+- The first time an old account (one coming from your previous provider)
+ successfuly log into Modoboa, a dedicated migration task is created
+- On a regular basis (every hour generally), a cron job is responsible
+ for generating an OfflineIMAP configuration file including
+ instructions to migrate all declared migrations and lauching
+ OfflineIMAP to start/continue migrations
+
+:::: warning
+::: title
+Warning
+:::
+
+As you probably already understood, you can\'t migrate all your accounts
+at the same time since you don\'t know their corresponding password
+(most of the time). Every account migration must be started by the
+account\'s owner.
+::::
+
+### Configuration
+
+Modoboa won\'thandle the installation of OfflineIMAP so you need to do
+it on your own. The following
+[instructions](https://www.offlineimap.org/doc/quick_start.html) will
+help you.
+
+Once done, *the new admin interface (v2)* will let you:
+
+- define the neccessary information to `connect to your old
+ platform/provider`
+- define `what is to be migrated` (ie. the accounts)
+- customize the behaviour of OfflineIMAP : go to \'Parameters \> Imap
+ Migration\' from the left menu
+
+:::: warning
+::: title
+Warning
+:::
+
+**Auto create domain** option must be enabled to make this feature
+
+: work. Go to \'Parameters \> Administration \> Mailboxes\' and check
+ if it\'s the case.
+::::
+
+To generate an OfflineIMAP configuration file, run the following
+command:
+
+``` bash
+> python manage.py generate_offlineimap_config
+```
+
+The synchronization script must be configured to run periodically on
+your new server. Since it will copy mailboxes content to its final
+destination, filesystem permissions must be respected. To do that, it
+must be executed by the user which owns mailboxes (generally `vmail`).
+
+Here is a configuration example where the script is executed every
+hours. You can copy it inside the `/etc/cron.d/modoboa` file:
+
+``` shell
+PYTHON=/srv/modoboa/env/bin/python
+INSTANCE=/srv/modoboa/instance
+
+0 */1 * * * vmail cd /srv/vmail && $PYTHON $INSTANCE/manage.py generate_offlineimap_config --output .offlineimaprc && /usr/local/bin/offlineimap > /dev/null 2>&1
+```
+
+Feel free to adapt it.
+
+This will sync back-and-forth user mailbox.
+
+### Providers
+
+A provider represents an IMAP server that hosts one or several domains
+you own. It includes connection information (network address, port,
+etc.) and a list of hosted domain.
+
+For each domain you declare, you must provide its name. Modoboa will
+then create a local domain with the exact same name if needed (ie. if it
+does not exist yet) the first time it migrates an account.
+
+Optionaly, you have the possibility to rename a domain (for example:
+domain.com -\> domain.net) by providing a value to the \'Local domain\'
+field.
+
+### Migrations
+
+Migrations are automatically created, you don\'t need to do it.
+
+The first time an account of your old platform successfuly log into
+Modoboa (using old credentials), a local account and a migration task
+will be created.
+
+:::: warning
+::: title
+Warning
+:::
+
+In case of domain renaming as described in Providers section, users
+must use the new email address as username for next connection attempts.
+::::
+
+SuperAdmins can monitor running migrations and stop/delete them from the
+[IMAP Migration] section (left menu).
+
+## From postfixadmin
+
+A dedicated command allows you to convert an existing [postfixadmin](http://postfixadmin.sourceforge.net/)
+database to a Modoboa one.
+
+Consult the [documentation](https://github.com/modoboa/modoboa-pfxadmin-migrate) to know more about the process.
+
+## Using CSV files
+
+Modoboa allows you to import any object (domain, domain alias, mailbox
+and alias) using a simple CSV file encoded using **UTF8**.
+
+Each line corresponds to a single object and must respect one of the following format:
+
+```csv
+domain; ; ; ;
+domainalias; ; ;
+relaydomain; ; ; ; ; ;
+account; ; ; ; ; ; ; ; ; [, ...]
+alias; ; ; ; ...
+```
+
+Boolean fields accept the following values: `true`, `1`, `yes`, `y` (case insensitive).
+Any other value will be evaluated as false.
+
+::: warning
+The order does matter. Objects are created sequencially so a domain must
+be created before its mailboxes and aliases and a mailbox must created
+before its alias(es).
+:::
+
+To actually import such a file:
+
+``` bash
+$ sudo -u -i bash
+$ source /bin/activate
+$ cd
+$ python manage.py modo import
+```
+
+Available options can be listed using the following command:
+
+``` bash
+$ python manage.py modo import -h
+```
\ No newline at end of file
diff --git a/doc-ng/package.json b/doc-ng/package.json
new file mode 100644
index 000000000..c614f0a19
--- /dev/null
+++ b/doc-ng/package.json
@@ -0,0 +1,11 @@
+{
+ "devDependencies": {
+ "vitepress": "2.0.0-alpha.15"
+ },
+ "packageManager": "pnpm@10.22.0+sha512.bf049efe995b28f527fd2b41ae0474ce29186f7edcb3bf545087bd61fbbebb2bf75362d1307fda09c2d288e1e499787ac12d4fcb617a974718a6051f2eee741c",
+ "scripts": {
+ "dev": "vitepress dev",
+ "build": "vitepress build",
+ "preview": "vitepress preview"
+ }
+}
\ No newline at end of file
diff --git a/doc-ng/pnpm-lock.yaml b/doc-ng/pnpm-lock.yaml
new file mode 100644
index 000000000..32dd05e18
--- /dev/null
+++ b/doc-ng/pnpm-lock.yaml
@@ -0,0 +1,1837 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ devDependencies:
+ vitepress:
+ specifier: 2.0.0-alpha.15
+ version: 2.0.0-alpha.15(@algolia/client-search@5.46.2)(postcss@8.5.6)(react@19.2.3)(search-insights@2.17.3)
+
+packages:
+
+ '@ai-sdk/gateway@2.0.24':
+ resolution: {integrity: sha512-mflk80YF8hj8vrF9e1IHhovGKC1ubX+sY88pesSk3pUiXfH5VPO8dgzNnxjwsqsCZrnkHcztxS5cSl4TzSiEuA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
+ '@ai-sdk/provider-utils@3.0.20':
+ resolution: {integrity: sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
+ '@ai-sdk/provider@2.0.1':
+ resolution: {integrity: sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng==}
+ engines: {node: '>=18'}
+
+ '@ai-sdk/react@2.0.120':
+ resolution: {integrity: sha512-x7Oa2LDRURc8uRnAdcEfydbHLSXGYjNaFlQrGuxZAMfqhLJQ+7x4K8Z6O5vnLt414mrPaVvgirfRqsP/nsxtnw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ react: ^18 || ~19.0.1 || ~19.1.2 || ^19.2.1
+ zod: ^3.25.76 || ^4.1.8
+ peerDependenciesMeta:
+ zod:
+ optional: true
+
+ '@algolia/abtesting@1.12.2':
+ resolution: {integrity: sha512-oWknd6wpfNrmRcH0vzed3UPX0i17o4kYLM5OMITyMVM2xLgaRbIafoxL0e8mcrNNb0iORCJA0evnNDKRYth5WQ==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/autocomplete-core@1.19.2':
+ resolution: {integrity: sha512-mKv7RyuAzXvwmq+0XRK8HqZXt9iZ5Kkm2huLjgn5JoCPtDy+oh9yxUMfDDaVCw0oyzZ1isdJBc7l9nuCyyR7Nw==}
+
+ '@algolia/autocomplete-plugin-algolia-insights@1.19.2':
+ resolution: {integrity: sha512-TjxbcC/r4vwmnZaPwrHtkXNeqvlpdyR+oR9Wi2XyfORkiGkLTVhX2j+O9SaCCINbKoDfc+c2PB8NjfOnz7+oKg==}
+ peerDependencies:
+ search-insights: '>= 1 < 3'
+
+ '@algolia/autocomplete-shared@1.19.2':
+ resolution: {integrity: sha512-jEazxZTVD2nLrC+wYlVHQgpBoBB5KPStrJxLzsIFl6Kqd1AlG9sIAGl39V5tECLpIQzB3Qa2T6ZPJ1ChkwMK/w==}
+ peerDependencies:
+ '@algolia/client-search': '>= 4.9.1 < 6'
+ algoliasearch: '>= 4.9.1 < 6'
+
+ '@algolia/client-abtesting@5.46.2':
+ resolution: {integrity: sha512-oRSUHbylGIuxrlzdPA8FPJuwrLLRavOhAmFGgdAvMcX47XsyM+IOGa9tc7/K5SPvBqn4nhppOCEz7BrzOPWc4A==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/client-analytics@5.46.2':
+ resolution: {integrity: sha512-EPBN2Oruw0maWOF4OgGPfioTvd+gmiNwx0HmD9IgmlS+l75DatcBkKOPNJN+0z3wBQWUO5oq602ATxIfmTQ8bA==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/client-common@5.46.2':
+ resolution: {integrity: sha512-Hj8gswSJNKZ0oyd0wWissqyasm+wTz1oIsv5ZmLarzOZAp3vFEda8bpDQ8PUhO+DfkbiLyVnAxsPe4cGzWtqkg==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/client-insights@5.46.2':
+ resolution: {integrity: sha512-6dBZko2jt8FmQcHCbmNLB0kCV079Mx/DJcySTL3wirgDBUH7xhY1pOuUTLMiGkqM5D8moVZTvTdRKZUJRkrwBA==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/client-personalization@5.46.2':
+ resolution: {integrity: sha512-1waE2Uqh/PHNeDXGn/PM/WrmYOBiUGSVxAWqiJIj73jqPqvfzZgzdakHscIVaDl6Cp+j5dwjsZ5LCgaUr6DtmA==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/client-query-suggestions@5.46.2':
+ resolution: {integrity: sha512-EgOzTZkyDcNL6DV0V/24+oBJ+hKo0wNgyrOX/mePBM9bc9huHxIY2352sXmoZ648JXXY2x//V1kropF/Spx83w==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/client-search@5.46.2':
+ resolution: {integrity: sha512-ZsOJqu4HOG5BlvIFnMU0YKjQ9ZI6r3C31dg2jk5kMWPSdhJpYL9xa5hEe7aieE+707dXeMI4ej3diy6mXdZpgA==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/ingestion@1.46.2':
+ resolution: {integrity: sha512-1Uw2OslTWiOFDtt83y0bGiErJYy5MizadV0nHnOoHFWMoDqWW0kQoMFI65pXqRSkVvit5zjXSLik2xMiyQJDWQ==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/monitoring@1.46.2':
+ resolution: {integrity: sha512-xk9f+DPtNcddWN6E7n1hyNNsATBCHIqAvVGG2EAGHJc4AFYL18uM/kMTiOKXE/LKDPyy1JhIerrh9oYb7RBrgw==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/recommend@5.46.2':
+ resolution: {integrity: sha512-NApbTPj9LxGzNw4dYnZmj2BoXiAc8NmbbH6qBNzQgXklGklt/xldTvu+FACN6ltFsTzoNU6j2mWNlHQTKGC5+Q==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/requester-browser-xhr@5.46.2':
+ resolution: {integrity: sha512-ekotpCwpSp033DIIrsTpYlGUCF6momkgupRV/FA3m62SreTSZUKjgK6VTNyG7TtYfq9YFm/pnh65bATP/ZWJEg==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/requester-fetch@5.46.2':
+ resolution: {integrity: sha512-gKE+ZFi/6y7saTr34wS0SqYFDcjHW4Wminv8PDZEi0/mE99+hSrbKgJWxo2ztb5eqGirQTgIh1AMVacGGWM1iw==}
+ engines: {node: '>= 14.0.0'}
+
+ '@algolia/requester-node-http@5.46.2':
+ resolution: {integrity: sha512-ciPihkletp7ttweJ8Zt+GukSVLp2ANJHU+9ttiSxsJZThXc4Y2yJ8HGVWesW5jN1zrsZsezN71KrMx/iZsOYpg==}
+ engines: {node: '>= 14.0.0'}
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.28.5':
+ resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.28.5':
+ resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/types@7.28.5':
+ resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
+ engines: {node: '>=6.9.0'}
+
+ '@docsearch/core@4.4.0':
+ resolution: {integrity: sha512-kiwNo5KEndOnrf5Kq/e5+D9NBMCFgNsDoRpKQJ9o/xnSlheh6b8AXppMuuUVVdAUIhIfQFk/07VLjjk/fYyKmw==}
+ peerDependencies:
+ '@types/react': '>= 16.8.0 < 20.0.0'
+ react: '>= 16.8.0 < 20.0.0'
+ react-dom: '>= 16.8.0 < 20.0.0'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+
+ '@docsearch/css@4.4.0':
+ resolution: {integrity: sha512-e9vPgtih6fkawakmYo0Y6V4BKBmDV7Ykudn7ADWXUs5b6pmtBRwDbpSG/WiaUG63G28OkJDEnsMvgIAnZgGwYw==}
+
+ '@docsearch/js@4.4.0':
+ resolution: {integrity: sha512-vCiKzjYD54bugUIMZA6YzuLDilkD3TNH/kfbvqsnzxiLTMu8F13psD+hdMSEOn7j+dFJOaf49fZ+gwr+rXctMw==}
+
+ '@docsearch/react@4.4.0':
+ resolution: {integrity: sha512-z12zeg1mV7WD4Ag4pKSuGukETJLaucVFwszDXL/qLaEgRqxEaVacO9SR1qqnCXvZztlvz2rt7cMqryi/7sKfjA==}
+ peerDependencies:
+ '@types/react': '>= 16.8.0 < 20.0.0'
+ react: '>= 16.8.0 < 20.0.0'
+ react-dom: '>= 16.8.0 < 20.0.0'
+ search-insights: '>= 1 < 3'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+ search-insights:
+ optional: true
+
+ '@esbuild/aix-ppc64@0.27.2':
+ resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.27.2':
+ resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.27.2':
+ resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.27.2':
+ resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.27.2':
+ resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.27.2':
+ resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.27.2':
+ resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.27.2':
+ resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.27.2':
+ resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.27.2':
+ resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.27.2':
+ resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.27.2':
+ resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.27.2':
+ resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.27.2':
+ resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.27.2':
+ resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.27.2':
+ resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.27.2':
+ resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.27.2':
+ resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.27.2':
+ resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.27.2':
+ resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.27.2':
+ resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.27.2':
+ resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.27.2':
+ resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.27.2':
+ resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.27.2':
+ resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.27.2':
+ resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@iconify-json/simple-icons@1.2.65':
+ resolution: {integrity: sha512-v/O0UeqrDz6ASuRVE5g2Puo5aWyej4M/CxX6WYDBARgswwxK0mp3VQbGgPFEAAUU9QN02IjTgjMuO021gpWf2w==}
+
+ '@iconify/types@2.0.0':
+ resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@opentelemetry/api@1.9.0':
+ resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
+ engines: {node: '>=8.0.0'}
+
+ '@rolldown/pluginutils@1.0.0-beta.53':
+ resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==}
+
+ '@rollup/rollup-android-arm-eabi@4.55.1':
+ resolution: {integrity: sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.55.1':
+ resolution: {integrity: sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.55.1':
+ resolution: {integrity: sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.55.1':
+ resolution: {integrity: sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.55.1':
+ resolution: {integrity: sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.55.1':
+ resolution: {integrity: sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.55.1':
+ resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.55.1':
+ resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.55.1':
+ resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-musl@4.55.1':
+ resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loong64-gnu@4.55.1':
+ resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loong64-musl@4.55.1':
+ resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.55.1':
+ resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-musl@4.55.1':
+ resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.55.1':
+ resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-musl@4.55.1':
+ resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.55.1':
+ resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.55.1':
+ resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-musl@4.55.1':
+ resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-openbsd-x64@4.55.1':
+ resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@rollup/rollup-openharmony-arm64@4.55.1':
+ resolution: {integrity: sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rollup/rollup-win32-arm64-msvc@4.55.1':
+ resolution: {integrity: sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.55.1':
+ resolution: {integrity: sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-gnu@4.55.1':
+ resolution: {integrity: sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==}
+ cpu: [x64]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.55.1':
+ resolution: {integrity: sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==}
+ cpu: [x64]
+ os: [win32]
+
+ '@shikijs/core@3.21.0':
+ resolution: {integrity: sha512-AXSQu/2n1UIQekY8euBJlvFYZIw0PHY63jUzGbrOma4wPxzznJXTXkri+QcHeBNaFxiiOljKxxJkVSoB3PjbyA==}
+
+ '@shikijs/engine-javascript@3.21.0':
+ resolution: {integrity: sha512-ATwv86xlbmfD9n9gKRiwuPpWgPENAWCLwYCGz9ugTJlsO2kOzhOkvoyV/UD+tJ0uT7YRyD530x6ugNSffmvIiQ==}
+
+ '@shikijs/engine-oniguruma@3.21.0':
+ resolution: {integrity: sha512-OYknTCct6qiwpQDqDdf3iedRdzj6hFlOPv5hMvI+hkWfCKs5mlJ4TXziBG9nyabLwGulrUjHiCq3xCspSzErYQ==}
+
+ '@shikijs/langs@3.21.0':
+ resolution: {integrity: sha512-g6mn5m+Y6GBJ4wxmBYqalK9Sp0CFkUqfNzUy2pJglUginz6ZpWbaWjDB4fbQ/8SHzFjYbtU6Ddlp1pc+PPNDVA==}
+
+ '@shikijs/themes@3.21.0':
+ resolution: {integrity: sha512-BAE4cr9EDiZyYzwIHEk7JTBJ9CzlPuM4PchfcA5ao1dWXb25nv6hYsoDiBq2aZK9E3dlt3WB78uI96UESD+8Mw==}
+
+ '@shikijs/transformers@3.21.0':
+ resolution: {integrity: sha512-CZwvCWWIiRRiFk9/JKzdEooakAP8mQDtBOQ1TKiCaS2E1bYtyBCOkUzS8akO34/7ufICQ29oeSfkb3tT5KtrhA==}
+
+ '@shikijs/types@3.21.0':
+ resolution: {integrity: sha512-zGrWOxZ0/+0ovPY7PvBU2gIS9tmhSUUt30jAcNV0Bq0gb2S98gwfjIs1vxlmH5zM7/4YxLamT6ChlqqAJmPPjA==}
+
+ '@shikijs/vscode-textmate@10.0.2':
+ resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
+
+ '@standard-schema/spec@1.1.0':
+ resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
+
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+ '@types/hast@3.0.4':
+ resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+
+ '@types/linkify-it@5.0.0':
+ resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==}
+
+ '@types/markdown-it@14.1.2':
+ resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==}
+
+ '@types/mdast@4.0.4':
+ resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
+
+ '@types/mdurl@2.0.0':
+ resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
+
+ '@types/unist@3.0.3':
+ resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+
+ '@types/web-bluetooth@0.0.21':
+ resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
+
+ '@ungap/structured-clone@1.3.0':
+ resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
+
+ '@vercel/oidc@3.0.5':
+ resolution: {integrity: sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==}
+ engines: {node: '>= 20'}
+
+ '@vitejs/plugin-vue@6.0.3':
+ resolution: {integrity: sha512-TlGPkLFLVOY3T7fZrwdvKpjprR3s4fxRln0ORDo1VQ7HHyxJwTlrjKU3kpVWTlaAjIEuCTokmjkZnr8Tpc925w==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ peerDependencies:
+ vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
+ vue: ^3.2.25
+
+ '@vue/compiler-core@3.5.26':
+ resolution: {integrity: sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==}
+
+ '@vue/compiler-dom@3.5.26':
+ resolution: {integrity: sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==}
+
+ '@vue/compiler-sfc@3.5.26':
+ resolution: {integrity: sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA==}
+
+ '@vue/compiler-ssr@3.5.26':
+ resolution: {integrity: sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw==}
+
+ '@vue/devtools-api@8.0.5':
+ resolution: {integrity: sha512-DgVcW8H/Nral7LgZEecYFFYXnAvGuN9C3L3DtWekAncFBedBczpNW8iHKExfaM559Zm8wQWrwtYZ9lXthEHtDw==}
+
+ '@vue/devtools-kit@8.0.5':
+ resolution: {integrity: sha512-q2VV6x1U3KJMTQPUlRMyWEKVbcHuxhqJdSr6Jtjz5uAThAIrfJ6WVZdGZm5cuO63ZnSUz0RCsVwiUUb0mDV0Yg==}
+
+ '@vue/devtools-shared@8.0.5':
+ resolution: {integrity: sha512-bRLn6/spxpmgLk+iwOrR29KrYnJjG9DGpHGkDFG82UM21ZpJ39ztUT9OXX3g+usW7/b2z+h46I9ZiYyB07XMXg==}
+
+ '@vue/reactivity@3.5.26':
+ resolution: {integrity: sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ==}
+
+ '@vue/runtime-core@3.5.26':
+ resolution: {integrity: sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q==}
+
+ '@vue/runtime-dom@3.5.26':
+ resolution: {integrity: sha512-XLLd/+4sPC2ZkN/6+V4O4gjJu6kSDbHAChvsyWgm1oGbdSO3efvGYnm25yCjtFm/K7rrSDvSfPDgN1pHgS4VNQ==}
+
+ '@vue/server-renderer@3.5.26':
+ resolution: {integrity: sha512-TYKLXmrwWKSodyVuO1WAubucd+1XlLg4set0YoV+Hu8Lo79mp/YMwWV5mC5FgtsDxX3qo1ONrxFaTP1OQgy1uA==}
+ peerDependencies:
+ vue: 3.5.26
+
+ '@vue/shared@3.5.26':
+ resolution: {integrity: sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==}
+
+ '@vueuse/core@14.1.0':
+ resolution: {integrity: sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw==}
+ peerDependencies:
+ vue: ^3.5.0
+
+ '@vueuse/integrations@14.1.0':
+ resolution: {integrity: sha512-eNQPdisnO9SvdydTIXnTE7c29yOsJBD/xkwEyQLdhDC/LKbqrFpXHb3uS//7NcIrQO3fWVuvMGp8dbK6mNEMCA==}
+ peerDependencies:
+ async-validator: ^4
+ axios: ^1
+ change-case: ^5
+ drauu: ^0.4
+ focus-trap: ^7
+ fuse.js: ^7
+ idb-keyval: ^6
+ jwt-decode: ^4
+ nprogress: ^0.2
+ qrcode: ^1.5
+ sortablejs: ^1
+ universal-cookie: ^7 || ^8
+ vue: ^3.5.0
+ peerDependenciesMeta:
+ async-validator:
+ optional: true
+ axios:
+ optional: true
+ change-case:
+ optional: true
+ drauu:
+ optional: true
+ focus-trap:
+ optional: true
+ fuse.js:
+ optional: true
+ idb-keyval:
+ optional: true
+ jwt-decode:
+ optional: true
+ nprogress:
+ optional: true
+ qrcode:
+ optional: true
+ sortablejs:
+ optional: true
+ universal-cookie:
+ optional: true
+
+ '@vueuse/metadata@14.1.0':
+ resolution: {integrity: sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA==}
+
+ '@vueuse/shared@14.1.0':
+ resolution: {integrity: sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw==}
+ peerDependencies:
+ vue: ^3.5.0
+
+ ai@5.0.118:
+ resolution: {integrity: sha512-sKJHfhJkvAyq5NC3yJJ4R8Z3tn4pSHF760/jInKAtmLwPLWTHfGo293DSO4un8QUAgJOagHd09VSXOXv+STMNQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
+ algoliasearch@5.46.2:
+ resolution: {integrity: sha512-qqAXW9QvKf2tTyhpDA4qXv1IfBwD2eduSW6tUEBFIfCeE9gn9HQ9I5+MaKoenRuHrzk5sQoNh1/iof8mY7uD6Q==}
+ engines: {node: '>= 14.0.0'}
+
+ birpc@2.9.0:
+ resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==}
+
+ ccount@2.0.1:
+ resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+
+ character-entities-html4@2.1.0:
+ resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
+
+ character-entities-legacy@3.0.0:
+ resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==}
+
+ comma-separated-tokens@2.0.3:
+ resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
+
+ copy-anything@4.0.5:
+ resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==}
+ engines: {node: '>=18'}
+
+ csstype@3.2.3:
+ resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
+ devlop@1.1.0:
+ resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+
+ entities@7.0.0:
+ resolution: {integrity: sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==}
+ engines: {node: '>=0.12'}
+
+ esbuild@0.27.2:
+ resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ estree-walker@2.0.2:
+ resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+ eventsource-parser@3.0.6:
+ resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==}
+ engines: {node: '>=18.0.0'}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ focus-trap@7.7.1:
+ resolution: {integrity: sha512-Pkp8m55GjxBLnhBoT6OXdMvfRr4TjMAKLvFM566zlIryq5plbhaTmLAJWTGR0EkRwLjEte1lCOG9MxF1ipJrOg==}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ hast-util-to-html@9.0.5:
+ resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==}
+
+ hast-util-whitespace@3.0.0:
+ resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
+
+ hookable@5.5.3:
+ resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
+
+ htm@3.1.1:
+ resolution: {integrity: sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ==}
+
+ html-void-elements@3.0.0:
+ resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
+
+ is-what@5.5.0:
+ resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==}
+ engines: {node: '>=18'}
+
+ json-schema@0.4.0:
+ resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
+
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+
+ mark.js@8.11.1:
+ resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==}
+
+ marked@16.4.2:
+ resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==}
+ engines: {node: '>= 20'}
+ hasBin: true
+
+ mdast-util-to-hast@13.2.1:
+ resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==}
+
+ micromark-util-character@2.1.1:
+ resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==}
+
+ micromark-util-encode@2.0.1:
+ resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==}
+
+ micromark-util-sanitize-uri@2.0.1:
+ resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==}
+
+ micromark-util-symbol@2.0.1:
+ resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==}
+
+ micromark-util-types@2.0.2:
+ resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==}
+
+ minisearch@7.2.0:
+ resolution: {integrity: sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==}
+
+ mitt@3.0.1:
+ resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ oniguruma-parser@0.12.1:
+ resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==}
+
+ oniguruma-to-es@4.3.4:
+ resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==}
+
+ perfect-debounce@2.0.0:
+ resolution: {integrity: sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@4.0.3:
+ resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+ engines: {node: '>=12'}
+
+ postcss@8.5.6:
+ resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ property-information@7.1.0:
+ resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==}
+
+ react@19.2.3:
+ resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==}
+ engines: {node: '>=0.10.0'}
+
+ regex-recursion@6.0.2:
+ resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
+
+ regex-utilities@2.3.0:
+ resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==}
+
+ regex@6.1.0:
+ resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==}
+
+ rfdc@1.4.1:
+ resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
+ rollup@4.55.1:
+ resolution: {integrity: sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ search-insights@2.17.3:
+ resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==}
+
+ shiki@3.21.0:
+ resolution: {integrity: sha512-N65B/3bqL/TI2crrXr+4UivctrAGEjmsib5rPMMPpFp1xAx/w03v8WZ9RDDFYteXoEgY7qZ4HGgl5KBIu1153w==}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ space-separated-tokens@2.0.2:
+ resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
+
+ speakingurl@14.0.1:
+ resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
+ engines: {node: '>=0.10.0'}
+
+ stringify-entities@4.0.4:
+ resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
+
+ superjson@2.2.6:
+ resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==}
+ engines: {node: '>=16'}
+
+ swr@2.3.8:
+ resolution: {integrity: sha512-gaCPRVoMq8WGDcWj9p4YWzCMPHzE0WNl6W8ADIx9c3JBEIdMkJGMzW+uzXvxHMltwcYACr9jP+32H8/hgwMR7w==}
+ peerDependencies:
+ react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ tabbable@6.4.0:
+ resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==}
+
+ throttleit@2.1.0:
+ resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==}
+ engines: {node: '>=18'}
+
+ tinyglobby@0.2.15:
+ resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
+ engines: {node: '>=12.0.0'}
+
+ trim-lines@3.0.1:
+ resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
+
+ unist-util-is@6.0.1:
+ resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==}
+
+ unist-util-position@5.0.0:
+ resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
+
+ unist-util-stringify-position@4.0.0:
+ resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+
+ unist-util-visit-parents@6.0.2:
+ resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==}
+
+ unist-util-visit@5.0.0:
+ resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
+
+ use-sync-external-store@1.6.0:
+ resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ vfile-message@4.0.3:
+ resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==}
+
+ vfile@6.0.3:
+ resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
+
+ vite@7.3.1:
+ resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^20.19.0 || >=22.12.0
+ jiti: '>=1.21.0'
+ less: ^4.0.0
+ lightningcss: ^1.21.0
+ sass: ^1.70.0
+ sass-embedded: ^1.70.0
+ stylus: '>=0.54.8'
+ sugarss: ^5.0.0
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ vitepress@2.0.0-alpha.15:
+ resolution: {integrity: sha512-jhjSYd10Z6RZiKOa7jy0xMVf5NB5oSc/lS3bD/QoUc6V8PrvQR5JhC9104NEt6+oTGY/ftieVWxY9v7YI+1IjA==}
+ hasBin: true
+ peerDependencies:
+ markdown-it-mathjax3: ^4
+ oxc-minify: '*'
+ postcss: ^8
+ peerDependenciesMeta:
+ markdown-it-mathjax3:
+ optional: true
+ oxc-minify:
+ optional: true
+ postcss:
+ optional: true
+
+ vue@3.5.26:
+ resolution: {integrity: sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ zod@4.3.5:
+ resolution: {integrity: sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==}
+
+ zwitch@2.0.4:
+ resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
+
+snapshots:
+
+ '@ai-sdk/gateway@2.0.24(zod@4.3.5)':
+ dependencies:
+ '@ai-sdk/provider': 2.0.1
+ '@ai-sdk/provider-utils': 3.0.20(zod@4.3.5)
+ '@vercel/oidc': 3.0.5
+ zod: 4.3.5
+
+ '@ai-sdk/provider-utils@3.0.20(zod@4.3.5)':
+ dependencies:
+ '@ai-sdk/provider': 2.0.1
+ '@standard-schema/spec': 1.1.0
+ eventsource-parser: 3.0.6
+ zod: 4.3.5
+
+ '@ai-sdk/provider@2.0.1':
+ dependencies:
+ json-schema: 0.4.0
+
+ '@ai-sdk/react@2.0.120(react@19.2.3)(zod@4.3.5)':
+ dependencies:
+ '@ai-sdk/provider-utils': 3.0.20(zod@4.3.5)
+ ai: 5.0.118(zod@4.3.5)
+ react: 19.2.3
+ swr: 2.3.8(react@19.2.3)
+ throttleit: 2.1.0
+ optionalDependencies:
+ zod: 4.3.5
+
+ '@algolia/abtesting@1.12.2':
+ dependencies:
+ '@algolia/client-common': 5.46.2
+ '@algolia/requester-browser-xhr': 5.46.2
+ '@algolia/requester-fetch': 5.46.2
+ '@algolia/requester-node-http': 5.46.2
+
+ '@algolia/autocomplete-core@1.19.2(@algolia/client-search@5.46.2)(algoliasearch@5.46.2)(search-insights@2.17.3)':
+ dependencies:
+ '@algolia/autocomplete-plugin-algolia-insights': 1.19.2(@algolia/client-search@5.46.2)(algoliasearch@5.46.2)(search-insights@2.17.3)
+ '@algolia/autocomplete-shared': 1.19.2(@algolia/client-search@5.46.2)(algoliasearch@5.46.2)
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+ - algoliasearch
+ - search-insights
+
+ '@algolia/autocomplete-plugin-algolia-insights@1.19.2(@algolia/client-search@5.46.2)(algoliasearch@5.46.2)(search-insights@2.17.3)':
+ dependencies:
+ '@algolia/autocomplete-shared': 1.19.2(@algolia/client-search@5.46.2)(algoliasearch@5.46.2)
+ search-insights: 2.17.3
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+ - algoliasearch
+
+ '@algolia/autocomplete-shared@1.19.2(@algolia/client-search@5.46.2)(algoliasearch@5.46.2)':
+ dependencies:
+ '@algolia/client-search': 5.46.2
+ algoliasearch: 5.46.2
+
+ '@algolia/client-abtesting@5.46.2':
+ dependencies:
+ '@algolia/client-common': 5.46.2
+ '@algolia/requester-browser-xhr': 5.46.2
+ '@algolia/requester-fetch': 5.46.2
+ '@algolia/requester-node-http': 5.46.2
+
+ '@algolia/client-analytics@5.46.2':
+ dependencies:
+ '@algolia/client-common': 5.46.2
+ '@algolia/requester-browser-xhr': 5.46.2
+ '@algolia/requester-fetch': 5.46.2
+ '@algolia/requester-node-http': 5.46.2
+
+ '@algolia/client-common@5.46.2': {}
+
+ '@algolia/client-insights@5.46.2':
+ dependencies:
+ '@algolia/client-common': 5.46.2
+ '@algolia/requester-browser-xhr': 5.46.2
+ '@algolia/requester-fetch': 5.46.2
+ '@algolia/requester-node-http': 5.46.2
+
+ '@algolia/client-personalization@5.46.2':
+ dependencies:
+ '@algolia/client-common': 5.46.2
+ '@algolia/requester-browser-xhr': 5.46.2
+ '@algolia/requester-fetch': 5.46.2
+ '@algolia/requester-node-http': 5.46.2
+
+ '@algolia/client-query-suggestions@5.46.2':
+ dependencies:
+ '@algolia/client-common': 5.46.2
+ '@algolia/requester-browser-xhr': 5.46.2
+ '@algolia/requester-fetch': 5.46.2
+ '@algolia/requester-node-http': 5.46.2
+
+ '@algolia/client-search@5.46.2':
+ dependencies:
+ '@algolia/client-common': 5.46.2
+ '@algolia/requester-browser-xhr': 5.46.2
+ '@algolia/requester-fetch': 5.46.2
+ '@algolia/requester-node-http': 5.46.2
+
+ '@algolia/ingestion@1.46.2':
+ dependencies:
+ '@algolia/client-common': 5.46.2
+ '@algolia/requester-browser-xhr': 5.46.2
+ '@algolia/requester-fetch': 5.46.2
+ '@algolia/requester-node-http': 5.46.2
+
+ '@algolia/monitoring@1.46.2':
+ dependencies:
+ '@algolia/client-common': 5.46.2
+ '@algolia/requester-browser-xhr': 5.46.2
+ '@algolia/requester-fetch': 5.46.2
+ '@algolia/requester-node-http': 5.46.2
+
+ '@algolia/recommend@5.46.2':
+ dependencies:
+ '@algolia/client-common': 5.46.2
+ '@algolia/requester-browser-xhr': 5.46.2
+ '@algolia/requester-fetch': 5.46.2
+ '@algolia/requester-node-http': 5.46.2
+
+ '@algolia/requester-browser-xhr@5.46.2':
+ dependencies:
+ '@algolia/client-common': 5.46.2
+
+ '@algolia/requester-fetch@5.46.2':
+ dependencies:
+ '@algolia/client-common': 5.46.2
+
+ '@algolia/requester-node-http@5.46.2':
+ dependencies:
+ '@algolia/client-common': 5.46.2
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.28.5': {}
+
+ '@babel/parser@7.28.5':
+ dependencies:
+ '@babel/types': 7.28.5
+
+ '@babel/types@7.28.5':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
+
+ '@docsearch/core@4.4.0(react@19.2.3)':
+ optionalDependencies:
+ react: 19.2.3
+
+ '@docsearch/css@4.4.0': {}
+
+ '@docsearch/js@4.4.0(@algolia/client-search@5.46.2)(react@19.2.3)(search-insights@2.17.3)':
+ dependencies:
+ '@docsearch/react': 4.4.0(@algolia/client-search@5.46.2)(react@19.2.3)(search-insights@2.17.3)
+ htm: 3.1.1
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+ - '@types/react'
+ - react
+ - react-dom
+ - search-insights
+
+ '@docsearch/react@4.4.0(@algolia/client-search@5.46.2)(react@19.2.3)(search-insights@2.17.3)':
+ dependencies:
+ '@ai-sdk/react': 2.0.120(react@19.2.3)(zod@4.3.5)
+ '@algolia/autocomplete-core': 1.19.2(@algolia/client-search@5.46.2)(algoliasearch@5.46.2)(search-insights@2.17.3)
+ '@docsearch/core': 4.4.0(react@19.2.3)
+ '@docsearch/css': 4.4.0
+ ai: 5.0.118(zod@4.3.5)
+ algoliasearch: 5.46.2
+ marked: 16.4.2
+ zod: 4.3.5
+ optionalDependencies:
+ react: 19.2.3
+ search-insights: 2.17.3
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+
+ '@esbuild/aix-ppc64@0.27.2':
+ optional: true
+
+ '@esbuild/android-arm64@0.27.2':
+ optional: true
+
+ '@esbuild/android-arm@0.27.2':
+ optional: true
+
+ '@esbuild/android-x64@0.27.2':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.27.2':
+ optional: true
+
+ '@esbuild/darwin-x64@0.27.2':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.27.2':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.27.2':
+ optional: true
+
+ '@esbuild/linux-arm64@0.27.2':
+ optional: true
+
+ '@esbuild/linux-arm@0.27.2':
+ optional: true
+
+ '@esbuild/linux-ia32@0.27.2':
+ optional: true
+
+ '@esbuild/linux-loong64@0.27.2':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.27.2':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.27.2':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.27.2':
+ optional: true
+
+ '@esbuild/linux-s390x@0.27.2':
+ optional: true
+
+ '@esbuild/linux-x64@0.27.2':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.27.2':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.27.2':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.27.2':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.27.2':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.27.2':
+ optional: true
+
+ '@esbuild/sunos-x64@0.27.2':
+ optional: true
+
+ '@esbuild/win32-arm64@0.27.2':
+ optional: true
+
+ '@esbuild/win32-ia32@0.27.2':
+ optional: true
+
+ '@esbuild/win32-x64@0.27.2':
+ optional: true
+
+ '@iconify-json/simple-icons@1.2.65':
+ dependencies:
+ '@iconify/types': 2.0.0
+
+ '@iconify/types@2.0.0': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@opentelemetry/api@1.9.0': {}
+
+ '@rolldown/pluginutils@1.0.0-beta.53': {}
+
+ '@rollup/rollup-android-arm-eabi@4.55.1':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.55.1':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.55.1':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.55.1':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.55.1':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.55.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.55.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.55.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.55.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.55.1':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-gnu@4.55.1':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-musl@4.55.1':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-gnu@4.55.1':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-musl@4.55.1':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.55.1':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.55.1':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.55.1':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.55.1':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.55.1':
+ optional: true
+
+ '@rollup/rollup-openbsd-x64@4.55.1':
+ optional: true
+
+ '@rollup/rollup-openharmony-arm64@4.55.1':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.55.1':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.55.1':
+ optional: true
+
+ '@rollup/rollup-win32-x64-gnu@4.55.1':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.55.1':
+ optional: true
+
+ '@shikijs/core@3.21.0':
+ dependencies:
+ '@shikijs/types': 3.21.0
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+ hast-util-to-html: 9.0.5
+
+ '@shikijs/engine-javascript@3.21.0':
+ dependencies:
+ '@shikijs/types': 3.21.0
+ '@shikijs/vscode-textmate': 10.0.2
+ oniguruma-to-es: 4.3.4
+
+ '@shikijs/engine-oniguruma@3.21.0':
+ dependencies:
+ '@shikijs/types': 3.21.0
+ '@shikijs/vscode-textmate': 10.0.2
+
+ '@shikijs/langs@3.21.0':
+ dependencies:
+ '@shikijs/types': 3.21.0
+
+ '@shikijs/themes@3.21.0':
+ dependencies:
+ '@shikijs/types': 3.21.0
+
+ '@shikijs/transformers@3.21.0':
+ dependencies:
+ '@shikijs/core': 3.21.0
+ '@shikijs/types': 3.21.0
+
+ '@shikijs/types@3.21.0':
+ dependencies:
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+
+ '@shikijs/vscode-textmate@10.0.2': {}
+
+ '@standard-schema/spec@1.1.0': {}
+
+ '@types/estree@1.0.8': {}
+
+ '@types/hast@3.0.4':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/linkify-it@5.0.0': {}
+
+ '@types/markdown-it@14.1.2':
+ dependencies:
+ '@types/linkify-it': 5.0.0
+ '@types/mdurl': 2.0.0
+
+ '@types/mdast@4.0.4':
+ dependencies:
+ '@types/unist': 3.0.3
+
+ '@types/mdurl@2.0.0': {}
+
+ '@types/unist@3.0.3': {}
+
+ '@types/web-bluetooth@0.0.21': {}
+
+ '@ungap/structured-clone@1.3.0': {}
+
+ '@vercel/oidc@3.0.5': {}
+
+ '@vitejs/plugin-vue@6.0.3(vite@7.3.1)(vue@3.5.26)':
+ dependencies:
+ '@rolldown/pluginutils': 1.0.0-beta.53
+ vite: 7.3.1
+ vue: 3.5.26
+
+ '@vue/compiler-core@3.5.26':
+ dependencies:
+ '@babel/parser': 7.28.5
+ '@vue/shared': 3.5.26
+ entities: 7.0.0
+ estree-walker: 2.0.2
+ source-map-js: 1.2.1
+
+ '@vue/compiler-dom@3.5.26':
+ dependencies:
+ '@vue/compiler-core': 3.5.26
+ '@vue/shared': 3.5.26
+
+ '@vue/compiler-sfc@3.5.26':
+ dependencies:
+ '@babel/parser': 7.28.5
+ '@vue/compiler-core': 3.5.26
+ '@vue/compiler-dom': 3.5.26
+ '@vue/compiler-ssr': 3.5.26
+ '@vue/shared': 3.5.26
+ estree-walker: 2.0.2
+ magic-string: 0.30.21
+ postcss: 8.5.6
+ source-map-js: 1.2.1
+
+ '@vue/compiler-ssr@3.5.26':
+ dependencies:
+ '@vue/compiler-dom': 3.5.26
+ '@vue/shared': 3.5.26
+
+ '@vue/devtools-api@8.0.5':
+ dependencies:
+ '@vue/devtools-kit': 8.0.5
+
+ '@vue/devtools-kit@8.0.5':
+ dependencies:
+ '@vue/devtools-shared': 8.0.5
+ birpc: 2.9.0
+ hookable: 5.5.3
+ mitt: 3.0.1
+ perfect-debounce: 2.0.0
+ speakingurl: 14.0.1
+ superjson: 2.2.6
+
+ '@vue/devtools-shared@8.0.5':
+ dependencies:
+ rfdc: 1.4.1
+
+ '@vue/reactivity@3.5.26':
+ dependencies:
+ '@vue/shared': 3.5.26
+
+ '@vue/runtime-core@3.5.26':
+ dependencies:
+ '@vue/reactivity': 3.5.26
+ '@vue/shared': 3.5.26
+
+ '@vue/runtime-dom@3.5.26':
+ dependencies:
+ '@vue/reactivity': 3.5.26
+ '@vue/runtime-core': 3.5.26
+ '@vue/shared': 3.5.26
+ csstype: 3.2.3
+
+ '@vue/server-renderer@3.5.26(vue@3.5.26)':
+ dependencies:
+ '@vue/compiler-ssr': 3.5.26
+ '@vue/shared': 3.5.26
+ vue: 3.5.26
+
+ '@vue/shared@3.5.26': {}
+
+ '@vueuse/core@14.1.0(vue@3.5.26)':
+ dependencies:
+ '@types/web-bluetooth': 0.0.21
+ '@vueuse/metadata': 14.1.0
+ '@vueuse/shared': 14.1.0(vue@3.5.26)
+ vue: 3.5.26
+
+ '@vueuse/integrations@14.1.0(focus-trap@7.7.1)(vue@3.5.26)':
+ dependencies:
+ '@vueuse/core': 14.1.0(vue@3.5.26)
+ '@vueuse/shared': 14.1.0(vue@3.5.26)
+ vue: 3.5.26
+ optionalDependencies:
+ focus-trap: 7.7.1
+
+ '@vueuse/metadata@14.1.0': {}
+
+ '@vueuse/shared@14.1.0(vue@3.5.26)':
+ dependencies:
+ vue: 3.5.26
+
+ ai@5.0.118(zod@4.3.5):
+ dependencies:
+ '@ai-sdk/gateway': 2.0.24(zod@4.3.5)
+ '@ai-sdk/provider': 2.0.1
+ '@ai-sdk/provider-utils': 3.0.20(zod@4.3.5)
+ '@opentelemetry/api': 1.9.0
+ zod: 4.3.5
+
+ algoliasearch@5.46.2:
+ dependencies:
+ '@algolia/abtesting': 1.12.2
+ '@algolia/client-abtesting': 5.46.2
+ '@algolia/client-analytics': 5.46.2
+ '@algolia/client-common': 5.46.2
+ '@algolia/client-insights': 5.46.2
+ '@algolia/client-personalization': 5.46.2
+ '@algolia/client-query-suggestions': 5.46.2
+ '@algolia/client-search': 5.46.2
+ '@algolia/ingestion': 1.46.2
+ '@algolia/monitoring': 1.46.2
+ '@algolia/recommend': 5.46.2
+ '@algolia/requester-browser-xhr': 5.46.2
+ '@algolia/requester-fetch': 5.46.2
+ '@algolia/requester-node-http': 5.46.2
+
+ birpc@2.9.0: {}
+
+ ccount@2.0.1: {}
+
+ character-entities-html4@2.1.0: {}
+
+ character-entities-legacy@3.0.0: {}
+
+ comma-separated-tokens@2.0.3: {}
+
+ copy-anything@4.0.5:
+ dependencies:
+ is-what: 5.5.0
+
+ csstype@3.2.3: {}
+
+ dequal@2.0.3: {}
+
+ devlop@1.1.0:
+ dependencies:
+ dequal: 2.0.3
+
+ entities@7.0.0: {}
+
+ esbuild@0.27.2:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.27.2
+ '@esbuild/android-arm': 0.27.2
+ '@esbuild/android-arm64': 0.27.2
+ '@esbuild/android-x64': 0.27.2
+ '@esbuild/darwin-arm64': 0.27.2
+ '@esbuild/darwin-x64': 0.27.2
+ '@esbuild/freebsd-arm64': 0.27.2
+ '@esbuild/freebsd-x64': 0.27.2
+ '@esbuild/linux-arm': 0.27.2
+ '@esbuild/linux-arm64': 0.27.2
+ '@esbuild/linux-ia32': 0.27.2
+ '@esbuild/linux-loong64': 0.27.2
+ '@esbuild/linux-mips64el': 0.27.2
+ '@esbuild/linux-ppc64': 0.27.2
+ '@esbuild/linux-riscv64': 0.27.2
+ '@esbuild/linux-s390x': 0.27.2
+ '@esbuild/linux-x64': 0.27.2
+ '@esbuild/netbsd-arm64': 0.27.2
+ '@esbuild/netbsd-x64': 0.27.2
+ '@esbuild/openbsd-arm64': 0.27.2
+ '@esbuild/openbsd-x64': 0.27.2
+ '@esbuild/openharmony-arm64': 0.27.2
+ '@esbuild/sunos-x64': 0.27.2
+ '@esbuild/win32-arm64': 0.27.2
+ '@esbuild/win32-ia32': 0.27.2
+ '@esbuild/win32-x64': 0.27.2
+
+ estree-walker@2.0.2: {}
+
+ eventsource-parser@3.0.6: {}
+
+ fdir@6.5.0(picomatch@4.0.3):
+ optionalDependencies:
+ picomatch: 4.0.3
+
+ focus-trap@7.7.1:
+ dependencies:
+ tabbable: 6.4.0
+
+ fsevents@2.3.3:
+ optional: true
+
+ hast-util-to-html@9.0.5:
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/unist': 3.0.3
+ ccount: 2.0.1
+ comma-separated-tokens: 2.0.3
+ hast-util-whitespace: 3.0.0
+ html-void-elements: 3.0.0
+ mdast-util-to-hast: 13.2.1
+ property-information: 7.1.0
+ space-separated-tokens: 2.0.2
+ stringify-entities: 4.0.4
+ zwitch: 2.0.4
+
+ hast-util-whitespace@3.0.0:
+ dependencies:
+ '@types/hast': 3.0.4
+
+ hookable@5.5.3: {}
+
+ htm@3.1.1: {}
+
+ html-void-elements@3.0.0: {}
+
+ is-what@5.5.0: {}
+
+ json-schema@0.4.0: {}
+
+ magic-string@0.30.21:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ mark.js@8.11.1: {}
+
+ marked@16.4.2: {}
+
+ mdast-util-to-hast@13.2.1:
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/mdast': 4.0.4
+ '@ungap/structured-clone': 1.3.0
+ devlop: 1.1.0
+ micromark-util-sanitize-uri: 2.0.1
+ trim-lines: 3.0.1
+ unist-util-position: 5.0.0
+ unist-util-visit: 5.0.0
+ vfile: 6.0.3
+
+ micromark-util-character@2.1.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-encode@2.0.1: {}
+
+ micromark-util-sanitize-uri@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-encode: 2.0.1
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-symbol@2.0.1: {}
+
+ micromark-util-types@2.0.2: {}
+
+ minisearch@7.2.0: {}
+
+ mitt@3.0.1: {}
+
+ nanoid@3.3.11: {}
+
+ oniguruma-parser@0.12.1: {}
+
+ oniguruma-to-es@4.3.4:
+ dependencies:
+ oniguruma-parser: 0.12.1
+ regex: 6.1.0
+ regex-recursion: 6.0.2
+
+ perfect-debounce@2.0.0: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@4.0.3: {}
+
+ postcss@8.5.6:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ property-information@7.1.0: {}
+
+ react@19.2.3: {}
+
+ regex-recursion@6.0.2:
+ dependencies:
+ regex-utilities: 2.3.0
+
+ regex-utilities@2.3.0: {}
+
+ regex@6.1.0:
+ dependencies:
+ regex-utilities: 2.3.0
+
+ rfdc@1.4.1: {}
+
+ rollup@4.55.1:
+ dependencies:
+ '@types/estree': 1.0.8
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.55.1
+ '@rollup/rollup-android-arm64': 4.55.1
+ '@rollup/rollup-darwin-arm64': 4.55.1
+ '@rollup/rollup-darwin-x64': 4.55.1
+ '@rollup/rollup-freebsd-arm64': 4.55.1
+ '@rollup/rollup-freebsd-x64': 4.55.1
+ '@rollup/rollup-linux-arm-gnueabihf': 4.55.1
+ '@rollup/rollup-linux-arm-musleabihf': 4.55.1
+ '@rollup/rollup-linux-arm64-gnu': 4.55.1
+ '@rollup/rollup-linux-arm64-musl': 4.55.1
+ '@rollup/rollup-linux-loong64-gnu': 4.55.1
+ '@rollup/rollup-linux-loong64-musl': 4.55.1
+ '@rollup/rollup-linux-ppc64-gnu': 4.55.1
+ '@rollup/rollup-linux-ppc64-musl': 4.55.1
+ '@rollup/rollup-linux-riscv64-gnu': 4.55.1
+ '@rollup/rollup-linux-riscv64-musl': 4.55.1
+ '@rollup/rollup-linux-s390x-gnu': 4.55.1
+ '@rollup/rollup-linux-x64-gnu': 4.55.1
+ '@rollup/rollup-linux-x64-musl': 4.55.1
+ '@rollup/rollup-openbsd-x64': 4.55.1
+ '@rollup/rollup-openharmony-arm64': 4.55.1
+ '@rollup/rollup-win32-arm64-msvc': 4.55.1
+ '@rollup/rollup-win32-ia32-msvc': 4.55.1
+ '@rollup/rollup-win32-x64-gnu': 4.55.1
+ '@rollup/rollup-win32-x64-msvc': 4.55.1
+ fsevents: 2.3.3
+
+ search-insights@2.17.3: {}
+
+ shiki@3.21.0:
+ dependencies:
+ '@shikijs/core': 3.21.0
+ '@shikijs/engine-javascript': 3.21.0
+ '@shikijs/engine-oniguruma': 3.21.0
+ '@shikijs/langs': 3.21.0
+ '@shikijs/themes': 3.21.0
+ '@shikijs/types': 3.21.0
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+
+ source-map-js@1.2.1: {}
+
+ space-separated-tokens@2.0.2: {}
+
+ speakingurl@14.0.1: {}
+
+ stringify-entities@4.0.4:
+ dependencies:
+ character-entities-html4: 2.1.0
+ character-entities-legacy: 3.0.0
+
+ superjson@2.2.6:
+ dependencies:
+ copy-anything: 4.0.5
+
+ swr@2.3.8(react@19.2.3):
+ dependencies:
+ dequal: 2.0.3
+ react: 19.2.3
+ use-sync-external-store: 1.6.0(react@19.2.3)
+
+ tabbable@6.4.0: {}
+
+ throttleit@2.1.0: {}
+
+ tinyglobby@0.2.15:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+
+ trim-lines@3.0.1: {}
+
+ unist-util-is@6.0.1:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-position@5.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-stringify-position@4.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+
+ unist-util-visit-parents@6.0.2:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.1
+
+ unist-util-visit@5.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.1
+ unist-util-visit-parents: 6.0.2
+
+ use-sync-external-store@1.6.0(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+
+ vfile-message@4.0.3:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-stringify-position: 4.0.0
+
+ vfile@6.0.3:
+ dependencies:
+ '@types/unist': 3.0.3
+ vfile-message: 4.0.3
+
+ vite@7.3.1:
+ dependencies:
+ esbuild: 0.27.2
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+ postcss: 8.5.6
+ rollup: 4.55.1
+ tinyglobby: 0.2.15
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ vitepress@2.0.0-alpha.15(@algolia/client-search@5.46.2)(postcss@8.5.6)(react@19.2.3)(search-insights@2.17.3):
+ dependencies:
+ '@docsearch/css': 4.4.0
+ '@docsearch/js': 4.4.0(@algolia/client-search@5.46.2)(react@19.2.3)(search-insights@2.17.3)
+ '@iconify-json/simple-icons': 1.2.65
+ '@shikijs/core': 3.21.0
+ '@shikijs/transformers': 3.21.0
+ '@shikijs/types': 3.21.0
+ '@types/markdown-it': 14.1.2
+ '@vitejs/plugin-vue': 6.0.3(vite@7.3.1)(vue@3.5.26)
+ '@vue/devtools-api': 8.0.5
+ '@vue/shared': 3.5.26
+ '@vueuse/core': 14.1.0(vue@3.5.26)
+ '@vueuse/integrations': 14.1.0(focus-trap@7.7.1)(vue@3.5.26)
+ focus-trap: 7.7.1
+ mark.js: 8.11.1
+ minisearch: 7.2.0
+ shiki: 3.21.0
+ vite: 7.3.1
+ vue: 3.5.26
+ optionalDependencies:
+ postcss: 8.5.6
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+ - '@types/node'
+ - '@types/react'
+ - async-validator
+ - axios
+ - change-case
+ - drauu
+ - fuse.js
+ - idb-keyval
+ - jiti
+ - jwt-decode
+ - less
+ - lightningcss
+ - nprogress
+ - qrcode
+ - react
+ - react-dom
+ - sass
+ - sass-embedded
+ - search-insights
+ - sortablejs
+ - stylus
+ - sugarss
+ - terser
+ - tsx
+ - typescript
+ - universal-cookie
+ - yaml
+
+ vue@3.5.26:
+ dependencies:
+ '@vue/compiler-dom': 3.5.26
+ '@vue/compiler-sfc': 3.5.26
+ '@vue/runtime-dom': 3.5.26
+ '@vue/server-renderer': 3.5.26(vue@3.5.26)
+ '@vue/shared': 3.5.26
+
+ zod@4.3.5: {}
+
+ zwitch@2.0.4: {}
diff --git a/doc-ng/public/admin_dashboard.png b/doc-ng/public/admin_dashboard.png
new file mode 100644
index 000000000..484026c59
Binary files /dev/null and b/doc-ng/public/admin_dashboard.png differ
diff --git a/doc-ng/public/api_access_form.png b/doc-ng/public/api_access_form.png
new file mode 100644
index 000000000..9fe51d206
Binary files /dev/null and b/doc-ng/public/api_access_form.png differ
diff --git a/doc-ng/public/modoboa_logo.png b/doc-ng/public/modoboa_logo.png
new file mode 100644
index 000000000..f15d39c79
Binary files /dev/null and b/doc-ng/public/modoboa_logo.png differ
diff --git a/doc-ng/public/user_webmail.png b/doc-ng/public/user_webmail.png
new file mode 100644
index 000000000..f670759f2
Binary files /dev/null and b/doc-ng/public/user_webmail.png differ
diff --git a/doc-ng/rebuild_venv.md b/doc-ng/rebuild_venv.md
new file mode 100644
index 000000000..71b10397d
--- /dev/null
+++ b/doc-ng/rebuild_venv.md
@@ -0,0 +1,79 @@
+---
+title: How to rebuild your virtual environment
+description: Procedure to repair your broken environment
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, virtualenv, broken, ubuntu'
+---
+
+# Rebuild Virtualenv
+
+Sometimes when upgrading your Operating System (eg from Ubuntu 17.04 to
+Ubuntu 17.10) your virtual environment running modoboa can get corrupted.
+
+Your first response will be to panic but fear not!
+The solution is in this document.
+
+First things first:
+
+## Recover your database password
+
+You will need to recover your database password (if using mysql or
+postgresql).
+
+You will find this in `/etc/postfix/sql-aliases.cf` or any
+file with `sql-*.cf` in the `/etc/postfix` directory.
+
+Make note of this as you will need it when reconfiguring modoboa.
+
+## Reinstall Modoboa
+
+Start out by backup up your modoboa settings file located in the `modoboa instance` directory
+(`/srv/modoboa/instance/instance/settings.py` if you used the default installer configuration).
+This contains your current configuration.
+
+Next, you want to remove all current modoboa files.
+
+After doing this, follow the manual installation instructions for `modoboa_manual_install` **only** as everything should be working properly.
+
+After this completes, simply restore your backed up settings file to `/srv/instance/instance/settings.py`
+(if you used installer default configuration).
+You will then need to reinstall your [extensions](http://modoboa.readthedocs.io/en/latest/index.html).
+
+You can find which plugins you had in your `settings.py` file under the `MODOBOA_APPS` variable.
+
+Instructions to install extensions can also be [found here](http://modoboa.readthedocs.io/en/latest/installation.html#extensions).
+
+Once you have completed this step, you will need to run the following commands:
+
+``` bash
+$ (env) $ cd
+$ (env) $ python manage.py migrate
+$ (env) $ python manage.py collectstatic
+```
+
+You will then see a message similar to:
+
+``` txt
+You have requested to collect static files at the destination
+location as specified in your settings:
+
+ /srv/modoboa/instance/sitestatic
+
+This will overwrite existing files!
+Are you sure you want to do this?
+
+Type 'yes' to continue, or 'no' to cancel:
+```
+
+You will want to answer `yes` here then simply restart
+the `uwsgi` process with `service uwsgi restart`
+and you should be up and running again.
+
+Simply log into your modoboa web panel and verify that
+your extensions and webmail box is working.
+
+## Information
+
+[Rebuild instructions from](https://help.pythonanywhere.com/pages/RebuildingVirtualenvs/)
diff --git a/doc-ng/rest_api.md b/doc-ng/rest_api.md
new file mode 100644
index 000000000..9314ccbdc
--- /dev/null
+++ b/doc-ng/rest_api.md
@@ -0,0 +1,44 @@
+---
+title: REST API to control your Modoboa instance.
+description: Guide to describe the use of the REST API to manage your modoboa instance
+head:
+ - - meta
+ - name: 'keywords'
+ content: 'modoboa, rest api, swagger, authorization, token'
+---
+
+# REST API
+
+To ease the integration with external sources (software or other), Modoboa provides a REST API.
+
+Every installed instance comes with a ready-to-use API and a documentation.
+
+**You will find them using the following url patterns:**
+
+- New API+Docs: *https://\/api/schema-v2/swagger/*
+- Old API: *http://\/api/v1/*
+
+::: warning
+You need to be logged in for the API docs to show.
+:::
+
+**Examples:**
+
+* [Demo Instance](https://demo.modoboa.org/api/schema-v2/swagger/ "Demo Instance Swagger API")
+
+documentation is available on the official demo.
+
+Using this API requires an authentication and for now, only a token
+based authentication is supported. To get a valid token, log-in to your
+instance with a super administrator, go to `Settings > API` and
+activate the API access. Press the Update button and wait until the page
+is reloaded, the token will be displayed.
+
+{.align-center}
+
+To make valid API calls, every requests you send must embed this token
+within an Authorization HTTP header like this:
+
+ Authorization: Token
+
+and the content type of those requests must be `application/json`.
\ No newline at end of file