Skip to content

Directory

Defines an external directory for account authentication and lookups.

This object can be configured from the WebUI under Settings › Authentication › Directories

Directory is a multi-variant object: each instance has an @type discriminator selecting one of the variants below, and each variant carries its own set of fields.

LDAP Directory

Type: String · required

Short description of this directory

Type: Uri · default: "ldap://localhost:389"

URL of the LDAP server

Type: Set<Uri> · enterprise

URLs of backup LDAP servers, tried in order when the primary server is unreachable

Type: Duration · default: 30000

Timeout for establishing a connection to the LDAP server and for each LDAP operation

Type: Boolean · default: false

Allow invalid TLS certificates when connecting to the server

Type: Boolean · default: false

Use TLS to connect to the remote server

Type: String · required

The base distinguished name (DN) from where searches should begin

Type: String?

The distinguished name of the account that the server will bind as to connect to the LDAP directory

Type: SecretKeyOptional · default: {"@type":"None"}

The password or secret for the bind DN account

Type: Boolean · default: true

Whether to use bind authentication. When enabled, the server will use the filterLogin to search for the user account and then attempt to bind as that account using the provided password. When disabled, the server will use the bind DN and secret to connect to the LDAP server and obtain the secret from the account entry using the attrSecret attribute.

Type: String · default: "(&(objectClass=inetOrgPerson)(mail=?))"

Searches for user accounts by e-mail address during authentication

Type: String · default: "(|(&(objectClass=inetOrgPerson)(|(mail=?)(mailAlias=?)))(&(objectClass=groupOfNames)(|(mail=?)(mailAlias=?))))"

Searches for users or groups matching a recipient e-mail address or alias

Type: String? · default: "(&(objectClass=groupOfNames)(member=?))"

Searches for groups that an account is member of. Use when the group membership is not provided in the account entry. The ? in the filter will be replaced with the account DN.

Type: Set<String> · default: {"objectClass":true}

LDAP attribute for the user’s account type, if missing defaults to individual.

Type: Set<String> · default: {"description":true}

LDAP attributes used to store the user’s description

Type: Set<String> · default: {"mail":true}

LDAP attribute for the user’s primary email address

Type: Set<String> · default: {"mailAlias":true}

LDAP attribute for the user’s email alias(es)

Type: Set<String> · default: {"memberOf":true}

LDAP attributes for the groups that a user belongs to. Used when filterMemberOf is not configured or when the group membership is also provided in the account entry.

Type: Set<String> · default: {"userPassword":true}

LDAP attribute for the user’s password hash. This setting is required when binding as a service user. When using bind authentication, configure the secret-changed attribute instead.

Type: Set<String> · default: {"pwdChangeTime":true}

LDAP attribute that provides a password change hash or a timestamp indicating when the password was last changed. When using bind authentication, this attribute is used to determine when to invalidate OAuth tokens.

Type: String · default: "groupOfNames"

LDAP object class used to identify group entries

Type: UnsignedInt · default: 10 · max: 8192

Maximum number of connections that can be maintained simultaneously in the connection pool

Type: Duration · default: 30000

Maximum amount of time that the connection pool will wait for a new connection to be created

Type: Duration · default: 30000

Maximum amount of time that the connection pool manager will wait for a connection to be recycled

Type: Duration · default: 30000

Maximum amount of time that the connection pool will wait for a connection to become available

Type: Id<Tenant>? · enterprise

Identifier for the tenant this directory belongs to

SQL Database

Type: String · required

Short description of this directory

Type: SqlAuthStore · default: {"@type":"Default"}

Storage backend where accounts and groups are stored

Type: String · default: "name"

Column holding the account’s primary e-mail address. When the column is missing from the result or does not hold an address, the login name is used as the address at sign-in, and recipient lookups find no aliases or groups.

Type: String · default: "secret"

Column name for the account password.

Type: String? · default: "type"

Column name for account type

Type: String? · default: "description"

Column name for account full name or description

Type: String · default: "SELECT name, secret, description, type FROM accounts WHERE name = $1 AND active = true"

Query to obtain the account details by login e-mail address.

Type: String · default: "SELECT name, secret, description, type FROM accounts WHERE name = $1 AND active = true"

Query to obtain the account details by recipient e-mail address or alias.

Type: String? · default: "SELECT member_of FROM group_members WHERE name = $1"

Query to obtain the groups an account is member of.

Type: String? · default: "SELECT address FROM emails WHERE name = $1"

Query to obtain the e-mail aliases of an account.

Type: Id<Tenant>? · enterprise

Identifier for the tenant this directory belongs to

OpenID Connect

Type: String · required

Short description of this directory

Type: Uri · required

The issuer URL of the OpenID Connect provider (e.g. https://sso.example.com/realms/myrealm). It is compared exactly with the issuer reported by the provider, so enter it exactly as the provider reports it, including any trailing slash. Endpoints are discovered from /.well-known/oauth-authorization-server first and then from /.well-known/openid-configuration. For Microsoft Entra ID, use the tenant-specific issuer.

Type: Boolean · default: false

Allow invalid TLS certificates when connecting to the OpenID Connect provider

Type: String · default: "preferred_username"

The claim name used to retrieve the user’s login name from the token or user info response. Common values are preferred_username, email, or sub depending on the provider’s configuration. If the claim value is not an email address and usernameDomain is set, the domain will be appended automatically (e.g. john becomes [email protected]). If the claim value already contains an @, it is used as-is. If the claim value is not an email address and no usernameDomain is configured, Stalwart will fall back to the email claim. If neither yields a valid email address, authentication will be rejected.

Type: String?

The domain name to append to the username when the value of claimUsername does not contain an @ symbol (e.g. setting this to example.com will turn john into [email protected]). If not set, Stalwart will fall back to the email claim when the username claim does not contain a valid email address.

Type: String?

The claim name used to retrieve the user’s display name from the token or user info response. Common values are name or display_name. If not set, the display name will not be populated. When it is set and the access token does not carry the claim, every login calls the provider’s user info endpoint to fetch it, so leave it unset unless the provider includes it in access tokens or the extra request is acceptable.

Type: String?

The claim name used to retrieve the user’s group memberships from the token or user info response. Common values are groups or roles depending on the provider’s configuration. If not set, group information will not be populated. Note that some providers omit group claims from the token to keep its size small and only return them via the user info endpoint. When it is set and the access token does not carry the claim, every login calls the user info endpoint to fetch it.

Type: Id<Tenant>? · enterprise

Identifier for the tenant this directory belongs to

Type: Set<String>

Additional values accepted in the token’s aud claim besides this server’s resource identifiers. Use it for the audience the identity provider puts in access tokens, for example the client id of the web interface, an API identifier, or a project id.

Type: OidcOpaqueTokenValidation · default: "none"

How access tokens that are not JWTs are validated. Disabled rejects them. User info calls the provider’s UserInfo endpoint, which cannot check the token’s audience or scopes, so any token the provider issued to any application is accepted, with the access set by the tokens without scopes option. Introspection calls the provider’s RFC 7662 introspection endpoint, checks audience and scopes, and requires client credentials. Only one method is used per token.

Type: String?

Client id used to authenticate to the introspection endpoint

Type: SecretKeyOptional · default: {"@type":"None"}

Client secret used to authenticate to the introspection endpoint

Type: OidcIntrospectionAuth · default: "clientSecretBasic"

How the server authenticates to the introspection endpoint

Type: OidcUnscopedTokenPolicy · default: "reject"

How access tokens that carry none of the interoperable mail, contacts, calendars or files scopes are handled. Tokens issued to this server’s web interface always have full access. Reject follows the OAuth profile for open public clients. Interoperable services grants access to mail, contacts, calendars and files, which is enough for mail clients when the provider cannot issue these scopes. Full access also grants management permissions to any such token and should only be used together with a strict audience check.

Type: Boolean · default: false

Accept access tokens whose aud claim is missing or matches none of this server’s resource identifiers or accepted audiences. Tokens issued to any application at the provider will then be accepted.

Type: Boolean · default: false

Accept JWT access tokens that carry ID token claims such as nonce or at_hash. Needed for providers whose access tokens are copies of the ID token. When enabled, an ID token can be presented as an access token.

Type: Boolean · default: false

Ignore a trailing slash difference between the configured issuer URL and the issuer reported by the provider and found in tokens

The Directory object is available via the urn:stalwart:jmap capability.

This is a standard Foo/get method as defined in RFC 8620, Section 5.1.

This method requires the sysDirectoryGet permission.

Terminal window
curl -X POST https://mail.example.com/jmap/ \
-H 'Authorization: Bearer $TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"methodCalls": [
[
"x:Directory/get",
{
"ids": [
"id1"
]
},
"c1"
]
],
"using": [
"urn:ietf:params:jmap:core",
"urn:stalwart:jmap"
]
}'

This is a standard Foo/set method as defined in RFC 8620, Section 5.3.

Supports the following operations in a single call: create, update, destroy.

This operation requires the sysDirectoryCreate permission.

Terminal window
curl -X POST https://mail.example.com/jmap/ \
-H 'Authorization: Bearer $TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"methodCalls": [
[
"x:Directory/set",
{
"create": {
"new1": {
"@type": "Ldap",
"baseDn": "Example",
"bindSecret": {
"@type": "None"
},
"description": "Example",
"urlFailover": {}
}
}
},
"c1"
]
],
"using": [
"urn:ietf:params:jmap:core",
"urn:stalwart:jmap"
]
}'

This operation requires the sysDirectoryUpdate permission.

Terminal window
curl -X POST https://mail.example.com/jmap/ \
-H 'Authorization: Bearer $TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"methodCalls": [
[
"x:Directory/set",
{
"update": {
"id1": {
"description": "updated value"
}
}
},
"c1"
]
],
"using": [
"urn:ietf:params:jmap:core",
"urn:stalwart:jmap"
]
}'

This operation requires the sysDirectoryDestroy permission.

Terminal window
curl -X POST https://mail.example.com/jmap/ \
-H 'Authorization: Bearer $TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"methodCalls": [
[
"x:Directory/set",
{
"destroy": [
"id1"
]
},
"c1"
]
],
"using": [
"urn:ietf:params:jmap:core",
"urn:stalwart:jmap"
]
}'

This is a standard Foo/query method as defined in RFC 8620, Section 5.5.

This method requires the sysDirectoryQuery permission.

Terminal window
curl -X POST https://mail.example.com/jmap/ \
-H 'Authorization: Bearer $TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"methodCalls": [
[
"x:Directory/query",
{
"filter": {
"memberTenantId": "id1"
}
},
"c1"
]
],
"using": [
"urn:ietf:params:jmap:core",
"urn:stalwart:jmap"
]
}'

The x:Directory/query filter argument accepts the following conditions (combinable with AnyOf / AllOf / Not per RFC 8620):

ConditionKind
memberTenantIdid of Tenant

stalwart-cli wraps the same JMAP calls. See the CLI reference for installation, authentication, and general usage.

Terminal window
stalwart-cli get Directory id1
Terminal window
stalwart-cli create Directory/Ldap \
--field description=Example \
--field 'urlFailover={}' \
--field baseDn=Example \
--field 'bindSecret={"@type":"None"}'
Terminal window
stalwart-cli query Directory
stalwart-cli query Directory --where memberTenantId=id1
Terminal window
stalwart-cli update Directory id1 --field description='updated value'
Terminal window
stalwart-cli delete Directory --ids id1

An optional secret value, or none.

  • None: No secret. No additional fields.
  • Value: Secret value. Carries the fields of SecretKeyValue.
  • EnvironmentVariable: Secret read from environment variable. Carries the fields of SecretKeyEnvironmentVariable.
  • File: Secret read from file. Carries the fields of SecretKeyFile.

A secret value provided directly.

Type: String · required · secret

Password or secret value

A secret value read from an environment variable.

Type: String · required

Environment variable name to read the secret from

A secret value read from a file.

Type: String · required

File path to read the secret from

Defines the SQL database used to store account and group information for SQL directories.

  • Default: Use data store (SQL only). No additional fields.
  • PostgreSql: PostgreSQL. Carries the fields of PostgreSqlStore.
  • MySql: mySQL. Carries the fields of MySqlStore.
  • Sqlite: SQLite. Carries the fields of SqliteStore.

PostgreSQL data store.

Type: Duration? · default: 15000

Maximum time to wait for a connection to the database server to be established

Type: Boolean · default: false

Use TLS to connect to the store

Type: Boolean · default: false

Allow invalid TLS certificates when connecting to the store

Type: UnsignedInt? · max: 8192 · min: 1

Maximum number of connections to the store. When unset, 4 per CPU core, between 16 and 32.

Type: PostgreSqlRecyclingMethod · default: "fast"

Method to use when recycling connections in the pool

Type: List<PostgreSqlSettings> · enterprise

List of read replicas for the store

Type: String · required

Hostname of the database server

Type: UnsignedInt · default: 5432 · max: 65535 · min: 1

Port of the database server

Type: String · default: "stalwart"

Name of the database

Type: String? · default: "stalwart"

Username to connect to the store

Type: SecretKeyOptional · default: {"@type":"None"}

Password to connect to the store

Type: String?

Additional connection options

PostgreSQL connection settings.

Type: String · required

Hostname of the database server

Type: UnsignedInt · default: 5432 · max: 65535 · min: 1

Port of the database server

Type: String · default: "stalwart"

Name of the database

Type: String? · default: "stalwart"

Username to connect to the store

Type: SecretKeyOptional · default: {"@type":"None"}

Password to connect to the store

Type: String?

Additional connection options

MySQL data store.

Type: Duration? · default: 15000

Time an idle connection may stay open before the server closes it (sets the MySQL wait_timeout session variable)

Type: Boolean · default: false

Use TLS to connect to the store

Type: Boolean · default: false

Allow invalid TLS certificates when connecting to the store

Type: UnsignedInt? · max: 1073741824 · min: 1024

Maximum size of a packet in bytes

Type: UnsignedInt? · max: 8192 · min: 1

Maximum number of connections to the store. When unset, 4 per CPU core, between 16 and 32.

Type: UnsignedInt? · default: 5 · max: 8192 · min: 1

Minimum number of connections to the store

Type: List<MySqlSettings> · enterprise

List of read replicas for the store

Type: String · required

Hostname of the database server

Type: UnsignedInt · default: 3306 · max: 65535 · min: 1

Port of the database server

Type: String · default: "stalwart"

Name of the database

Type: String? · default: "stalwart"

Username to connect to the store

Type: SecretKeyOptional · default: {"@type":"None"}

Password to connect to the store

MySQL connection settings.

Type: String · required

Hostname of the database server

Type: UnsignedInt · default: 3306 · max: 65535 · min: 1

Port of the database server

Type: String · default: "stalwart"

Name of the database

Type: String? · default: "stalwart"

Username to connect to the store

Type: SecretKeyOptional · default: {"@type":"None"}

Password to connect to the store

SQLite embedded data store.

Type: String · required

Path to the SQLite data directory

Type: UnsignedInt? · max: 64 · min: 1

Number of worker threads to use for the store, defaults to the number of cores

Type: UnsignedInt? · max: 8192 · min: 1

Maximum number of connections to the store. When unset, the number of worker threads, between 4 and 16.

ValueLabel
fastFast recycling method
verifiedVerified recycling method
cleanClean recycling method
ValueLabel
noneDisabled
userInfoUserInfo endpoint (no audience or scope checks)
introspectionToken introspection (RFC 7662)
ValueLabel
clientSecretBasicHTTP Basic (client_secret_basic)
clientSecretPostForm parameters (client_secret_post)
noneNo authentication
ValueLabel
rejectReject
interoperableInteroperable services only
fullFull access (unsafe)