Skip to content

Administrator guide

Mailclient is Wolf-Cloud's lightweight PHP webmail app. It runs as a Dockerized PHP 8.3 and Apache service backed by Postgres 16. Users sign in with their real Wolf-Cloud mailbox credentials; Mailclient does not keep a separate local password database.

The source code is hosted at:

https://git.wolf-cloud.com/liamwolf/mailclient.git

What the stack provides

  • Browser-based webmail for the configured IMAP and SMTP servers
  • Automatic local user provisioning after the first successful mailbox login
  • Folders, search, unread and starred filters, message reading, attachments, compose and send, and common message actions
  • User settings for identity, signature, default folders, pagination, notifications, undo send, and active sessions
  • Nextcloud CardDAV and CalDAV sync for contacts, recipient autocomplete, and editable calendar views
  • Optional authenticator 2FA, passkeys, recovery codes, security emails, and administrator-managed local user controls
  • An optional GreenMail profile for local end-to-end testing without real mail infrastructure

Repository layout

  • public/index.php is the main authenticated mail router.
  • public/login.php handles login, TOTP challenges, bypass requests, and logout.
  • public/includes/auth/ contains session, CSRF, login, lockout, provisioning, and user-repository logic.
  • public/includes/core/ contains app config, URL helpers, proxy handling, HTML helpers, and asset helpers.
  • public/includes/mail/ contains mailbox connection, folder handling, list/read/action flows, MIME helpers, and compose/send logic.
  • public/includes/security/ contains HTML sanitization and the signed remote-image proxy.
  • public/includes/render/ contains login, TOTP, passkey, and error rendering helpers.
  • public/templates/ contains the mail, settings, admin, compose, and shell templates.
  • public/assets/css/ and public/assets/js/ contain the app styling and browser behavior.
  • docker-compose.yml starts the app, Postgres, and the optional GreenMail test service.
  • .env.example is the configuration source of truth for local and production-style runs.

Requirements

  • Docker
  • Docker Compose

Deploy locally or on a server

  1. Clone the repository:

bash git clone https://git.wolf-cloud.com/liamwolf/mailclient.git cd mailclient

  1. Copy the example environment file:

bash cp .env.example .env

  1. Fill in at least:

  2. APP_SECRET_KEY

  3. DB_PASSWORD
  4. ADMIN_USERNAMES
  5. IMAP_* and SMTP_* for the real mail provider, unless using the GreenMail test profile

  6. Start the stack:

bash docker compose up -d --build

  1. Open /login on the configured host. For the default local Compose binding, use http://localhost:8098/login.

On first run, the application creates its database schema automatically. Admin access is not seeded separately; any mailbox user listed in ADMIN_USERNAMES becomes an admin after a successful mailbox login.

Important environment variables

Many of the settings below (branding, security mail, session/size limits, and DAV client tuning) can also be changed at runtime from the Admin panel without editing .env or restarting the container. A value saved there takes priority over its env var for this instance; the env var remains the fallback default until something is saved from the panel.

App and session

  • APP_TITLE: UI title and branding text
  • BRAND_FOOTER_TEXT, BRAND_LOGO_URL, BRAND_LOGO_ALT, BRAND_FAVICON_URL, BRAND_FONTS_CSS_URL: optional branding overrides
  • DOCS_LOGO_URL: optional logo URL for the bundled documentation site
  • APP_SECRET_KEY: base64-encoded 32-byte key used for encrypted session mailbox passwords and encrypted TOTP secrets
  • APP_SESSION_IDLE_TIMEOUT: idle timeout in seconds before re-authentication is required
  • APP_SESSION_ABSOLUTE_TIMEOUT: maximum total session lifetime in seconds
  • APP_SESSION_REMEMBER_TIMEOUT: maximum total session lifetime and cookie lifetime when Remember me is checked
  • APP_MAX_MESSAGE_BYTES: maximum message size the web UI will open or parse
  • APP_MAX_REMOTE_IMAGE_BYTES: maximum remote-image size allowed through the image proxy
  • APP_TRUSTED_PROXIES: comma-separated proxy IPs or CIDRs allowed to supply X-Forwarded-* headers
  • TZ: optional container/display timezone; also localizes the contact phone-number placeholder when mapped
  • ADMIN_USERNAMES: comma-separated mailbox usernames or email addresses that receive administrator access after login

Generate APP_SECRET_KEY with:

php -r "echo base64_encode(sodium_crypto_secretbox_keygen()), PHP_EOL;"

Database

  • DB_HOST
  • DB_PORT
  • DB_NAME
  • DB_USER
  • DB_PASSWORD

In the bundled Compose stack, DB_HOST points at the postgres service.

Mail transport

These endpoints are shared by all users:

  • IMAP_HOST
  • IMAP_PORT
  • IMAP_ENCRYPTION
  • SMTP_HOST
  • SMTP_PORT
  • SMTP_ENCRYPTION

Login credentials are checked directly against the configured IMAP server. Outbound mail is sent through the configured SMTP server using the signed-in user's mailbox credentials.

Account-security mail

These settings are used only for sign-in alerts and emailed 2FA bypass codes:

  • SECURITY_SMTP_HOST
  • SECURITY_SMTP_PORT
  • SECURITY_SMTP_ENCRYPTION
  • SECURITY_SMTP_USERNAME
  • SECURITY_SMTP_PASSWORD
  • SECURITY_SMTP_FROM_EMAIL
  • SECURITY_SMTP_FROM_NAME
  • TOTP_BYPASS_MAX_REQUESTS_PER_15MIN

Use a dedicated low-privilege mailbox or service account for security mail. That keeps account notifications and bypass codes separate from any user's personal mailbox password.

Calendar and contacts sync

These settings apply to the Nextcloud CardDAV/CalDAV integration:

  • DAV_TIMEOUT_SECONDS: timeout for DAV HTTP requests
  • DAV_MAX_RESPONSE_BYTES: maximum DAV response size accepted during discovery and sync
  • DAV_ALLOW_INSECURE_HTTP: set to 1, true, yes, or on only for a trusted LAN/dev Nextcloud server without HTTPS

Auth and user model

  • A valid IMAP login is the only way to create an app account.
  • Mailclient auto-creates a local user record on first successful login.
  • Local user records store preferences, 2FA state, recovery codes, and session-related metadata.
  • Mailbox passwords are never written to the database. After successful login, they are encrypted with APP_SECRET_KEY and kept only in the server-side session.
  • If TOTP is enabled, login continues through a second-factor challenge after the IMAP password check succeeds.
  • When another device is already signed in, the login flow can request an emailed bypass code or wait for in-session approval.
  • Administrators can manage local users from the Admin panel's Admins & Users tab, including promoting or demoting admins, disabling accounts, and resetting local auth-related state.

Security operations

  • Keep .env out of version control.
  • Treat APP_SECRET_KEY as sensitive production secret material.
  • Rotating APP_SECRET_KEY invalidates encrypted session mailbox passwords and encrypted TOTP secrets. Expect active sessions and stored 2FA state to need re-establishment after rotation.
  • Configure APP_TRUSTED_PROXIES when Mailclient is behind a trusted reverse proxy that supplies X-Forwarded-* headers.
  • Login forms are protected by CSRF validation and username-based lockout windows.
  • Sessions enforce idle and absolute lifetimes and are tied to the browser user-agent hash.
  • Oversized messages are refused before being fully parsed into PHP memory.
  • Remote images remain opt-in and are fetched through a signed same-origin proxy that requires an active session and rejects localhost or private-network targets.
  • Containers run with no-new-privileges:true.

Local test profile

For end-to-end testing without real mailbox infrastructure, use the bundled GreenMail profile:

docker compose --profile test up -d --build

Use the test values documented in .env.example:

  • IMAP_HOST=testmail
  • IMAP_PORT=3143
  • IMAP_ENCRYPTION=plain
  • SMTP_HOST=testmail
  • SMTP_PORT=3025
  • SMTP_ENCRYPTION=plain
  • SECURITY_SMTP_HOST=testmail
  • SECURITY_SMTP_PORT=3025
  • SECURITY_SMTP_ENCRYPTION=plain
  • SECURITY_SMTP_USERNAME=security@localhost
  • SECURITY_SMTP_PASSWORD=testpass
  • SECURITY_SMTP_FROM_EMAIL=security@localhost

GreenMail test users:

  • testuser / testpass (GreenMail's IMAP/SMTP-AUTH login identity is the bare username, not the full address; mail sent from the app is still addressed to testuser@localhost)
  • security@localhost / testpass

Development and verification

There is no separate asset pipeline or frontend build step. The image copies public/ directly into the Apache document root during build, and Apache mod_rewrite serves clean routes such as /login, /mail, /settings, and /admin.

Run the lightweight repo-local regression suite with:

php tests/run.php

Documentation build

The documentation site uses MkDocs Material with mike for versioning. The docs service in docker-compose.yml builds the docs-deps stage of the root Dockerfile and runs a live-reload mkdocs serve against it.

Serve the docs from the repository root:

docker compose --profile docs up docs

The live docs server is available at http://localhost:8000 while the docs service is running.