Skip to content

Config Plugins

Dejan Strbac edited this page Nov 12, 2026 · 1 revision

This page is the reference for the [plugin.<name>] sections of config.toml. It covers which plugins load, the server URL each plugin takes, the options each one reads, and the password plugin's two ways of changing a password: an HTTP endpoint or a command. For what each plugin does, see Plugins. The full key list is in section 9 of CONFIGURATION.md.

The keys of a plugin section

ALPS has six plugins: base, caldav, carddav, gpg, managesieve and password. Each one has a section named after it, written in lower case exactly as listed. Every section takes the same three keys:

Key Type Read by
enabled Boolean Every plugin but base, to decide which plugins load (see Which plugins load). enabled = false keeps a plugin out, enabled = true puts it on an enabled list, and a section that leaves enabled out keeps the plugin's defaults. password loads only with enabled = true.
server String caldav, carddav and managesieve. The other plugins ignore it.
options Table caldav (week_start) and password. The other plugins ignore it.

Any other key in a plugin section, such as a misspelt enable = true, is ignored, and ALPS logs a warning for it at startup: Unknown configuration key: plugin.caldav.enable (ignored). The keys inside options are not checked.

options can be written inline or as its own table. These two are the same:

[plugin.caldav]
options = { week_start = 0 }
[plugin.caldav.options]
week_start = 0

ALPS reads these sections once, when it starts. It has no configuration reload, so restart ALPS after you change one. SIGHUP only reopens the log file.

Which plugins load

ALPS decides at startup, in two steps.

  1. enabled decides which plugins may load.

    • base serves sign-in and all mail, and always loads, whatever its section says.
    • A section with enabled = false keeps its plugin out, even when it sets a server.
    • While no section sets enabled = true, every other plugin may load, whether its section leaves enabled out or it has no section at all.
    • As soon as one section sets enabled = true, those sections form an enabled list. Only base and the plugins whose own sections set enabled = true may load then. A plugin without a section, or whose section leaves enabled out, stays out too.
    • password may load only with enabled = true in its own section. So whenever it loads, the enabled list is on.

    ALPS does not set up a plugin that this step keeps out. It does not read the plugin's server or options, makes no DNS lookup or startup check for it, and logs nothing about it. A server value that ALPS would refuse therefore does not stop startup while its plugin is off.

  2. Each plugin that may load is set up, when it has what it needs.

    • gpg always.
    • caldav and carddav when they have a section, with or without a server, or when a [provider.multi] backend names their endpoint. Without either, they stay out.
    • managesieve only when its section sets server, or a [provider.multi] backend names a managesieve endpoint. A section with neither keeps it out, and startup logs ManageSieve enabled without a server or routed endpoint; plugin disabled.
    • password only when something can change a password: an endpoint or command in [plugin.password.options], or a [provider.multi] backend whose password block sets one. Without any of them, startup logs password: enabled without an endpoint, command or routed backend; plugin disabled.

    A server that ALPS cannot use stops startup in this step (see Server URLs).

Each plugin that loads is logged as Loaded plugin "<name>". A name on the enabled list that did not load, because no plugin has that name or because the plugin lacked what it needs, is logged at warn level as Enabled plugin "<name>" did not load; check its name and backend configuration.

A section that leaves enabled out keeps its plugin's defaults, so [plugin.caldav] with only a server loads CalDAV as long as no section sets enabled = true. Once one does, give every plugin you want its own section with enabled = true, as config.example.toml does.

[plugin.base]
enabled = true

[plugin.caldav]
enabled = true
server = "https://dav.example.com/"

[plugin.carddav]
enabled = true
server = "https://dav.example.com/"

[plugin.managesieve]
enabled = true
server = "managesieve://mail.example.com:4190"

[plugin.gpg]
enabled = true

[plugin.password]
enabled = false

To switch a plugin off in this layout, set its enabled = false, or remove its section or its enabled line. The gpg plugin finds recipients' keys through carddav, so keep both if you use encryption.

What loads with a few other configurations:

Configuration Plugins that load Result
No plugin sections at all, and no multi backend that names a DAV or ManageSieve endpoint base, gpg caldav, carddav and managesieve have no section and no server, so they stay out.
No section sets enabled = true. [plugin.carddav] sets only a server base, gpg, carddav A section that leaves enabled out keeps the plugin's defaults.
No section sets enabled = true. [plugin.gpg] and [plugin.carddav] set enabled = false, and carddav has a server base enabled = false switches each of them off. ALPS does not read the CardDAV server, so even a value it would refuse does not stop startup.
No section sets enabled = true. [plugin.password] enabled = false base, gpg Settings has no Password page.
[plugin.password] sets enabled = true and an endpoint, and no other section does base, password The enabled list leaves gpg out.
[plugin.password] sets enabled = true with no endpoint or command, and no backend has a password block base Startup logs password: enabled without an endpoint, command or routed backend; plugin disabled, and warns Enabled plugin "password" did not load; ....
[plugin.caldav], [plugin.carddav], [plugin.managesieve] and [plugin.gpg] set enabled = true, with no server anywhere, as in config.example.toml base, gpg, caldav, carddav ManageSieve stays off, and startup logs its two lines. Calendar and Contacts have only their sections in Settings, where each user can set up a server of their own (see A plugin loaded without a server).
[plugin.Base] enabled = true and [plugin.sieve] enabled = true base Base is not base, and sieve is not a plugin, but the two sections turn on the enabled list, so gpg is left out. Startup warns Enabled plugin "Base" did not load; ..., and the same for sieve.

A section for a name that no plugin has is not reported as an unknown key. When it sets enabled = true, it turns on the enabled list, and ALPS warns that the name did not load. Otherwise ALPS ignores it without a word. After every configuration change, check the startup log for a Loaded plugin line for each plugin you expect, and for warnings.

Checking what loaded

ALPS logs one line for each plugin that loads. These are the messages of those lines (with the default JSON log format, the msg field), for a configuration whose only plugin section is [plugin.password] with enabled = true and an endpoint:

Loaded plugin "password"
Loaded plugin "base"

A plugin that is switched off, or that lacks what it needs, gets no line of its own, apart from the managesieve and password lines above and the warning for a name on the enabled list. When their section sets a server, caldav, carddav and managesieve also log the server they use: Configured CalDAV server: ..., Configured CardDAV server: ... and Configured ManageSieve server: ....

A signed-in browser sees the loaded plugins in the EnabledPlugins field of GET /session, and in ActivePlugins those of them the account can use: all of them, except caldav and carddav for an account that has no server for them. The web app shows the Settings sections of the plugins in EnabledPlugins, and the tabs and pages of those in ActivePlugins. Switching a plugin off therefore needs only the configuration change and a restart.

Server URLs

caldav, carddav and managesieve each take one server. It is the default server for every user, unless a per-domain endpoint under the multi provider replaces it (see Per-domain endpoints). For CalDAV and CardDAV, users can also enter a server of their own in Settings (see Connections in Settings).

Plugin TLS No TLS Without a scheme
caldav https://, caldavs:// http://, http+insecure://, caldav+insecure:// A bare host name, with no port or path, is looked up in DNS at startup (see below). Any other value without one of these schemes stops startup.
carddav https://, carddavs:// http://, http+insecure://, carddav+insecure:// Stops startup, a bare host name included.
managesieve managesieve://, managesieves://, sieve://, or a bare host name. All of them use STARTTLS. managesieve+insecure://, sieve+insecure:// A bare host name works. A host and port without a scheme stops startup.

Write full URLs, with the port wherever it is not the default: https://dav.example.com:8443/, managesieve://mail.example.com:4190. Write an IPv6 address in brackets: managesieve://[2001:db8::1]:4190. Each plugin takes only its own schemes, so caldavs:// in [plugin.carddav] stops startup with unknown scheme for CardDAV server. These checks run only for a plugin that enabled lets through (see Which plugins load).

CalDAV and CardDAV

  • Sign-in. ALPS sends every CalDAV and CardDAV request with HTTP Basic authentication. By default it uses the login and password the user signed in to ALPS with, so the DAV server must accept that login. Users can enter a separate username and password in Settings (see Connections in Settings).
  • TLS. Use https://, caldavs:// or carddavs://. With the plain-HTTP forms the password crosses the network unencrypted. ALPS checks certificates against the system's trust store, and has no option to skip that check, so a DAV server with a private CA needs that CA in the host's trust store. On Linux and the BSDs, Go reads the trusted CA certificates from SSL_CERT_FILE and SSL_CERT_DIR when they are set (environment variables). HTTPS_PROXY, HTTP_PROXY and NO_PROXY apply to the requests to the servers set here and in multi backends.
  • CalDAV discovery. A bare domain such as example.com makes ALPS look up the SRV record _caldavs._tcp.example.com once, at startup, and connect over HTTPS to the server it names. The path comes from the TXT record at the same name, or is /.well-known/caldav. If the lookup fails, for example because there is no SRV record, ALPS logs caldav: failed to discover CalDAV server: ... and loads the plugin without a default server. Users whose multi backend names a caldav endpoint keep their calendars, and the others have Calendar and Tasks only once they set up a server of their own in Settings. A discovered URL that ALPS cannot use stops startup (caldav: Discover returned an invalid URL: ...). CardDAV has no discovery.
  • A value without a scheme. A host and port such as dav.example.com:8443 is read as the unknown scheme dav.example.com and stops startup, in both plugins. So does a host name with a path, such as dav.example.com/caldav, and a URL with a slash missing, such as https:/dav.example.com. Write the full URL.
  • Startup check. Each DAV plugin sends one unauthenticated OPTIONS request to its server, with a 10-second timeout. A 2xx, 401, 404 or 405 answer counts as reachable. Anything else, or no answer, is logged as caldav: failed to connect to CalDAV server "...": ... (continuing anyway) (carddav likewise), and ALPS starts anyway. An unreachable server can delay startup by up to 10 seconds for each plugin. Neither the discovery nor this check runs for a plugin that enabled keeps out, or for the servers users enter in Settings.

A refused CardDAV value stops ALPS with:

failed to load plugins: CardDAV server requires a scheme (https://, http+insecure://), got: //carddav.example.com

The DAV server itself needs a few things, such as one address book provisioned for each account. They are listed in CDAV.md and on Mail-Server-Requirements.

Connections in Settings

While caldav or carddav is loaded, Settings > Calendar and Settings > Contacts let each mail account set up its own connection: the default server with a separate username and password, or a personal server of the user's own. ALPS checks a connection before it saves it, and keeps it with the account's other records on the mail server (Storage-in-IMAP-METADATA). Two server keys bear on it:

  • [server] login_key. Separate passwords are stored encrypted with it. Without it, Settings offers only the mail username and password.
  • [server] dav_allow_private_servers (default false). Personal servers are reached over HTTPS only, and only at public addresses. Set it to true when your users keep DAV servers on a trusted intranet. It never restricts the servers set in this file.

Both keys are on Config-Server. What users see, and the exact rules for personal servers, are in CDAV.md.

ManageSieve

  • Connection. ALPS opens a plain TCP connection to port 4190, unless the URL names another port, and upgrades it with STARTTLS before it signs in. managesieves:// is not implicit TLS: it behaves like managesieve://.
  • Certificates. With managesieve://, managesieves://, sieve:// or a bare host, the certificate is verified against the host name in the URL. If the server does not offer STARTTLS, ALPS does not sign in.
  • The +insecure schemes. These sign in without TLS when the server offers no STARTTLS, and skip certificate verification when it does. Use them only for a server on the same host or a trusted local network.
  • Sign-in. ALPS uses SASL PLAIN with the user's ALPS login and password. ManageSieve has no DNS discovery.

An unknown scheme stops ALPS with a message like:

failed to load plugins: unknown scheme for ManageSieve server: "https" (use managesieves://, managesieve://, or managesieve+insecure://)

The value mail.example.com:4190 gives the same error, with "mail.example.com" as the scheme. Write managesieve://mail.example.com:4190. For the Sieve extensions the filters and the auto-reply need, see FILTERS.md.

A plugin loaded without a server

A caldav or carddav plugin can load with no server, for example with enabled = true and the server line commented out, as in config.example.toml. A user whose multi backend names no endpoint then has no default server. That user sees only the plugin's section in Settings, where they can set up a server of their own. Calendar and Tasks, or Contacts, appear once the account has a server. Until then, the plugin's requests for that account are answered with 409, without a Request error line in the log:

Request Answer
Calendar (GET /calendar/calendars) 409 {"error":"caldav_not_configured"}
Contacts (GET /contacts) 409 {"error":"carddav_not_configured"}

managesieve loads without a global server only under multi, when some backend names a managesieve endpoint. Its Filters page then shows for every user, and fails for the users whose backend names none:

Request Answer Log
Filters (GET /managesieve/script) 500 {"error":"sieve_unavailable"} managesieve: failed to connect to the sieve server: ManageSieve server is not configured

Give ManageSieve a server, or a per-domain endpoint for each user's backend, or switch it off. Give CalDAV and CardDAV a server unless every user is to set up their own.

Per-domain endpoints

With [provider] type = "multi", each [provider.multi.domains."<domain>"], [[provider.multi.routes]] and [provider.multi.default] table can name its own caldav, carddav and managesieve endpoints:

[[provider.multi.routes]]
domains = ["example.com", "example.org"]
type = "imap"
server = "imaps://imap.example.com:993"
caldav = "https://dav.example.com/"
carddav = "https://dav.example.com/"
managesieve = "managesieve://imap.example.com:4190"
  • A backend that names no endpoint for a service uses the global server of that plugin, never another backend's.
  • A per-domain caldav or carddav endpoint must carry one of its plugin's schemes and a host: a bare host name is not discovered here. A per-domain managesieve endpoint takes the same forms as the global server, a bare host name included.
  • ALPS checks every per-domain endpoint at startup, with the same rules it uses when the endpoint is used. A value it cannot use stops startup, with the backend named, for example Error generating options: multi: invalid service for route [0]: caldav: CalDAV server requires a scheme (https://, http+insecure://), got: //dav.example.com. The check reads only the value: per-domain endpoints are neither discovered nor contacted at startup.
  • A ManageSieve endpoint may skip TLS only if its own scheme says +insecure, whatever the global server says.
  • Under multi, a backend that names an endpoint is enough to set up its plugin without a global server. The plugin then loads whenever enabled lets it through.

The routing rules are described on Config-Providers-and-SMTP and Setup-Many-Domains.

Debug output

With [server] debug = true, the DAV plugins log every request and response, bodies included, through [logging] as protocol trace lines at debug level, with protocol set to caldav or carddav and the exchange in data. The Authorization, Proxy-Authorization, Cookie and Set-Cookie headers are redacted. ManageSieve logs its exchange the same way (protocol managesieve), with the sign-in written as [REDACTED] and every script ALPS saves written in full. The lines appear only while [logging] level is debug. They hold users' events, contacts and filters, so keep debug off in production (Config-Logging-and-Cache).

CalDAV options

[plugin.caldav] reads one option:

Option Default Meaning
week_start 1 The first day of the week for users who have not picked one in Settings > Localization > First Day of the Week. 0, "0", "sunday" or "sun" mean Sunday. 1, "1", "monday" or "mon" mean Monday. Strings match in any case. Any other value means Monday.
first_day_of_week The same option under another name, read only when week_start is absent.

The values below were each set in turn, and the default ALPS gave a user with no choice of their own was read back from GET /settings:

Setting First day
none 1 (Monday)
week_start = "Sunday" 0 (Sunday)
week_start = 0 0
first_day_of_week = "sun" 0
week_start = 1, first_day_of_week = 0 1
week_start = 6 1

carddav, managesieve, gpg and base read no options.

Password plugin

The password plugin adds Settings > Password. ALPS checks the user's current password itself. It then passes the change to an HTTP endpoint, or runs a program on the ALPS host. For a walkthrough, see Setup-Password-Change. The full behavior is in PASSWORD_CHANGE.md.

Switching it on

[plugin.password]
enabled = true

[plugin.password.options]
endpoint = "https://admin.example.com/api/mailboxes/{email}/password"
  • [plugin.password] enabled = true loads the plugin. Without it, Settings has no Password page and there is no /password/change route.
  • The plugin also needs something that can change a password: an endpoint or a command in [plugin.password.options], or a [provider.multi] backend whose password block sets one (see Per-domain password blocks). Without any of them it stays off, and startup logs password: enabled without an endpoint, command or routed backend; plugin disabled.
  • If no other section sets enabled = true, this one turns on the enabled list (see Which plugins load). Every other plugin you use, such as gpg, caldav or carddav, then needs its own enabled = true, or ALPS starts without it. base always loads.
  • Set endpoint or command in [plugin.password.options], not both.
  • The HTTP request, redirects included, and the command each have 15 seconds.
  • With enabled = true, ALPS checks the command options at startup (see Checks at startup). It does not check the HTTP options there. An endpoint that is not a valid URL, or a method that is not a valid HTTP method, starts, and every change then fails with 500.

Placeholders

endpoint, the arguments of command, and stdin take these placeholders:

Placeholder Value
{email} The login exactly as the user signed in with it. A user who signs in with a bare user name, for instance under [provider.multi] default_domain, gives that bare name.
{local} The part of the login before the first @, or the whole login when it has none.
{domain} The part of the login after the first @, or empty.
{old_password}, {new_password} The passwords. Allowed only in stdin.

In endpoint, each value is escaped as a URL path segment. /, ?, ;, , and spaces are escaped, and @, +, & and = are left as they are. Put the placeholders in the path, not in the query string.

HTTP endpoint

Option Default Meaning
endpoint none The URL ALPS sends the change to. Use https://, since the body can carry the passwords.
method "POST" The HTTP method. ALPS sends it in upper case.
auth_type "none" "basic" sends HTTP Basic authentication with username and password. "bearer" sends Authorization: Bearer <token>. Matched in any case. Any other value sends no Authorization header.
username, password none The credentials for "basic". These are your admin API's credentials, not the user's.
token none The token for "bearer".
payload "json" "form", in any case, sends application/x-www-form-urlencoded. Any other value sends application/json.
payload_mapping none The fields the body carries (see below).

The username, password and token values are secrets, so keep the configuration file readable only by the user ALPS runs as.

ALPS goes by the final answer. It follows up to 10 redirects. After a 301, 302 or 303 the request is repeated as a GET with no body. After a 307 or 308 it is repeated with the same method and body. Any final status below 400 counts as a changed password, so point endpoint at the URL that makes the change, not at one that redirects. A final status of 400 or above is answered as follows:

Endpoint answers User gets Settings shows
401, 403 or 5xx 502 password_service_unavailable "Password changes are temporarily unavailable. Please try again later."
Any other 4xx 400 password_change_rejected "Could not change your password. Please try again."
Cannot be reached, a connection or TLS error, more than 10 redirects, or no answer within 15 seconds 502 password_service_unavailable "Password changes are temporarily unavailable. Please try again later."

The endpoint's own status and body go to the ALPS log, as Password change failed, status <code>: <body>. A 401 or 403 means the endpoint did not accept ALPS's username, password or token, which is why it is reported as an unavailable service rather than as a refused change.

Payload mapping

[plugin.password.options.payload_mapping] names the fields of the request body. Each entry is <field sent> = "<value>", where the value is one of:

Value What is sent
username The login, the same as {email}
local The same as {local}
domain The same as {domain}
old_password The current password the user typed
new_password The new password

Only mapped fields are sent, sorted by name. An entry with any other value is dropped without a message, so a mapping cannot send a fixed value. ALPS does not check the mapping at startup. Without a mapping the body is {} for JSON, or empty for a form, with the same Content-Type. The mapping is used only with endpoint.

This block was run against a capture server:

[plugin.password.options]
endpoint = "https://admin.example.com/users/{local}/{domain}/{email}/password"
method = "put"
auth_type = "basic"
username = "admin"
password = "admin-api-secret"
payload = "json"

[plugin.password.options.payload_mapping]
email = "username"
password = "old_password"
new_password = "new_password"
dom = "domain"
loc = "local"
constant = "fixed-value"

For ada@example.test, the endpoint received this request. constant was dropped:

PUT /users/ada/example.test/ada@example.test/password
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4tYXBpLXNlY3JldA==

{"dom":"example.test","email":"ada@example.test","loc":"ada","new_password":"new-secret-1","password":"old-secret"}

With endpoint = "https://admin.example.com/api/mailbox/{email}", payload = "FORM", auth_type = "Bearer", token = "tok-123", no method, and a mapping of email = "username" and new_password = "new_password", the endpoint received:

POST /api/mailbox/ada@example.test
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer tok-123

email=ada%40example.test&new_password=new-secret-2

Command

For a mail server that changes passwords with a command line tool, set command in place of endpoint:

[plugin.password.options]
# maddy reads the new password from standard input when -p is left out.
command = ["/usr/local/bin/maddy", "creds", "password", "{email}"]
# stdin = "{new_password}\n"
Option Default Meaning
command none A list: the program, as an absolute path, then each of its arguments. The arguments may carry {email}, {local} and {domain}.
stdin "{new_password}\n" What the program reads on standard input. It may carry every placeholder, the passwords included. stdin = "" means the default, not empty input. Allowed only with command.

How ALPS runs it:

  • No shell. ALPS runs the program directly, so each list entry reaches it as exactly one argument, whatever the login contains. Quotes, $VAR and | are passed on as written. Wrap the tool in a script if you need a shell.
  • Passwords on standard input only. Other users on the host can read the arguments of a running program, so ALPS refuses the password placeholders there.
  • Argument checks. If an argument starts with a placeholder and would begin with a dash once filled in, the change is refused with 400 password_change_rejected and the program is not run. A fixed prefix such as --user={email} is fine. A value that stdin uses and that holds a carriage return, a line feed or a NUL is refused the same way.
  • Exit status. 0 means the password was changed. 1 or above is a refused change (400 password_change_rejected). A program that cannot be started, or is ended by a signal, gives 502 password_service_unavailable. A program still running after 15 seconds is killed with its whole process group, and the answer is 504 password_service_unavailable.
  • Logging. When a run fails, ALPS logs the first 4 KiB of the program's standard error, with both passwords masked. Standard output is discarded.
  • Environment. The program runs as the user ALPS runs as, in ALPS's working directory, with PATH as its only environment variable, in a process group of its own. The work must stay in that group, as a user ALPS can signal, so that a timeout or a cancelled request can stop it. If the tool needs another user's rights, call it through a helper that manages and stops its own children, or use the HTTP backend.
  • Unix only. Commands need a Unix host. On any other host, a global command stops startup when [plugin.password] enabled = true, and the HTTP backend is the one to use.

A test helper configured as command = ["/lab/bin/setpw.sh", "--user={email}", "{local}", "{domain}"], with stdin = "{old_password}\n{new_password}\n", received the arguments --user=ada@example.test, ada and example.test, and an environment holding only PATH (plus the PWD its shell sets). When it wrote the password to standard error and exited with status 1, the ALPS log read:

Password change command failed for ada@example.test: /lab/bin/setpw.sh: exit status 1 (stderr "refusing on purpose: [password]")

For the login -eve@example.test, the argument {local} would have started with a dash. The change was refused and the program was not run:

Password change command failed for -eve@example.test: {local} would open an argument with a dash

Checks at startup

With [plugin.password] enabled = true, ALPS refuses to start when the global [plugin.password.options] block cannot run. Each of these was tried, and stopped ALPS with the message shown after failed to load plugins: plugin.password: :

Block Message
Both endpoint and command set either endpoint or command, not both
command = "/lab/bin/setpw.sh {email}", a string command must be a list: the program, then each of its arguments
A relative program, bin/setpw.sh the program "bin/setpw.sh" must be an absolute path
A placeholder in the program, /lab/bin/{domain}.sh the program path cannot carry {domain}
{new_password} in an argument command arguments cannot carry {new_password}: passwords go on stdin
A program that does not exist the program: stat /lab/bin/missing: no such file or directory
A file with no execute bit the program "/lab/bin/noexec.sh" is not an executable file
stdin without command stdin is used only with command
stdin = 5 with command stdin must be a string

With enabled = false, the same blocks do not stop startup, since the plugin does not load and the blocks are not read. With enabled = true, ALPS runs these checks again on every change, so a program removed after startup is refused then, with 500 password_service_unavailable and the log message Password change is misconfigured: <reason>. A program that has an execute bit but that the ALPS user still cannot run passes the checks, and fails when it is run (502).

Per-domain password blocks

Under the multi provider, a backend can carry its own password block, for example when each group of domains has its own admin API:

  • [provider.multi.domains."<domain>".password]
  • [provider.multi.routes.password], placed directly under its own [[provider.multi.routes]]. In TOML this sub-table belongs to the route declared directly above it.
  • [provider.multi.default.password], for every login that no domain entry or route names.
[[provider.multi.routes]]
domains = ["example.com", "example.org"]
type = "imap"
server = "imaps://imap.example.com:993"

[provider.multi.routes.password]
endpoint = "https://admin.example.com/api/mailboxes/{email}/password"
auth_type = "bearer"
token = "..."

[provider.multi.routes.password.payload_mapping]
new_password = "new_password"

A backend with no password block uses [plugin.password.options]. For logins on a backend that has one, the block replaces [plugin.password.options] whole. Nothing is merged in from the global block, payload_mapping and the credentials included. In a test with a global block that had Basic credentials and a mapping, a route whose block set only endpoint sent {} with no Authorization header, while a login on another domain still used the global block. Give every routed block its own credentials and its own payload_mapping (or a command).

[plugin.password] enabled = true is still required. A routed block that sets endpoint or command is enough to load the plugin, with no global endpoint or command. Logins on a backend without a block of their own then get the global block, which can change nothing, and every change they try fails with 502 password_service_unavailable. A routed block may also set command and stdin. ALPS does not check routed blocks at startup. A routed block that fails the checks when it is used is answered 500 password_service_unavailable, with the log message Password change is misconfigured: <reason>.

After a change

When the backend reports success, ALPS signs in to the mail server again with the new password, and the user stays signed in. If some of the user's linked accounts could not be updated with the new password, Settings shows "Your password has been changed. Some linked accounts could not be updated." If the sign-in with the new password fails, the answer is still 200, with code reauthenticate, and Settings shows "Your password has been changed. Please sign in again." ALPS does not sign the user out, and the session keeps the old password. This happened in testing with an endpoint that answered 204 without changing the password. A reauthenticate answer after a reported success usually means the backend did not change the password that the mail server checks.

The other answers, the rate limits on the current-password check, and the POST /password/change request format are described in PASSWORD_CHANGE.md.

Related pages

Clone this wiki locally