Service-Bus: document email.received mal_hash_match field (DEBT-046)
Update the email.received row to reflect the actual ingester-emitted payload shape (now that the SMTP services emit it, vs the "reserved/no publisher yet" status from the original landing). Add the new mal_hash_match field with its absence-vs-False-vs-True semantics, and a one-line note on the observed_attachments companion table.
@@ -175,7 +175,7 @@ Current topic families:
|
||||
| `canary.{token_id}.revoked` | API (`DELETE /tokens/{id}`) | `{token_id, decky_id, revoked_at}` — operator removed a token; subscribers may evict cached lookups by token id |
|
||||
| `system.canary.health` | `decnet canary` worker | standard worker heartbeat |
|
||||
| `smtp.probe.pending` | Ingester | `{decky, attacker_ip, stored_as, mail_from, rcpt_to}` — fired when an smtp_relay decky stores a new inbound message. The realism worker subscribes and forwards the email to the upstream relay if this source IP has not yet reached `probe_limit`. `stored_as` is the quarantine filename (relative to the per-decky smtp dir). `mail_from` / `rcpt_to` are the envelope sender/recipients captured at SMTP time. The worker writes a `probe_relay` bounty row on success or failure so the limit check is DB-backed and survives container restarts. |
|
||||
| `email.received` | _reserved (smtp / smtp-relay services)_ | `{message_uuid, decky_id, session_id, attacker_ip, mail_from, rcpt_count, body_sha256, header_names: [...], attachment_sha256s: [...]}` — fired on full-message receipt, consumed by the TTP `email_lifter`. PII discipline (TTP_TAGGING.md "Hard parts §6"): hashes, counts, header *names*, and rcpt-domain sets only — never rcpt addresses or body bytes. Constant declared with no publisher yet; the SMTP services start emitting it when E.3 (the implementation phase) lands. |
|
||||
| `email.received` | Ingester (`decnet/web/ingester.py:_publish_email_received`) | `{source_id, attacker_uuid, attacker_ip, decky_id, service, subject, from_domain, mail_from_domain, return_path_domain, rcpt_count, rcpt_domains, x_mailer, dkim_signed, spf_pass, urls, attachment_count, attachment_sha256s, attachment_extensions, body_simhash, body_base64_bytes, attachment_macros, attachment_password_protected, html_smuggling, stored_as, body_sha256, mal_hash_match?}` — fired on full-message receipt, consumed by the TTP `email_lifter`. PII discipline (TTP_TAGGING.md "Hard parts §6"): hashes, counts, header *names*, and rcpt-domain sets only — never rcpt addresses or body bytes. **`mal_hash_match`** (DEBT-046, 2026-05-03): boolean, present only when the message had attachments. `True` when any attachment SHA-256 hits the configured `MalHashProvider` (default: MalwareBazaar bulk feed, gated on `DECNET_MALWAREBAZAAR_AUTH_KEY`); `False` when every hash was checked-and-clean; **omitted** when there were no attachments at all (so R0046's `is True` predicate stays silent on hash-less mail, matching the rule's pre-paydown behavior). Per-hash observations also persist to the `observed_attachments` table — DECNET is a honeypot _platform_, not just a consumer of intel. |
|
||||
| `ttp.tagged` | `decnet.ttp.worker` | `{tag_uuids: [...], techniques_added: [...], attacker_uuid?, identity_uuid, session_id?}` — published only when `INSERT OR IGNORE` wrote at least one new row. Idempotent re-evaluations that produce zero new tags publish zero events (loop-prevention invariant — a webhook subscriber re-triggering enrichment on `ttp.tagged` could otherwise loop forever). |
|
||||
| `ttp.rule.fired.{technique_id}` | `decnet.ttp.worker` | `{technique_id, tag_uuids: [...], attacker_uuid?, identity_uuid, session_id?}` — per-technique fan-out for SIEM correlation rules that subscribe to one technique. Topic key is the parent technique; sub-technique IDs are not promoted to the topic (a `T1110.001` tag fires `ttp.rule.fired.T1110`). Use `ttp.rule.fired.>` for fleet-wide subscribers. |
|
||||
| `ttp.rule.suppressed` | _reserved (TTP worker)_ | `{rule_id, technique_id, reason}` where `reason ∈ {"below_floor", "rate_limited", "rule_disabled"}` — observability for tags that *would have* been written but were dropped. Drives the dashboard's per-rule suppression counters. Suppression-event publish is deferred — the v0 worker drops sub-floor confidence at the repo layer and the bus event is unwired. |
|
||||
|
||||
Reference in New Issue
Block a user