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.phpis the main authenticated mail router.public/login.phphandles 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/andpublic/assets/js/contain the app styling and browser behavior.docker-compose.ymlstarts the app, Postgres, and the optional GreenMail test service..env.exampleis the configuration source of truth for local and production-style runs.
Requirements
- Docker
- Docker Compose
Deploy locally or on a server
- Clone the repository:
bash git clone https://git.wolf-cloud.com/liamwolf/mailclient.git cd mailclient
- Copy the example environment file:
bash cp .env.example .env
-
Fill in at least:
-
APP_SECRET_KEY DB_PASSWORDADMIN_USERNAMES-
IMAP_*andSMTP_*for the real mail provider, unless using the GreenMail test profile -
Start the stack:
bash docker compose up -d --build
- Open
/loginon the configured host. For the default local Compose binding, usehttp://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 textBRAND_FOOTER_TEXT,BRAND_LOGO_URL,BRAND_LOGO_ALT,BRAND_FAVICON_URL,BRAND_FONTS_CSS_URL: optional branding overridesDOCS_LOGO_URL: optional logo URL for the bundled documentation siteAPP_SECRET_KEY: base64-encoded 32-byte key used for encrypted session mailbox passwords and encrypted TOTP secretsAPP_SESSION_IDLE_TIMEOUT: idle timeout in seconds before re-authentication is requiredAPP_SESSION_ABSOLUTE_TIMEOUT: maximum total session lifetime in secondsAPP_SESSION_REMEMBER_TIMEOUT: maximum total session lifetime and cookie lifetime when Remember me is checkedAPP_MAX_MESSAGE_BYTES: maximum message size the web UI will open or parseAPP_MAX_REMOTE_IMAGE_BYTES: maximum remote-image size allowed through the image proxyAPP_TRUSTED_PROXIES: comma-separated proxy IPs or CIDRs allowed to supplyX-Forwarded-*headersTZ: optional container/display timezone; also localizes the contact phone-number placeholder when mappedADMIN_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_HOSTDB_PORTDB_NAMEDB_USERDB_PASSWORD
In the bundled Compose stack, DB_HOST points at the postgres service.
Mail transport
These endpoints are shared by all users:
IMAP_HOSTIMAP_PORTIMAP_ENCRYPTIONSMTP_HOSTSMTP_PORTSMTP_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_HOSTSECURITY_SMTP_PORTSECURITY_SMTP_ENCRYPTIONSECURITY_SMTP_USERNAMESECURITY_SMTP_PASSWORDSECURITY_SMTP_FROM_EMAILSECURITY_SMTP_FROM_NAMETOTP_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 requestsDAV_MAX_RESPONSE_BYTES: maximum DAV response size accepted during discovery and syncDAV_ALLOW_INSECURE_HTTP: set to1,true,yes, orononly 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_KEYand 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
.envout of version control. - Treat
APP_SECRET_KEYas sensitive production secret material. - Rotating
APP_SECRET_KEYinvalidates encrypted session mailbox passwords and encrypted TOTP secrets. Expect active sessions and stored 2FA state to need re-establishment after rotation. - Configure
APP_TRUSTED_PROXIESwhen Mailclient is behind a trusted reverse proxy that suppliesX-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=testmailIMAP_PORT=3143IMAP_ENCRYPTION=plainSMTP_HOST=testmailSMTP_PORT=3025SMTP_ENCRYPTION=plainSECURITY_SMTP_HOST=testmailSECURITY_SMTP_PORT=3025SECURITY_SMTP_ENCRYPTION=plainSECURITY_SMTP_USERNAME=security@localhostSECURITY_SMTP_PASSWORD=testpassSECURITY_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 totestuser@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.