Skip to content
  • Categories
  • Recent
  • Popular
  • Tags
  • Users
  • Groups
  • Documentation
  • Downloads
  • Report a bug
  • GitHub mirror
  • Paid support
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
hMailServer Community Forum
hMailServer 6.3.8 is the current release, signed 2 November 2026. Windows and Linux, and an in-place upgrade from 5.3 or later, but for the original hMailServer's 5.7.Download 6.3.8 Documentation FAQ
  1. Support and discussion for hMailServer
  2. Running hMailServer
  3. Guides & tutorials
  4. Compliance: eDiscovery, legal holds, personal data, erasure and the audit trail

Compliance: eDiscovery, legal holds, personal data, erasure and the audit trail

Scheduled Pinned Locked Moved Guides & tutorials
administrationsecuritycontrol-deckrest-apibackup
1 Posts 1 Posters 128 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • ProgressiverobotP
    ProgressiverobotP
    Progressiverobot
    hMailServer Team
    wrote on last edited by chris
    #1

    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) regroups the Control Panel's sidebar: the audit trail, journaling and eDiscovery are a Compliance group of their own rather than the last rows of Monitoring & troubleshooting, as the table below has it, and Ctrl+K takes every old title and route to its page. See the 6.3.7 announcement.

    This guide covers what an operator is asked for when a lawyer, a regulator or a person with a data request arrives: eDiscovery (cases, searches, legal holds and exports), a person's own data (the export and the erasure), and the audit trail that records who did what. eDiscovery, the person's data as one archive, the erasure of log lines, the erasure's reach into the stores beside the mailbox, and the audit trail are new since 6.3.4. The one thing to know first: cases, their files and their holds are not in the backup. A server rebuilt from a backup has no cases, no exports and no holds, so place the holds again before its mail can be deleted.

    Where it is

    Where What
    The Control Deck, Discovery view The whole of it: cases, the search form, holds with Lift the hold, exports with Download, Open and Close
    REST, /api/v1/discovery/cases The same ten routes the Deck calls, in the OpenAPI document
    The Control Panel (Windows only), Monitoring & troubleshooting → Discovery (eDiscovery, in the Compliance group, since 6.3.7) Read-only: the cases and their lines of the audit trail, read over the REST listener, which must be on (RestApiPort). It changes nothing
    COM, hmctl Neither

    On Linux, then, discovery is worked from the Deck or over REST. Discovery and the erasure do nothing until somebody opens a case or asks for an erasure; the audit trail is on from the first start.

    Two neighbouring tools are not this one. The archive has had a legal hold of its own since before 6.3.4 (/api/v1/archive/{id}/hold), which keeps one archived copy from the archive's retention sweep (Quotas, retention and the archive). Journaling, a copy of a domain's or an account's mail sent elsewhere as it passes, has its own topic: Journaling and dual delivery.

    Cases

    A case has a name (up to 255 characters), a description (up to 1,000), who opened it and when, and a state: open or closed. Its searches, holds and exports belong to it. Closing a case releases every hold of it that is still live, each release on the audit trail; a closed case runs no search, places no hold and makes no export, and what it made stays readable and downloadable. A case is never deleted, and an address's erasure does not reach it.

    The files a case makes are under the data directory, in Discovery\case-<id>\: search-<id>.json for each search and export-<id>.zip for each export. Nothing removes an export; delete its zip by hand when it is no longer needed.

    Searching

    POST /api/v1/discovery/cases/{id}/searches takes:

    Part What it means
    custodians (required, at most 100) An address, a domain, or * for every account on the server and the whole archive
    from, to (optional, YYYY-MM-DD) The day a message is dated by its Date field, read as UTC; else the day the server stored it
    words (optional, at most 50) Every word must be found, a quoted phrase as one: a case-insensitive substring of what IMAP SEARCH TEXT reads
    attachment (optional) name, type and min_kb; {} is any attachment at all
    scope mailboxes, archive or both, the default

    There is no sender or recipient criterion: a word is looked for in the whole decoded header, and a person is named as a custodian. A query the server cannot take is refused with 400 and the reason; 202 answers with the search's id, and the search is queued.

    Where it looks. For each custodian account: its folders, its deleted items (what a hold kept, and what RecoverDeletedDays keeps), and the archive. A search copies nothing; it lists each hit, and lists separately what it could not read, with the reason.

    What it cannot see.

    • 100,000 hits. A search stops there and says truncated. Narrow it and run it again.
    • Text inside a PDF, a Word document or a spreadsheet. Only text/* attachments are read, up to 1 MB of characters a message. An attachment criterion still finds the PDF by name, type and size.
    • More than 80 MB a message. It is listed as not searched when the query asks about its body, and never dropped as a non-match.
    • Zero-access mailboxes and messages encrypted end to end are matched on their headers alone and marked headers_only.
    • The stores beside the mailbox, <data directory>\Confidential and \Protected, and archived copies with no index row (a bounce, a delivery report).

    The full-text index is a filter, never the answer. It is off by default (IndexerFullText). When it is on, a message the index proves cannot contain a word is ruled out; every other message is read.

    One at a time. Searches and exports are queued and run by one runner, the server's own. Only one hMailServer service may run against a database at a time (Warm standby and high availability), so a standby started after a failover takes over the queue rather than sharing it.

    Holds

    POST /api/v1/discovery/cases/{id}/holds takes one of:

    Body Holds
    {"scope":"account","target":"[email protected]"} Every message of the account, and its archived copies
    {"scope":"domain","target":"example.com"} Every message of every account in the domain, and their archived copies
    {"scope":"archive","search":12} The archived copies search 12 of this case matched, at most 10,000 at once

    A hold is live until it is released or its case is closed, and what it keeps is kept by any live hold of any case.

    Path Under a hold
    The person's delete (IMAP EXPUNGE, POP3 DELE, the webmail's delete, a folder deleted) and an administrator's delete of a message The message leaves the folder, and a copy is kept among the account's deleted items whatever RecoverDeletedDays says (0 by default). If the copy cannot be made, the delete is refused (HM7333)
    An IMAP MOVE out of the account The original is kept among the held account's deleted items
    Every message of the account deleted at once Refused, 409 over REST
    The account's own filters at delivery: a rule's Delete or Reject, a Sieve discard, reject or redirect, a forward that keeps no copy The copy is kept among the deleted items instead of dropped; if it cannot be kept, the message is delivered
    Retention, the recover-deleted sweep, deleting a deleted item for good Nothing of the held account is deleted
    A mailbox restore with mode replace, a full restore with the domains option Refused (the full restore as HM5014)
    Moving the account to another domain, removing its mailbox key, deleting the account or its domain Refused, over REST, SCIM and COM alike
    The archive's retention sweep and an address's erasure A held copy keeps its file and its index row

    What a hold does not reach: the server's and a domain's rules, which decide before a message is split between its recipients; spam and viruses refused or deleted before a message is the account's, and what the quarantine discards; the delivery queue; the confidential and password-protected stores; archived copies with no index row. If ArchiveDir was put inside the data directory, a full restore with the messages option replaces held archived copies too, so keep the archive outside it.

    The hold is not hidden from the custodian. The account's own GET /api/v1/me/recoverable answers held: true and lists what the hold kept, and the webmail's Recover deleted items page offers no delete for good.

    It fails closed. When the holds cannot be read, every question is answered held, and the error log says so once a minute (HM7332). A hold is a row in the database. The server reads the holds again as soon as one is placed or released, and no more than five seconds after a change made elsewhere.

    Exports

    POST .../searches/{search}/export queues an export of a finished search's hits. The zip holds:

    • Each hit as EML, the message's own bytes as the server stores them, under <custodian>/<folder>/<n>-<message id>.eml, and an mbox per custodian folder beside them, in parts of at most 256 MB.
    • SHA256SUMS, so that sha256sum -c SHA256SUMS checks the unpacked export.
    • manifest.json: the case, the search as it was run, who ran it, who asked for the export, every item with its path, SHA-256 and size, and every item withheld, with the reason.

    The zip's own SHA-256 is kept with the export, and the case answers it (exports[].sha256).

    Withheld: a zero-access mailbox's messages, and anything deleted, moved or swept since the search ran. Search again to find where a message is now.

    Limits. The zip written is the 32-bit format, so no export passes 4 GB: narrow the search and export each part. A download past 500 MB answers 413 with where the file is on the server's disk and its SHA-256. An export that would leave the data directory's volume with less than a gigabyte free fails. A discovery export is never a PST; an examiner who needs one converts the EML files with a third-party converter.

    Who may

    Permission Allows Held by
    view_discovery Reading the cases, their searches and hits, holds and exports The server administrator and the auditor role
    manage_discovery Opening and closing a case, running a search, placing and releasing a hold, making an export The server administrator; a custom role may be given it

    Downloading an export needs view_mailbox as well, since it hands over the mail itself, the custodians' deleted items among it. A search that reads the archive needs view_archive as well. A credential confined to named domains names no custodian outside them, never *, and is answered 404 for a case that reaches outside them.

    A person's data: the export

    One .zip of everything the server holds for an account. The person downloads it from the webmail's Your data card (GET /api/v1/me/export.zip), with no administrator involved; that download is refused (403) to a request authenticated with an app password. The administrator has the Deck's "Everything (.zip)" on the account row, GET /api/v1/accounts/{address}/export.zip (the administrator password's alone), POST /api/v1/accounts/{address}/export with archive, hmctl privacy export <address> --archive <file> and, on Windows, COM Account.ExportArchive(file).

    Entry What it is
    manifest.json The address, the moment, and the counts of what follows
    mail/<folder path>.mbox Every folder with mail, one mbox each
    account.xml What the backup carries for the account, less the credentials
    contacts.vcf The address book as vCard 3.0
    calendars/<calendar>/<object> Every calendar object as the client sent it
    preferences.json, filters.sieve The webmail's preferences, and the active Sieve script
    logs.txt The lines of the server's own log files that name the address, up to 50 MB

    A download past 500 MB is refused with 413, and the operator writes the archive to disk instead; the disk form has no ceiling but the format's 4 GB.

    A person's data: the erasure

    POST /api/v1/addresses/erase, hmctl privacy erase, the Deck's "Erase traces" and, on Windows, the Control Panel's "Erase traces..." remove what deleting the account deliberately leaves: quarantine entries, message-trace rows, greylisting triplets, distribution-list memberships, aliases on either side, the bounce and complaint records, and the rows naming the address in other people's address books and first-contact memory. With the archive included, the person's archive tree is swept and every index row naming the address goes. The log lines are asked for by name (include_logs on the REST body, --logs😞 every occurrence becomes [erased], and a log past 256 MB is reported instead of rewritten.

    Since 6.3.4 it also reaches the stores beside the mailbox. A message held for review that the address sent or would receive is discarded. The address is taken out of the recipients of a message that expires, and one it sent is ended. A password-protected message to it is ended. The recipients an account remembers writing to, the out-of-office memory, booking policies and correspondents' S/MIME certificates and OpenPGP keys lose it.

    Every list of addresses is matched whole. Erasing [email protected] leaves [email protected] and [email protected] as they are.

    A legal hold outranks it. An erasure never deletes a message from a mailbox, so a hold meets it only in the stores it deletes from, and there the hold wins. The REST answer's held counts what a hold kept; run the erasure again once the case is closed. When the holds themselves cannot be read, the erasure fails (HM6250; 500 over REST) and says nothing about a legal hold.

    What it leaves, by design, among others: mail in mailboxes, the person's own and everybody else's, and a mailbox's deleted items; a list's archive of its posts; other people's calendars; configuration somebody made that names the address (a forward, a rule, a list's moderator); the audit trail, and the alert events, which nothing prunes yet; the spam trap record; the delivery queue and the webhook queue; the contacts in DMARC and TLS reports; the archive's Inbound folder; and backups, which are outside the running server's reach. PersonalData.md has the full list, with each reason.

    The audit trail, for a compliance reader

    The audit trail is on by default. It keeps one row in hm_audit for each administrative change made over COM, REST or the Deck, a mailbox's move included, with a few exceptions such as an API key made or revoked or a quarantined message released or deleted; it records changes, never reads. A password, key, token or other secret is recorded as (changed), never as a value. Each row carries the SHA-256 of the row before it and its own, so GET /api/v1/audit/verify tells an edited row from a removed one: tampering is detectable, not impossible, since anyone who can write the table can recompute the whole chain. audit_retention_days is 0 by default, which keeps everything.

    Three things on it matter here. Every discovery search is recorded with its whole query; a download of a discovery export is recorded on the trail alone, so with the trail off a download is recorded nowhere; and an erasure is recorded with the address it erased wherever it deletes from recorded tables (aliases, list recipients, the archive index): those rows name the person until audit_retention_days ages them out. Alerts, the audit trail, syslog and the journal has the columns, reading and putting a change back, who may read it, and what it does not record.

    Regulatory scope, in one paragraph

    The project's own determination, reviewed 12 August 2026, is that the EU Cyber Resilience Act and the revised Product Liability Directive do not place a manufacturer's obligations on it, because it is supplied outside a commercial activity. The document calls itself a self-assessment and not legal advice, and lists the triggers that would re-open it. Under GDPR and UK GDPR the project is neither controller nor processor: the operator of an installation is. The export, the erasure and the log retention limits exist so that operators can meet their own obligations.

    What it does not do

    • No PST export, and no text read from attachments but text/*.
    • No silent hold: the custodian can see what a hold kept.
    • No export past 4 GB and no download past 500 MB.
    • No saved queries, review tags, redaction or de-duplication across custodians: a message sent to two custodians is two hits.
    • No search of backups, and no case carried by a backup.

    Where to read more

    • Wiki: Compliance and eDiscovery.
    • Documents: Discovery.md, PersonalData.md, AlertsAndAuditTrail.md and RegulatoryScope.md.
    • On this forum: The Control Deck, Journaling and dual delivery, and what a backup carries and what it leaves in Backup and restore.
    1 Reply Last reply
    0

    Hello! It looks like you're interested in this conversation, but you don't have an account yet.

    Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

    With your input, this post could be even better 💗

    Register Login
    Reply
    • Reply as topic
    Log in to reply
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes


    hMailServer Community

    The community support forum for hMailServer — the free, open-source mail server for Windows and Linux — maintained by Progressive Robot Ltd. The software is AGPL-3.0 and stays that way; this is where it is discussed, debugged and documented.

    The software

    • Documentation
    • Downloads
    • hMailServer 6.3
    • Source on GitLab

    Getting help

    • Forum rules
    • About this forum
    • Support plans
    • Pay monthly support
    • Talk to Progressive Robot

    Legal

    • Privacy policy
    • Terms and conditions
    • Cookie policy

    Progressive Robot Ltd · 220 Chester Business Park, Heronsway, Chester, Cheshire CH4 9GB, United Kingdom · +44 (0)1244 911212 · [email protected] · VAT 506152326

    Powered by NodeBB

    • Login

    • Don't have an account? Register

    • Login or register to search.
    • First post
      Last post
    0
    • Categories
    • Recent
    • Popular
    • Tags
    • Users
    • Groups
    • Documentation
    • Downloads
    • Report a bug
    • GitHub mirror
    • Paid support