diff --git a/Service-Bus.md b/Service-Bus.md index 3f1e054..374167f 100644 --- a/Service-Bus.md +++ b/Service-Bus.md @@ -172,6 +172,11 @@ Current topic families: | `canary.{token_id}.triggered` | `decnet canary` worker | `{token_id, decky_id, src_ip, user_agent?, request_path?, dns_qname?, occurred_at, raw_headers?}` — attacker hit the HTTP slug or DNS subdomain; correlator + webhook fanout consume to attribute and forward | | `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. | +| `ttp.tagged` | _reserved (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}` | _reserved (TTP worker)_ | `{rule_id, technique_id, sub_technique_id?, tag_uuid, confidence}` — per-technique fan-out for SIEM correlation rules that subscribe to one technique. Topic key is the parent technique; `sub_technique_id` lives in the payload. 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. | | `system.log` | _reserved_ | — | | `system.bus.health` | Bus worker heartbeat | `{ts, uptime_s}` |