The single span surface for mailglass_inbound (D-45-01).
Mirrors Mailglass.Webhook.Telemetry: every inbound :telemetry.span/3 call
lives here so the extended NoPiiInTelemetry check (enabled for inbound in
Plan 01) has exactly ONE module to audit, plus the four call sites. Callers
MUST NOT reach for :telemetry.span/3 directly — use the named helpers below.
Events emitted
| Event | Type | Stop metadata keys (D-45-03 whitelist) |
|---|---|---|
[:mailglass_inbound, :ingress, :request, :start | :stop | :exception] | full span | provider, tenant_id, status, byte_size |
[:mailglass_inbound, :route, :match, :start | :stop | :exception] | full span | mailbox, candidate_count, status |
[:mailglass_inbound, :persist, :record, :start | :stop | :exception] | full span | provider, tenant_id, operation, record_type |
[:mailglass_inbound, :execution, :run, :start | :stop | :exception] | full span | mailbox, outcome, source |
[:mailglass_inbound, :ingress, :rate_limit, :start | :stop | :exception] | full span | provider, tenant_id, bucket, limit, retry_after |
[:mailglass_inbound, :ingress, :suppression_flag, :start | :stop | :exception] | full span | provider, tenant_id, flagged |
[:mailglass_inbound, :prune, :sweep, :start | :stop | :exception] | full span | status, records_deleted, evidence_deleted, fresh_runs_deleted, replay_runs_deleted |
Event-name convention note (D-49-17 deviation): the CONTEXT named the rate-limit event
[:mailglass_inbound, :rate_limit, :stop]and the prune event[:mailglass_inbound, :prune, :stop](3 final segments). The locked[root, domain, resource, action]4-segment telemetry convention (TelemetryEventConvention, enforced at lint time) requires 4 final segments, so these ship as[:mailglass_inbound, :ingress, :rate_limit, *](rate-limit is an ingress-path event, beside:suppression_flag) and[:mailglass_inbound, :prune, :sweep, *]. Same resource name, convention-compliant.
Every helper is a full :start/:stop/:exception span via :telemetry.span/3.
There is no single-emit (fire-and-forget) helper here — inbound emits via spans
only. :telemetry.span/3 supplies :duration in its :stop measurements
automatically; callers MUST NOT hand-compute latency into metadata.
Per-request stop metadata enrichment
Each helper accepts a zero-arity function returning either:
result— bare value; stop metadata equals themetadataargument passed at call time (before the outcome is known).{result, stop_metadata}— tuple; stop metadata is the returned map. Used by the call sites to attach the classified:status,:operation,:outcome,:mailboxonto the:stopevent after the inner function returns. Start metadata is always themetadataargument at call time.
Whitelist discipline (D-45-03)
The ONLY allowed metadata keys across all spans:
provider, tenant_id, status, latency, byte_size, mailbox, candidate_count,
outcome, source, operation, record_type,
bucket, limit, retry_after, flagged,
records_deleted, evidence_deleted, fresh_runs_deleted, replay_runs_deletedThe Phase-49 additions (bucket, limit, retry_after, flagged + the per-table
prune counts) are all counts/types/statuses — never PII (D-49-17/23/29). The
rate-limit + suppression-flag spans carry the bucket TYPE and a boolean flag,
never the recipient/sender value.
NEVER include in any metadata map:
:to, :from, :cc, :bcc, :subject, :body, :html_body, :headers,
:recipient, :sender, :emailNoPiiInTelemetry (extended to inbound in Plan 01) lints THIS module plus every
caller against the forbidden-key set.
Handler isolation (TELE-05)
:telemetry.span/3 wraps each attached handler in a try/catch. A handler that
raises is detached automatically and [:telemetry, :handler, :failure] is
emitted — the caller's inbound pipeline is unaffected. mailglass_inbound does
not add a parallel try/rescue around business code (that would duplicate or,
worse, swallow the meta-event operators rely on). TELE-05 comes for free from
routing every span through :telemetry.span/3.
Summary
Functions
Wrap the body of MailglassInbound.Execution.execute/2 in a
[:mailglass_inbound, :execution, :run, *] span.
Wrap the entire inbound ingress path in a
[:mailglass_inbound, :ingress, :request, *] span.
Wrap the repo.transact in MailglassInbound.Ingress.Persist.persist/2 in a
[:mailglass_inbound, :persist, :record, *] span.
Wrap a retention prune sweep in a [:mailglass_inbound, :prune, :sweep, *]
span (IOPS-03, D-49-29). Consumed by Plan 03. The :sweep resource segment
satisfies the 4-segment event convention.
Wrap the post-verify rate-limit check in a
[:mailglass_inbound, :ingress, :rate_limit, *] span (IOPS-04, D-49-17). The
rate limiter is an ingress-path event, so it lives under the :ingress domain
(beside :suppression_flag) to satisfy the 4-segment event convention.
Wrap MailglassInbound.Router.Matcher.match/2 in a
[:mailglass_inbound, :route, :match, *] span.
Wrap the inbound suppression-flag computation in a
[:mailglass_inbound, :ingress, :suppression_flag, *] span (IOPS-05, D-49-23).
Consumed by Plan 02.
Functions
Wrap the body of MailglassInbound.Execution.execute/2 in a
[:mailglass_inbound, :execution, :run, *] span.
This is the single synchronous sync point both the Oban and Task.Supervisor
async paths funnel through, so the span covers both (D-45-02, RESEARCH Pitfall 5
— wrap execute/2, never dispatch/2).
Stop metadata SHOULD include :mailbox, :outcome, :source. All PII-free.
fun may return a bare result OR {result, stop_metadata}.
Wrap the entire inbound ingress path in a
[:mailglass_inbound, :ingress, :request, *] span.
Stop metadata SHOULD include :provider, :tenant_id, :status,
:byte_size. NEVER include PII (see the module doc whitelist). Latency is
supplied by :telemetry.span/3 in its measurements — do not put it in metadata.
fun may return a bare result OR {result, stop_metadata} — see the
moduledoc's "Per-request stop metadata enrichment" section.
Wrap the repo.transact in MailglassInbound.Ingress.Persist.persist/2 in a
[:mailglass_inbound, :persist, :record, *] span.
Stop metadata SHOULD include :provider, :tenant_id, :operation
(:insert | :dedup_skip), :record_type. All PII-free.
fun may return a bare result OR {result, stop_metadata}.
Wrap a retention prune sweep in a [:mailglass_inbound, :prune, :sweep, *]
span (IOPS-03, D-49-29). Consumed by Plan 03. The :sweep resource segment
satisfies the 4-segment event convention.
Stop metadata SHOULD include the per-table counts :records_deleted,
:evidence_deleted, :fresh_runs_deleted, :replay_runs_deleted, and a
:status. Counts only — no PII.
fun may return a bare result OR {result, stop_metadata}.
Wrap the post-verify rate-limit check in a
[:mailglass_inbound, :ingress, :rate_limit, *] span (IOPS-04, D-49-17). The
rate limiter is an ingress-path event, so it lives under the :ingress domain
(beside :suppression_flag) to satisfy the 4-segment event convention.
Stop metadata SHOULD include :provider, :tenant_id, and on a trip the
bucket :bucket TYPE (:tenant | :recipient | :sender_domain), :limit
(capacity), and :retry_after (seconds). It MUST NOT carry the recipient or
sender VALUE — only the bucket type (D-49-16).
fun may return a bare result OR {result, stop_metadata}.
Wrap MailglassInbound.Router.Matcher.match/2 in a
[:mailglass_inbound, :route, :match, *] span.
Stop metadata SHOULD include :mailbox + :candidate_count on a match, or
:status (:no_match) + :candidate_count on a miss. All PII-free.
fun may return a bare result OR {result, stop_metadata}.
Wrap the inbound suppression-flag computation in a
[:mailglass_inbound, :ingress, :suppression_flag, *] span (IOPS-05, D-49-23).
Consumed by Plan 02.
Stop metadata SHOULD include :flagged (boolean), :tenant_id, :provider —
never the address. No auto-bounce, no auto-suppression: the flag is diagnostic
signal only.
fun may return a bare result OR {result, stop_metadata}.