chore(1.2): remove per-worker unit templates superseded by consolidation

The batch/cpu supervisor groups + heavy fleet replace 10 per-worker units
(reconciler/enrich/orchestrator/mutator/clusterer/campaign-clusterer/
attribution/reuse-correlator/profiler/ttp). Removed their deploy/*.service.j2
templates and rewired decnet.target to the 3 consolidated units. Dropped
test_orchestrator_unit.py (tested a removed unit). CLI commands (decnet ttp,
mutate, …) stay for manual runs; new units' Conflicts= still name the old
units defensively for hosts mid-migration.
This commit is contained in:
2026-06-18 19:42:51 -04:00
parent 7b0ff127c3
commit beaa604811
12 changed files with 7 additions and 626 deletions

View File

@@ -1,45 +0,0 @@
[Unit]
Description=DECNET Attribution Engine v0 (per-(identity, primitive) state machine)
Documentation=https://git.resacachile.cl/anti/DECNET/wiki/Workers#attribution
After=network-online.target decnet-bus.service
Wants=network-online.target decnet-bus.service
[Service]
Type=simple
User={{ user }}
Group={{ group }}
WorkingDirectory={{ install_dir }}
EnvironmentFile=-{{ install_dir }}/.env.local
Environment=DECNET_SYSTEM_LOGS=/var/log/decnet/decnet.attribution.log
# Subscribes to attacker.observation.> and, for each event, ensures a
# stub AttackerIdentity row, runs the per-ValueKind merger over the
# full identity-keyed observation series, upserts the derived state in
# attribution_state, and publishes attribution.profile.state_changed
# only on transition. Periodic tick (default 60s) fires
# attribution.profile.multi_actor_suspected when >= 2 primitives flag
# the same identity. Closes DEBT-051.
ExecStart={{ venv_dir }}/bin/decnet attribution
StandardOutput=append:/var/log/decnet/decnet.attribution.log
StandardError=append:/var/log/decnet/decnet.attribution.log
CapabilityBoundingSet=
AmbientCapabilities=
# Security Hardening
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
LockPersonality=yes
ReadWritePaths={{ install_dir }} /var/log/decnet
Restart=on-failure
RestartSec=5
TimeoutStopSec=15
[Install]
WantedBy=multi-user.target

View File

@@ -1,52 +0,0 @@
[Unit]
Description=DECNET Campaign Clusterer (identities → campaigns / operations)
Documentation=https://git.resacachile.cl/anti/DECNET/wiki/Workers#campaign-clusterer
After=network-online.target decnet-bus.service decnet-clusterer.service
Wants=network-online.target decnet-bus.service decnet-clusterer.service
[Service]
Type=simple
User={{ user }}
Group={{ group }}
WorkingDirectory={{ install_dir }}
EnvironmentFile=-{{ install_dir }}/.env.local
Environment=DECNET_SYSTEM_LOGS=/var/log/decnet/decnet.campaign-clusterer.log
# Subscribes to identity.>; falls back to a 60s slow-tick poll when
# the bus is idle or unavailable. Reads AttackerIdentity rows,
# projects them into the campaign-level similarity graph
# (phase-handoff / shared-infra / temporal overlap / cohort), runs
# union-find, writes campaigns rows + sets
# attacker_identities.campaign_id, and publishes campaign.formed /
# campaign.identity.assigned / campaign.merged / campaign.unmerged
# plus the cross-family identity.campaign.assigned for identity-side
# subscribers.
#
# Master-only: gated via MASTER_ONLY_COMMANDS in decnet/cli/gating.py.
# Sits one layer above decnet-clusterer (the After=/Wants= ensures the
# identity layer is up first; the campaign clusterer then wakes on
# identity.> events fired by it).
ExecStart={{ venv_dir }}/bin/decnet campaign-clusterer
StandardOutput=append:/var/log/decnet/decnet.campaign-clusterer.log
StandardError=append:/var/log/decnet/decnet.campaign-clusterer.log
CapabilityBoundingSet=
AmbientCapabilities=
# Security Hardening
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
LockPersonality=yes
ReadWritePaths={{ install_dir }} /var/log/decnet
Restart=on-failure
RestartSec=5
TimeoutStopSec=15
[Install]
WantedBy=multi-user.target

View File

@@ -1,47 +0,0 @@
[Unit]
Description=DECNET Identity Clusterer (per-IP observations → identities)
Documentation=https://git.resacachile.cl/anti/DECNET/wiki/Workers#identity-clusterer
After=network-online.target decnet-bus.service
Wants=network-online.target decnet-bus.service
[Service]
Type=simple
User={{ user }}
Group={{ group }}
WorkingDirectory={{ install_dir }}
EnvironmentFile=-{{ install_dir }}/.env.local
Environment=DECNET_SYSTEM_LOGS=/var/log/decnet/decnet.clusterer.log
# Subscribes to attacker.observed and attacker.scored; falls back to a
# 60s slow-tick poll when the bus is idle or unavailable. Reads
# Attacker rows, projects per-IP observations into the similarity
# graph (JA3 / HASSH / payload-hash / C2-endpoint), runs union-find,
# writes attacker_identities rows + sets attackers.identity_id, and
# publishes identity.formed / identity.observation.linked /
# identity.merged / identity.unmerged.
#
# Master-only: gated via MASTER_ONLY_COMMANDS in decnet/cli/gating.py.
ExecStart={{ venv_dir }}/bin/decnet clusterer
StandardOutput=append:/var/log/decnet/decnet.clusterer.log
StandardError=append:/var/log/decnet/decnet.clusterer.log
CapabilityBoundingSet=
AmbientCapabilities=
# Security Hardening
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
LockPersonality=yes
ReadWritePaths={{ install_dir }} /var/log/decnet
Restart=on-failure
RestartSec=5
TimeoutStopSec=15
[Install]
WantedBy=multi-user.target

View File

@@ -1,47 +0,0 @@
[Unit]
Description=DECNET Threat-Intel Enrichment (GreyNoise + AbuseIPDB + abuse.ch)
Documentation=https://git.resacachile.cl/anti/DECNET/wiki/Workers#intel-enrichment
After=network-online.target decnet-bus.service
Wants=network-online.target decnet-bus.service
[Service]
Type=simple
User={{ user }}
Group={{ group }}
WorkingDirectory={{ install_dir }}
EnvironmentFile=-{{ install_dir }}/.env.local
Environment=DECNET_SYSTEM_LOGS=/var/log/decnet/decnet.enrich.log
# Subscribes to attacker.observed and attacker.scored; falls back to a 60s
# slow-tick poll when the bus is idle or unavailable. Per attacker IP fans
# out across the configured intel providers, writes the aggregate verdict
# to attacker_intel, and publishes attacker.intel.enriched.
#
# Free-tier API keys are read from .env.local:
# DECNET_GREYNOISE_API_KEY= (optional, lifts rate limit)
# DECNET_ABUSEIPDB_API_KEY= (required for AbuseIPDB lookups)
# DECNET_THREATFOX_API_KEY= (optional, lifts rate limit)
ExecStart={{ venv_dir }}/bin/decnet enrich
StandardOutput=append:/var/log/decnet/decnet.enrich.log
StandardError=append:/var/log/decnet/decnet.enrich.log
CapabilityBoundingSet=
AmbientCapabilities=
# Security Hardening
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
LockPersonality=yes
ReadWritePaths={{ install_dir }} /var/log/decnet
Restart=on-failure
RestartSec=5
TimeoutStopSec=15
[Install]
WantedBy=multi-user.target

View File

@@ -1,41 +0,0 @@
[Unit]
Description=DECNET Mutator (runtime fleet mutation watch loop)
Documentation=https://git.resacachile.cl/anti/DECNET/wiki/Workers#mutator
After=network-online.target docker.service decnet-bus.service
Wants=network-online.target decnet-bus.service
Requires=docker.service
[Service]
Type=simple
User={{ user }}
Group={{ group }}
# Mutator recomposes decky services via docker compose.
SupplementaryGroups=docker
WorkingDirectory={{ install_dir }}
EnvironmentFile=-{{ install_dir }}/.env.local
Environment=DECNET_SYSTEM_LOGS=/var/log/decnet/decnet.mutator.log
ExecStart={{ venv_dir }}/bin/decnet mutate --watch
StandardOutput=append:/var/log/decnet/decnet.mutator.log
StandardError=append:/var/log/decnet/decnet.mutator.log
CapabilityBoundingSet=
AmbientCapabilities=
# Security Hardening
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
LockPersonality=yes
ReadWritePaths={{ install_dir }} /var/log/decnet
Restart=on-failure
RestartSec=5
TimeoutStopSec=15
[Install]
WantedBy=multi-user.target

View File

@@ -1,50 +0,0 @@
[Unit]
Description=DECNET Orchestrator (synthetic life-injection — inter-decky traffic, file plants, email drops)
Documentation=https://git.resacachile.cl/anti/DECNET/wiki/Workers#orchestrator
After=network-online.target decnet-bus.service
Wants=network-online.target decnet-bus.service
[Service]
Type=simple
User={{ user }}
Group={{ group }}
WorkingDirectory={{ install_dir }}
EnvironmentFile=-{{ install_dir }}/.env.local
Environment=DECNET_SYSTEM_LOGS=/var/log/decnet/decnet.orchestrator.log
# Realism content engine — LLM + persona-pool config used by the
# email + (post-stage-6) file-class enrichment paths. See
# decnet/realism/llm/factory.py and decnet/realism/personas_pool.py.
Environment=DECNET_REALISM_LLM=ollama
Environment=DECNET_REALISM_MODEL=llama3.1
Environment=DECNET_REALISM_TIMEOUT=60
Environment=DECNET_REALISM_PERSONAS=/etc/decnet/email_personas.json
ExecStart={{ venv_dir }}/bin/decnet orchestrate
StandardOutput=append:/var/log/decnet/decnet.orchestrator.log
StandardError=append:/var/log/decnet/decnet.orchestrator.log
# The orchestrator drives `docker exec` against decky containers, so it
# needs membership in the docker group. It does NOT bind to the network,
# launch new containers, or write outside its own logs and install dir.
SupplementaryGroups=docker
CapabilityBoundingSet=
AmbientCapabilities=
# Security Hardening
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
LockPersonality=yes
ReadWritePaths={{ install_dir }} /var/log/decnet
Restart=on-failure
RestartSec=5
TimeoutStopSec=15
[Install]
WantedBy=multi-user.target

View File

@@ -1,38 +0,0 @@
[Unit]
Description=DECNET Profiler (attacker profiling and scoring)
Documentation=https://git.resacachile.cl/anti/DECNET/wiki/Workers#profiler
After=network-online.target decnet-bus.service
Wants=network-online.target decnet-bus.service
[Service]
Type=simple
User={{ user }}
Group={{ group }}
WorkingDirectory={{ install_dir }}
EnvironmentFile=-{{ install_dir }}/.env.local
Environment=DECNET_SYSTEM_LOGS=/var/log/decnet/decnet.profiler.log
ExecStart={{ venv_dir }}/bin/decnet profiler
StandardOutput=append:/var/log/decnet/decnet.profiler.log
StandardError=append:/var/log/decnet/decnet.profiler.log
CapabilityBoundingSet=
AmbientCapabilities=
# Security Hardening
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
LockPersonality=yes
ReadWritePaths={{ install_dir }} /var/log/decnet
Restart=on-failure
RestartSec=5
TimeoutStopSec=15
[Install]
WantedBy=multi-user.target

View File

@@ -1,47 +0,0 @@
[Unit]
Description=DECNET Fleet Reconciler (converges decnet-state.json ↔ fleet_deckies DB ↔ docker)
Documentation=https://git.resacachile.cl/anti/DECNET/wiki/Workers#reconciler
After=network-online.target decnet-bus.service
Wants=network-online.target decnet-bus.service
[Service]
Type=simple
User={{ user }}
Group={{ group }}
WorkingDirectory={{ install_dir }}
EnvironmentFile=-{{ install_dir }}/.env.local
Environment=DECNET_SYSTEM_LOGS=/var/log/decnet/decnet.reconciler.log
ExecStart={{ venv_dir }}/bin/decnet reconcile
StandardOutput=append:/var/log/decnet/decnet.reconciler.log
StandardError=append:/var/log/decnet/decnet.reconciler.log
# The reconciler queries the docker daemon (via `docker.from_env()`) to
# observe per-container state. Membership in the docker group lets it
# read /var/run/docker.sock without root. It does NOT exec into
# containers, bind to the network, or spawn new containers.
SupplementaryGroups=docker
CapabilityBoundingSet=
AmbientCapabilities=
# Security Hardening
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
LockPersonality=yes
# Read-only access to /var/lib/decnet so we can read decnet-state.json.
# Read-write access only to install_dir + log dir.
ReadOnlyPaths=/var/lib/decnet
ReadWritePaths={{ install_dir }} /var/log/decnet
Restart=on-failure
RestartSec=5
TimeoutStopSec=15
[Install]
WantedBy=multi-user.target

View File

@@ -1,41 +0,0 @@
[Unit]
Description=DECNET Credential-Reuse Correlator (cross-target secret-reuse detection)
Documentation=https://git.resacachile.cl/anti/DECNET/wiki/Workers#reuse-correlator
After=network-online.target decnet-bus.service
Wants=network-online.target decnet-bus.service
[Service]
Type=simple
User={{ user }}
Group={{ group }}
WorkingDirectory={{ install_dir }}
EnvironmentFile=-{{ install_dir }}/.env.local
Environment=DECNET_SYSTEM_LOGS=/var/log/decnet/decnet.reuse-correlator.log
# Subscribes to credential.captured and attacker.observed; falls back to
# a 60s slow-tick poll when the bus is idle or unavailable. Publishes
# credential.reuse.detected once per new/grown finding.
ExecStart={{ venv_dir }}/bin/decnet reuse-correlate
StandardOutput=append:/var/log/decnet/decnet.reuse-correlator.log
StandardError=append:/var/log/decnet/decnet.reuse-correlator.log
CapabilityBoundingSet=
AmbientCapabilities=
# Security Hardening
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
LockPersonality=yes
ReadWritePaths={{ install_dir }} /var/log/decnet
Restart=on-failure
RestartSec=5
TimeoutStopSec=15
[Install]
WantedBy=multi-user.target

View File

@@ -1,57 +0,0 @@
[Unit]
Description=DECNET TTP Tagger (MITRE ATT&CK technique tagging)
Documentation=https://git.resacachile.cl/anti/DECNET/wiki/Workers#ttp-tagger
After=network-online.target decnet-bus.service decnet-clusterer.service decnet-enrich.service decnet-reuse-correlator.service
Wants=network-online.target decnet-bus.service
[Service]
Type=simple
User={{ user }}
Group={{ group }}
WorkingDirectory={{ install_dir }}
EnvironmentFile=-{{ install_dir }}/.env.local
Environment=DECNET_SYSTEM_LOGS=/var/log/decnet/decnet.ttp.log
# Subscribes to attacker.session.ended (primary), attacker.observed,
# attacker.intel.enriched, identity.formed, identity.merged,
# credential.reuse.detected, email.received, and canary.> ; falls back
# to a 60s slow-tick poll when the bus is idle or unavailable. Each
# event is dispatched through the CompositeTagger (RuleEngine +
# Behavioral / Intel / Email / CanaryFingerprint / Identity /
# Credential lifters), persisted via the idempotent INSERT OR IGNORE
# repo write, and ttp.tagged + ttp.rule.fired.<technique_id> are
# published only when the insert returned a non-zero rowcount
# (loop-prevention invariant — see TTP_TAGGING.md §"Bus topics").
#
# Master-only: gated via MASTER_ONLY_COMMANDS in decnet/cli/gating.py.
# Sits one layer above the identity / intel / reuse-correlator
# workers — the After= dependencies ensure their bus topics are live
# before the TTP worker subscribes.
ExecStart={{ venv_dir }}/bin/decnet ttp
StandardOutput=append:/var/log/decnet/decnet.ttp.log
StandardError=append:/var/log/decnet/decnet.ttp.log
CapabilityBoundingSet=
AmbientCapabilities=
# Security Hardening
NoNewPrivileges=yes
ProtectSystem=full
# Dev installs under /home need ProtectHome=read-only (the worker
# reads ./rules/ttp/ from the project root, which lives under /home
# on dev boxes — read-only suffices because the FilesystemRuleStore
# only reads YAMLs, never writes).
ProtectHome=read-only
PrivateTmp=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
LockPersonality=yes
ReadWritePaths={{ install_dir }} /var/log/decnet
Restart=on-failure
RestartSec=5
TimeoutStopSec=15
[Install]
WantedBy=multi-user.target

View File

@@ -5,23 +5,21 @@ Documentation=https://git.resacachile.cl/anti/DECNET/wiki/Workers
# heartbeats to it), then the API + data-plane workers. systemd resolves the
# actual ordering via each unit's own After=/Wants= on decnet-bus.service —
# this target is a convenience grouping, not an ordering primitive.
# Consolidated since 1.1/1.2: the batch (reconcile/enrich/orchestrate/mutate)
# and cpu (clusterer/campaign-clusterer/attribution/reuse-correlate) supervisor
# groups and the heavy (profiler/ttp) prefork fleet replace their per-worker
# units. The standalone workers below kept their own units.
Wants=decnet-bus.service \
decnet-api.service \
decnet-web.service \
decnet-collector.service \
decnet-profiler.service \
decnet-sniffer.service \
decnet-prober.service \
decnet-mutator.service \
decnet-reconciler.service \
decnet-reuse-correlator.service \
decnet-enrich.service \
decnet-clusterer.service \
decnet-campaign-clusterer.service \
decnet-ttp.service \
decnet-webhook.service \
decnet-canary.service \
decnet-orchestrator.service
decnet-supervise-batch.service \
decnet-supervise-cpu.service \
decnet-fleet-heavy.service
After=decnet-bus.service
[Install]