Service-Bus: TTP worker now publishes ttp.tagged + ttp.rule.fired
Flips the 'reserved (TTP worker)' annotations on ttp.tagged and
ttp.rule.fired.{technique_id} to actual publisher attribution
(decnet.ttp.worker), reflecting the E.3.14 worker bootstrap that
landed in DECNET. ttp.rule.suppressed stays reserved — v0 drops
sub-floor confidence at the repo layer and the bus event is
unwired pending E.3.14b.
@@ -174,9 +174,9 @@ Current topic families:
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `ttp.rule.reloaded.{rule_id}` | `decnet.ttp.store` (FilesystemRuleStore + DatabaseRuleStore) | `{rule_id, rule_version, deleted?}` — fired by the rule store when a rule's *definition* changes (YAML edit on disk for the FS backend, `ttp_rule` row update on the DB backend). One event per per-rule edit — never batched. A 5-rule deploy fires 5 events; the engine recompiles each rule alone and atomically swaps it into the dispatch index. Built via `topics.ttp_rule_reloaded(rule_id)`; fleet subscribers use `ttp.rule.reloaded.>`. `deleted: true` indicates the rule file was removed. |
|
||||
| `ttp.rule.state.{rule_id}` | `decnet.ttp.store` (FilesystemRuleStore + DatabaseRuleStore) + `POST /api/v1/ttp/rules/{rule_id}/state` | `{rule_id, state, set_by, auto_revert?}` — fired when a rule's *operational state* changes (operator hits disable/clip via the API, or an `expires_at` TTL fires and auto-reverts the state). `state ∈ {"enabled", "disabled", "clipped"}`. `auto_revert: true` flags TTL-driven reverts so dashboards can distinguish them from operator actions. Built via `topics.ttp_rule_state(rule_id)`; fleet subscribers use `ttp.rule.state.>`. |
|
||||
| `system.log` | _reserved_ | — |
|
||||
|
||||
Reference in New Issue
Block a user