-
Notifications
You must be signed in to change notification settings - Fork 3
Security Hardening
This page began as a chapter of the 6.2.10 manual and has been corrected against the code of 6.2.28. The Control Panel's pages are grouped differently now, so the paths below use today's groups; the TLS ports 465, 993 and 995 exist only after you create them on the TCP/IP ports page (a fresh install seeds 25, 587, 110 and 143); and everything added since 6.2.10 is in Changes-Since-6210. Where a value here disagrees with the Settings Reference, which is generated from the code, the reference is right.
-
Never become an open relay. hMailServer's shipped defaults are safe; the danger is a careless IP range. Check Access & abuse protection → IP ranges and make sure no external range is allowed to relay without authentication.

-
Auto-ban blocks an IP after repeated failed logins. On by default — leave it on. Tune it under Access & abuse protection → Auto-ban.

Auto-ban was the third tab of the SSL/TLS page in that build. In 6.2.28 it is a page of its own — Access & abuse protection → Auto-ban — with the four settings tabulated in §17.5; the protocol-version half stayed behind on TLS & certificates → SSL/TLS.
-
Require TLS. Disable plaintext logins on ports reachable from the internet — that is §9.5 of Encryption and Certificates, plus the "Require TLS for auth" range option below.
-
Strong passwords everywhere. Use the generator in the account dialog, and consider the password policy in §17.2.
Two decisions are made for every RCPT TO, in this order, and they are separate: may this
address relay at all, and must it authenticate first
(Server/SMTP/SMTPConnection.cpp).
flowchart TD
A["RCPT TO arrives"] --> B["Classify the sender:<br/>local if its domain is a hosted<br/>active domain, or a route says so"]
B --> C["Classify the recipient:<br/>local or remote"]
C --> D{"Does the matching IP range<br/>allow this combination?"}
D -- no --> R550["550 Delivery is not allowed to this address."]
D -- yes --> E{"Does the range require SMTP<br/>authentication for this combination?"}
E -- "yes, and not authenticated" --> R530["530 SMTP authentication is required."]
E -- "no, or authenticated" --> OK["Recipient accepted, 250 2.1.5"]
The four combinations are configured independently, twice — once as "may they" and once as "must they authenticate". The two shipped ranges set them like this:
Internet (priority 10, 0.0.0.0–255.255.255.255) |
My computer (priority 30, 127.0.0.1) |
|
|---|---|---|
| Allow SMTP / POP3 / IMAP | yes / yes / yes | yes / yes / yes |
| Deliver local → local | allowed, auth required | allowed, no auth |
| Deliver local → remote | allowed, auth required | allowed, no auth |
| Deliver remote → local | allowed, no auth | allowed, no auth |
| Deliver remote → remote (relay) | allowed, auth required | allowed, auth required |
| Enable anti-spam | on | on |
| Enable anti-virus | on | on |
| Require TLS for auth | off | off |
(Decoded from the rangeoptions bitmask in the hm_securityranges seed rows: 96203 for
Internet, 71627 for My computer. The bit values are in
Server/Common/BO/SecurityRange.h.)
Read that table as the open-relay defence: remote → remote requires authentication on every shipped range, and it is the only combination that requires it even from localhost. "Remote → local" is ordinary inbound mail and correctly needs no authentication.
The highest priority matching range wins. Auto-ban creates its bans at priority 100, above both shipped ranges, which is what makes a ban effective.
Two range options change behaviour elsewhere on this wiki. Clearing Enable anti-spam for a range turns off every spam test, greylisting and the recipient tarpit for it (Stopping Spam §10.1). Clearing Enable anti-virus means messages from it are never flagged for scanning (Stopping Viruses §11.1).
"Require TLS for auth" is a range option, not a port option. With it set,
AUTHis not advertised at all on a cleartext connection from that range — on SMTP, POP3, IMAP and ManageSieve alike. That matters more than it sounds: before this was fixed,EHLOofferedAUTH LOGIN PLAIN SCRAM-SHA-256and theAUTHthat followed was answered530, by which time the client had already put the base64 password on the wire.
| Mechanism | SMTP | IMAP | POP3 | Advertised when |
|---|---|---|---|---|
LOGIN |
yes | via LOGIN command |
USER/PASS
|
Always, unless AUTH is refused on this connection |
PLAIN |
yes | AUTH=PLAIN |
SASL PLAIN |
Only while the protocol's plain-text setting is on |
SCRAM-SHA-256 |
yes | AUTH=SCRAM-SHA-256 |
SASL … SCRAM-SHA-256 |
Whenever AUTH is available — independent of the plain-text setting, because the password never crosses the wire |
SCRAM-SHA-256-PLUS |
yes | AUTH=SCRAM-SHA-256-PLUS |
yes |
TLS connections only — it binds the exchange to the server certificate (RFC 5802 + RFC 5929 tls-server-end-point) |
XOAUTH2, OAUTHBEARER
|
yes | yes | yes |
OAuth2Enabled=1, and over TLS unless OAuth2RequireTLS=0
|
EXTERNAL |
yes | yes | yes | Exactly when this connection presented a client certificate that verified against the port's CA bundle and names an address (§9.4 of Encryption and Certificates) |
IMAP additionally advertises LOGINDISABLED when it will not accept LOGIN on this
connection, so a client knows before it sends the password in the clear (RFC 2595 §3.1).
DisableAUTHList (a comma-separated list of local TCP ports) suppresses AUTH entirely on
the ports you name — for example a port 25 that only takes inbound mail from other servers.
Configure this under Access & abuse protection → Authentication, card Password storage.
| Value | Scheme | Suitable for a new password? |
|---|---|---|
| 0 | none — stored as typed | no |
| 1 | Blowfish — reversible | no |
| 2 | MD5 | no |
| 3 | SHA-256 | acceptable |
| 4 | PBKDF2-HMAC-SHA256 | the default |
| 5 | Argon2id | recommended, memory-hard |
| 6 | DPAPI — machine-bound, for stored secrets, not passwords | no |
| 7 | scrypt (RFC 7914) | recommended, memory-hard |
[Settings]
PreferredHashAlgorithm=5 ; 3, 4, 5 or 7. Anything else is refused and read as 4
MinimumAcceptedHashAlgorithm=4 ; refuse a logon whose stored hash is weaker. 0 = off
PasswordPepper= ; a server-wide secret mixed into Argon2id hashes
PasswordHashIterations=0 ; PBKDF2. 0 = 210,000. Range 10,000 to 10,000,000
PasswordHashMemoryKB=0 ; Argon2id. 0 = 19,456 KiB. Range 4,096 to 1,048,576
PasswordHashTimeCost=0 ; Argon2id passes. 0 = 2. Range 1 to 20-
A
PreferredHashAlgorithmoutside {3, 4, 5, 7} is not silently accepted. It is reported as HM5528 and read as 4, because two of the call sites hand the value straight to the crypto layer as the scheme to store a new secret under — a 0 or a 1 there would write account passwords in a form anybody with the database can read back. -
Existing weaker hashes upgrade transparently on the next successful logon, and so
does a hash of the right scheme derived with a work factor you have since raised. The
upgrade is one-way: lowering a work factor later leaves stored hashes alone and applies
only to new ones, so backing out of an expensive setting does not rewrite every password
to something weaker. If the upgraded hash cannot be saved, the logon still succeeds on
the existing hash and the attempt is retried next time
(
Common/Util/PasswordValidator.cpp). -
MinimumAcceptedHashAlgorithmranks by strength, and Argon2id and scrypt are peers — setting it to 5 accepts either. -
PasswordPeppermust be set before accounts exist. Changing it later invalidates every existing password. It only affects Argon2id hashes, so PBKDF2 stays usable as the SCRAM salted password.
The defaults for the two work factors are the OWASP recommended minimums: 210,000 PBKDF2 iterations, and 19,456 KiB with 2 passes for Argon2id. Both are bounded at each end because both ends are a failure — below the floor is a hash an attacker brute-forces at leisure, above the ceiling is a logon the server cannot afford, since every verification runs on a connection thread.
All off until set, all in [Settings]:
PasswordPolicyMinimumLength=12
PasswordPolicyRequireMixedCase=1
PasswordPolicyRequireDigit=1
PasswordPolicyRequireNonAlphanumeric=1
PasswordPolicyRejectCommon=1 ; refuse passwords from a built-in common list
PasswordPolicyHistoryCount=5 ; refuse the last N passwords
PasswordPolicyMaximumAgeDays=0 ; 0 = passwords never expireBy default the service runs as LocalSystem, which is more privilege than it needs:
[Settings]
ServiceAccountName=NT SERVICE\hMailServer
ServiceAccountPassword=The virtual account NT SERVICE\hMailServer needs no password. Grant it Log on as a
service and access to the program, data and database directories. Applied when the service
is next registered.
Check the knock-on effects before you do it. A least-privilege account also has to be able to run your virus scanner and read its signature database (Stopping Viruses §11.2), read the certificate and key files (Encryption and Certificates §9.2), and — if you use DPAPI secret protection below — decrypt secrets that were encrypted under the previous account. Change one thing at a time.
There are two, and the Control Panel's Access & abuse protection → Administrative access page, Two-factor tab, carries both.
flowchart TD
A["Administrator presents a password"] --> B{"Is a server-enforced<br/>second factor enrolled?"}
B -- no --> C{"Is the Control-Panel-only<br/>code configured?"}
C -- no --> IN["Access granted"]
C -- yes --> D["Control Panel asks for a code<br/>after the server has already<br/>accepted the password"]
D --> IN
B -- yes --> E["The password alone is refused,<br/>everywhere"]
E --> F["Control Panel prompts for a code,<br/>COM callers use AuthenticateWithCode,<br/>the REST API reads X-hMailServer-OTP"]
F --> IN
-
Server-enforced (use this one). Enrolling a second factor on the administrator credential itself (
Settings.EnrolAdministratorTOTP, or the button on that tab) shows a scannable QR code; from then on the password alone is refused everywhere — the Control Panel, the REST API and COM scripts alike — and a code must be presented (Application.AuthenticateWithCode; the REST API reads it from anX-hMailServer-OTPheader, and answers a password-only request withX-hMailServer-OTP: required). The secret lives inhMailServer.INI([Security] AdministratorTotpSecret, DPAPI-protected);Settings.DisableAdministratorTOTPremoves it. -
Control-Panel-only (older, weaker). A code the Control Panel itself asks for after the server has already accepted the password. It does nothing for the REST API or for scripts. Its secret is stored under
HKLM, so turning it on or off needs the Control Panel run as administrator; the link Set up two-factor authentication… on the Connect screen sets this one up.
Scan the QR code with Microsoft Authenticator, Google Authenticator, Authy or 1Password and confirm with a code before you leave the dialog — the dialog rolls the enrolment back if you close it without confirming, precisely so a mis-scan cannot lock you out.
Refusals are logged, and the two reasons are distinguished:
Administrator logon refused: the password was correct but a second factor is enrolled and no code was presented. Authenticate with a code (Application.AuthenticateWithCode).
REST API: the administrator password was accepted but the one-time code in X-hMailServer-OTP was not.
Five independent mechanisms. They close different holes, and knowing which is which is the difference between tuning and guessing.
| Mechanism | Counts by | Default | Effect when it fires |
|---|---|---|---|
| Auto-ban | connecting IP | on, 3 failures within 30 minutes, 60-minute ban | An expiring IP range at priority 100 named Auto-ban: <user>, plus a disconnect |
| Account lockout | username (canonicalised) | off | The name is refused without the password being checked, for AccountLockoutMinutes
|
| Logon tarpit | per connection | off | Each failed logon's refusal waits LogonTarpitSeconds × failures so far on that connection, capped at 30 s |
| Recipient tarpit | per session | off | Every RCPT TO past SmtpTarpitCount waits SmtpTarpitDelaySeconds for its reply |
| Submission rate limit | source IP | off |
421 Too many messages from your IP address at MAIL FROM
|
stateDiagram-v2
[*] --> Clean
Clean --> Counting: a logon fails, one failure row written
Counting --> Counting: another failure below the limit
Counting --> Clean: the housekeeping pass deletes rows older than the window
Counting --> Banned: the limit is reached, counted failures cleared, IP range created
Banned --> Clean: the range expires and the housekeeping pass deletes it
Counted per connecting IP address across every protocol that authenticates — SMTP AUTH, POP3, IMAP, ManageSieve and the REST API all feed the same counter. The four settings are on Access & abuse protection → Auto-ban:
| Setting | Default | Trap |
|---|---|---|
AutoBanOnLogonFailure |
on | — |
MaxInvalidLogonAttempts |
3 | 0 disables auto-ban entirely, whatever the switch above says |
MaxInvalidLogonAttemptsWithin |
30 minutes | Not a sliding window. It is how long a failure record survives: a housekeeping pass running every minute deletes older rows, and only while auto-ban is enabled |
AutoBanMinutes |
60 |
0 disconnects the client but creates no range at all, so nothing is actually banned |
The button Clear logon-failure list clears counted failures only. Addresses already
banned stay banned until their Auto-ban: range expires — delete it on the IP ranges page
to release one now.
Five failures on this path are reported rather than swallowed, because each one is a way the control silently stops working:
| Code | Meaning |
|---|---|
| HM6114 | A failed logon could not be recorded, so no ban will ever trigger while this persists |
| HM6099 | The auto-ban range could not be saved, so the address has not been blocked despite reaching the limit |
| HM6113 | Spent failures could not be cleared, so the address may be re-banned immediately after its ban expires |
| HM6112 | Expired IP ranges could not be removed, so an expired ban keeps blocking the address |
| HM6115 | Expired logon failures could not be cleared, so occasional failures spread over a long period may add up to a ban |
Auto-ban cannot see a distributed attack: a botnet spending one guess per address per account never crosses any single address's threshold. Account lockout counts by the name being guessed at, which is the one thing a distributed attack cannot vary.
[Settings]
AccountLockoutThreshold=10 ; failed logons per username before a lockout. 0 = off
AccountLockoutWindowMinutes=30 ; the window they must fall inside
AccountLockoutMinutes=30 ; how long the lock lastsFour properties worth knowing before you enable it:
- It counts by name, not by account, so failures against a nonexistent username lock that string exactly as a real account's would. Counting only real accounts would make the lockout an account-existence oracle.
- It counts the canonical name: domain aliases are resolved and the default domain applied first, so one mailbox does not get a separate budget per spelling.
- A locked name is refused before the password is checked, with the ordinary invalid-credentials reply. Nothing an attacker reads distinguishes a locked name from a wrong password. What is still measurable is timing: skipping the check also skips the Argon2id or PBKDF2 verification, so a locked name answers about as fast as a nonexistent one. That is accepted deliberately — paying the KDF to hide it would hand an attacker a memory-hard amplifier they could aim at a name they locked themselves.
- A lockout refusal is deliberately not charged to the connecting IP. Once a name is locked, the person most likely to keep trying is its owner, with the correct password, and charging those attempts to their address is how a lockout caused by a botnet gets an innocent office NAT — or a co-hosted webmail front end on loopback — banned from SMTP, IMAP and POP3 for everybody behind it.
Every one of these is 0 — off — until you set it. The values below are a starting point, not the defaults.
[Settings]
LogonTarpitSeconds=2 ; per failure on this connection, capped at 30 s. 0 = off
SmtpTarpitCount=10 ; recipients an unauthenticated session may name before...
SmtpTarpitDelaySeconds=5 ; ...each further RCPT TO waits this long, capped at 30 s
MaxSubmissionsPerIPPerMinute=30 ; MAIL FROM commands per source IP per minute. 0 = off
MaxOutboundPerDestinationPerMinute=60 ; messages this server sends per destination domain per minute- A correct password never waits. The logon tarpit multiplies by the number of failures on this connection, in 64-bit arithmetic so an absurd count cannot wrap the product into a small delay, and clamps at 30 seconds.
- The recipient tarpit exempts authenticated sessions and any range with anti-spam switched off — a legitimate MTA names a handful of recipients and never reaches the count; a dictionary attack names hundreds and pays for each.
- Both delays are timers on the connection, not threads asleep. That is why they exist at all: a sleeping thread is a denial-of-service amplifier.
-
MaxSubmissionsPerIPPerMinutecounts everyMAIL FROMfrom that IP, authenticated or not, and refuses the excess with421. It contains a compromised account. -
MaxOutboundPerDestinationPerMinutedefers rather than bounces — enhanced code4.4.5, "mail system congestion" — which keeps you the right side of large providers' rate limits. The log line isDelivery to example.com deferred due to the per-destination rate limit.
The brake on a compromised account, counted per account at submission, and stored in their own INI sections so they can be re-read without a service restart:
[SendingLimits]
MaxMessagesPerAccountPerPeriod=500
MaxRecipientsPerAccountPerPeriod=2000
PeriodHours=24 ; clamped to 1-168
StateSaveIntervalSeconds=10 ; clamped to 1-3600
[SendingLimitsOverrides]
newsletter@yourcompany.com=5000:50000
ceo@yourcompany.com=0:0Recipients are the stricter measure — one message to two thousand addresses is two thousand
recipients. An override replaces the global ceilings for that address; 0:0 exempts it
entirely; an optional third field sets a different period in hours. A malformed line is
ignored with a log entry, and the global limit still applies to that account. Counters
survive a restart via a state file in the data directory.
Forwarding a message keeps the original envelope sender, so the next hop checks SPF for a domain that never authorised this server, and the message fails. These are the two answers. Both are on TLS & certificates → Transport security.
[Settings]
RewriteEnvelopeFromWhenForwarding=0 ; the simple fallback: use the forwarding account's address
SRSEnabled=1
SRSSecret=<a long random string you never change>
BATVEnabled=1
BATVSecret=<a different long random string>| SRS | BATV | |
|---|---|---|
| What it does | Rewrites the envelope sender into one this server can vouch for and can undo on the way back, so bounces still reach the original sender | Signs your outbound envelope sender with a prvs tag, so a bounce for a message this server never sent can be recognised and dropped |
| Needs | SRSSecret |
BATVSecret |
| With the secret empty | Nothing is rewritten — and the plain fallback above is skipped as well, which is strictly worse than leaving SRS off | Senders are not tagged and returning bounces are not validated; the switch reads enabled and does nothing |
The Control Panel shows a live warning for each of those two states, because the switch reading "enabled" while the feature does nothing is the failure mode this page exists to prevent.
SRSSecretmust be stable. Changing it invalidates outstanding SRS addresses, which remain valid for 21 days — so bounces for recently forwarded mail will be lost.
RewriteEnvelopeFromWhenForwarding is the fallback for anyone who does not want to manage
a secret: it makes SPF pass at the next hop at the cost of the original sender's address, so
a bounce comes back to the forwarding mailbox rather than to whoever wrote the message.
While SRS is enabled it is not consulted at all.
Off by default, because nothing constrained this before 5 September 2026 and an installation may depend on that:
[Settings]
SmtpAuthenticatedSenderCheck=1With it on, MAIL FROM in an authenticated session must be the account's own address, an
alias that resolves to it (followed up to five hops), or another mailbox whose owner has
granted this account the post (p) right on their INBOX. That is the RFC 4314 right
whose subject is sending on a mailbox's behalf, IMAP itself never enforces it, and any IMAP
client's SETACL can spell it — so it is the Send-As grant, with no new right invented.
Anything else is refused with 550 5.7.1 before a byte of the message is accepted. The null
sender is always allowed, so an authenticated client can still send a bounce.
The grant is only honoured while IMAP ACL enforcement is on: "ACL disabled" must never become "anybody may send as anybody".
If hMailServer sits behind a load balancer or a TLS terminator, it needs to be told the real client address — otherwise every connection appears to come from the proxy, and auto-ban, the rate limits, the IP ranges and every spam test are all reasoning about the wrong address.
[Settings]
SMTPProxyProtocolEnabled=1
SMTPProxyProtocolTrustedIPs=10.0.0.5, 10.0.0.6
SMTPXClientEnabled=0
SMTPXClientTrustedIPs=Both are off by default and both are gated on a trusted-IP list checked against the real
TCP peer, not against anything the peer claims. XCLIENT is not even advertised to an
untrusted peer — advertising it would both invite the attempt and leak the deployment shape
— and a peer that tries it anyway gets 550.
Related, on the same page:
| Setting | Default | What it does |
|---|---|---|
AuthUserReplacementIP |
empty | Replace the client IP in Received headers for authenticated users |
AddXAuthUserHeader |
off | Add an X-AuthUser header naming the authenticated account |
AddXAuthUserIP |
on | Include the client IP in that header |
AddXOriginalRcptTo |
off | Add an X-OriginalRcptTo header |
BlockedIPHoldSeconds |
0 | How long to hold a refused connection open before closing it |
Maintenance → Server limits & expert settings collects the remaining controls.

Renamed to Server limits & expert settings since this capture, and considerably longer: the cards for front-end proxies, received headers, message-store durability, sending limits, stored-secret protection and the service account are all below the two in view. The shape is the same — one INI key per row, and a service restart to apply them.
| Setting | Default | Why you might change it |
|---|---|---|
ProtectStoredSecretsWithDPAPI |
on | Secrets in hMailServer.INI are DPAPI-protected. Turning it off makes them readable by anyone who can read the file |
MessageStoreFsync |
off | Flush each received message to disk before acknowledging it. Durable, slower — the honest choice if a 250 must mean the message survives a power cut |
MessageStoreConsistencyCheck |
off | Periodically cross-check message rows against files on disk; read-only, writes a report on divergence |
ClientSessionCeiling |
1800 s | Absolute lifetime of one client session. This is what guarantees a stalled scan or a hung peer cannot hold a thread for ever |
ExternalProcessTimeout |
300 s | The ceiling on a command-line virus scanner or an event-script process |
ScriptTimeout |
60 s | Event script execution |
ScriptAllowedObjects |
* |
Restrict which COM objects event scripts may create |
DNSQueryTimeout |
10 s | Every DNS-backed spam check is bounded by this |
SMTPDMaxSizeDrop |
0 | Drop oversized inbound messages mid-transfer rather than reading them to the end |
Invalid command limit (maximumincorrectcommands, COM MaxNumberOfInvalidCommands) |
100, but the switch beside it is off |
Disconnect clients sending too many invalid commands (disconnectinvalidclients) is off by default, so the limit does nothing until you turn it on. Both are on the Protocols page, not this one |
Work down it; each row says what it costs when it is wrong.
| # | Do this | Where | What goes wrong if you skip it |
|---|---|---|---|
| 1 | Confirm no range allows remote → remote without authentication | IP ranges | Open relay |
| 2 | Get a certificate and set every listener's connection security | Encryption and Certificates §9.1, §9.5 | Passwords in the clear; a fresh install offers no TLS |
| 3 | Set Require TLS for auth on the Internet range | IP ranges |
AUTH is offered, and used, on cleartext connections |
| 4 | Leave auto-ban on; confirm the limit is not 0 and the duration is not 0 | Auto-ban | Unlimited password guessing with the feature switched on |
| 5 | Turn on account lockout | AccountLockoutThreshold |
A distributed attack never crosses any single IP's threshold |
| 6 | Choose Argon2id or scrypt, and set a floor | Authentication | Legacy hashes stay in use for accounts nobody logs into |
| 7 | Enrol the server-enforced administrator second factor | Administrative access | A leaked administrator password is a full compromise, including the REST API |
| 8 | Set the two tarpits and MaxSubmissionsPerIPPerMinute
|
INI | A compromised account can empty its rate budget in seconds |
| 9 | Set per-account sending limits | Server limits & expert settings | One compromised mailbox becomes your reputation problem |
| 10 | Turn on SmtpAuthenticatedSenderCheck
|
INI | One compromised account can send as every other |
| 11 | Enable SRS and set its secret if you forward | Transport security | SPF failures on forwarded mail — or, with the secret missing, worse than not enabling it |
| 12 | Move the service to NT SERVICE\hMailServer
|
INI, then re-register |
LocalSystem is more privilege than the server needs |
| 13 | Read the error log for the codes in §17.5 | Monitoring | Every one of them is a security control that has silently stopped working |
Then check the three read-only overview pages — Spam filtering overview, Virus scanning overview and Transport encryption overview — which exist specifically to catch a configuration that looks enabled and is inert.
See also: Encryption and Certificates · Stopping Spam · Stopping Viruses · Security Policy · Settings Reference · Monitoring and Health · Ports Reference · Assurance Case
hMailServer 6.3.2 · AGPL-3.0-or-later · Repository · Report a documentation error
Hmail Server — full index
Start here
1. Install and run
- Before You Install
- Installing hMailServer
- Installing on Linux
- Running in a Container
- The Control Panel
- Your First Domain and Mailbox
- Connecting a Mail Client
- DNS for Your Domain
2. Secure it
3. Operate it
- Monitoring and Health
- Backup and Restore
- Troubleshooting
- Diagnosing Stalled Mail
- Relocating an Installation
- Upgrading hMailServer
- Upgrading Guide
- Migrating the Database Backend
- High Availability Runbook
- Warm Standby
- Runbooks Digest
4. Extend it
- Rules and Sieve
- Aliases Lists and Public Folders
- Routes and Relays
- The COM API and Scripting
- The REST API
- APIs Reference
5. Contribute to it
- Project Handbook
- Architecture
- Contributing
- Release Process
- Governance
- Assurance Case
- Regression Test Environment
- Fuzzing
- Regulatory Scope
- Third-Party Binaries
Look it up — from any journey