Mail journal (WORM)
Write-Once-Read-Many (WORM) journal archive for inbound + outbound mail. Financial-services regulations ([SEC 17a-4(f)](https://www.ecfr.gov/current/title-17/chapter-II/part-240/section-240.17a-4), [FINRA Rule 4511](https://www.finra.org/rules-guidance/rulebooks/finra-rules/4511)), [HIPAA §164.312(b)](https://www.ecfr.gov/current/title-45/subtitle-A/subchapter-C/part-164#p-164.312(b)) audit-trail requirements, and [MiFID II Article 16(7)](https://www.esma.europa.eu/sites/default/files/library/mifid-ii-recordkeeping-final-report.pdf) EU financial-communications retention all require a tamper-evident, retention-bound, legal-hold-aware copy of every message that crosses the mail boundary. v0.9.57 lands that primitive as a thin composition over the framework's existing WORM substrate.
What it composes:
- b.objectStore.bucketOps({ objectLockEnabled: true }) — the WORM storage layer. Object Lock (S3 / Azure Immutable Blob / GCS retention-policy) is the substrate that makes "write once" enforceable at the storage layer, not just policy. - b.vault.seal — every journaled payload (headers + envelope + body) is sealed at rest with the operator's vault key. The DB row keeps forensic-queryable plaintext columns (journalId, direction, archivedAt, actorId, messageId, sizeBytes, regimes[], legalHold, storageKey); everything else lives in the single sealed blob column. - b.legalHold — every entry carries a legalHold flag. Once set, the entry is exempt from retention-window expiry even after the floor passes. - b.retention.complianceFloor — per-regime retention windows (HIPAA 6yr, SOX 7yr, MiFID II 5yr, FINRA / SEC 17a-4 6yr). Operator declares which regimes apply via regimes: ["sec-17a-4", "finra-4511", "hipaa"]; the journal computes the longest window across all declared regimes and tags every entry. - b.audit.safeEmit — every record / read / list operation emits an audit event on the framework's existing audit chain.
What it does NOT do:
- **No delete surface.** The WORM bucket enforces immutability; this primitive doesn't even expose delete(). Operators who need GDPR Art. 17 erasure on a journaled message MUST crypto- erase via b.cryptoField.eraseRow (rotates the per-row key so the sealed bytes become permanently undecryptable) — the operator's posture choice between "regulatory record-keeping overrides the erasure right" and "right-to-be-forgotten overrides record-keeping". The framework refuses to pick. - **No automated expiry.** expireSurface() returns the list of entries past their retention floor + not under legal hold; operators decide what to do with that list (it's typically "leave them; the storage cost is negligible and the audit trail benefit is real"). - **No MX / submission auto-wiring.** v0.9.57 ships the primitive; the next slice will wire record() from the v0.9.46 MX listener + v0.9.47 submission listener so every accepted inbound + outbound message journals automatically.
b.mail.journal.create(opts) #
{
storage: b.objectStore.bucketOps handle,
regimes: string[],
vault: b.vault handle,
legalHold: b.legalHold handle,
db: b.db handle,
audit: b.audit namespace,
namespace: string,
}
Returns a journal handle bound to the operator-supplied WORM bucket. The bucket SHOULD have Object Lock / immutability enabled at the storage layer (S3 ObjectLockEnabled, Azure Immutable Blob, GCS retention-policy) — the journal primitive emits an audit warning at create-time if the bucket reports objectLockEnabled: false, but doesn't refuse since some operator deployments use FS-level WORM via filesystem ACLs the framework can't introspect.
var journal = b.mail.journal.create({
storage: operatorWormBucket,
regimes: ["sec-17a-4", "finra-4511"],
vault: b.vault,
legalHold: b.legalHold,
db: b.db,
});
await journal.record({
direction: "inbound",
actorId: "compliance",
messageId: "",
headers: { from: "alice@x.com", to: "bob@y.com", subject: "Q3 results" },
bodyBytes: rfc822Bytes,
envelope: { mailFrom: "alice@x.com", rcptTo: ["bob@y.com"] },
});
Last updated 2026-08-08T16:39:49.652Z by seeder.