Mail Agent
A mailbox-access facade that owns RBAC, posture enforcement, audit emission, dispatch (local / worker-pool / queue), and worker isolation around a mail store, so a protocol server built on top can stay a thin shell. It is designed to be the shared dispatch layer mail-protocol servers route through; today the read surface and the mailbox-mutation + Sieve-upload methods are wired, while the compose/send and identity/vacation/MDN/export verbs are not yet wired into the facade (see below).
agent.create() returns the facade. Methods backed by b.mailStore (folders / fetch / search / move / flag / delete / expunge, plus sieve.put) run immediately. The remaining verbs — compose / send / reply / forward, sieve.list / sieve.activate, identity / vacation / mdn.*, export / job / import — throw mail-agent/not-implemented: they are not yet routed through the agent. Until they are, compose the underlying primitive directly (b.mail.send.deliver for outbound, b.mail.sieve for Sieve, b.mailMdn for MDN, etc.) — which is what the framework's own JMAP emailSubmissionSet handler does. They wire into the facade when a protocol server adopts the agent as its dispatch layer.
var agent = b.mail.agent.create({
store, audit, permissions,
posture: "hipaa",
identity: function (actorId) {
return { email: actorId + "@hospital.example", name: actorId };
},
dispatch: { mode: "auto" },
});
var folders = await agent.folders({ actor: { id: "u1", roles: ["clinician"], purposeOfUse: "TREATMENT" } });
## Dispatch modes
- local (default when no queue) — every method runs in-process. Fast-path ops (fetch / folders / flag / quota) bypass worker dispatch; heavy ops (search / export / sieve-on-bulk) run on the supplied workerPool when configured. - queue — every method publishes to the queue topic; an agent.consumer() running in a dedicated process (or replicas across hosts) pulls and executes. The consumer carries its own store reference; the queue payload carries actor + posture metadata, which the consumer re-validates against its local posture before unseal (no posture downgrade across the boundary). - auto — fast-path ops local, heavy ops to queue if configured else workerPool else local.
## Posture enforcement
When posture is set, every actor passed to every method must carry the posture-required fields (HIPAA → purposeOfUse, PCI-DSS → pciScope, GDPR → lawfulBasis). b.guardMailQuery. validateActor is the canonical check; the agent invokes it on every entrypoint.
b.mail.agent.create(opts) #
{
store: b.mailStore instance, // required
audit: b.audit namespace, // optional; defaults to b.audit
permissions: b.permissions instance, // optional; agent skips RBAC if absent (operator's choice)
posture: "hipaa"|"pci-dss"|"gdpr"|"soc2"|null,
identity: function(actorId) → { email, name } // OR object map
dispatch: { mode, queue, workerPool, queueTopic, taskTimeoutMs, queueDepthCap, vaultKeyDelivery },
}
Create the agent facade. Returns an object with read / write / sieve / identity / mdn / export / import methods. Reads stay synchronous-shaped via promises; writes audit on completion. (The queue consumer is the sibling export b.mail.agent.consumer, not a method on this object.)
var agent = b.mail.agent.create({ store: myStore });
var folders = await agent.folders({ actor: { id: "u1" } });
b.mail.agent.consumer(opts) #
{
agent: a b.mail.agent.create() instance, // required
queue: b.queue / b.queueRedis, // required
taskTopic: string, // default "mail.agent.tasks"
maxConcurrency: number, // default 4
}
Create a queue consumer that pulls mail.agent.tasks envelopes and runs them against an operator-supplied agent. Each replica runs in its own process / host for multi-host load-spreading; queue payload carries actor + posture; consumer re-validates against its local posture before unseal.
var consumer = b.mail.agent.consumer({ agent: localAgent, queue: redisQueue });
await consumer.start();
Last updated 2026-08-08T16:39:49.652Z by seeder.