Inspiration
Supply-chain attacks keep landing as "a Node process read your secrets." That's useless when the process has 800 dependencies. After Shai-Hulud / event-stream style incidents, I wanted the kernel answer with a package name on it — not another SCA scanner that only looks at lockfiles.
I build Goodman at Heisenbug. The bet is simple: if you can attribute the syscall to package@version, you can learn a baseline and notice when that package starts doing something new.
What it does
Goodman watches Node/Python processes with CO-RE eBPF (open/openat/connect/execve + optional LSM block, off by default). It captures the user stack, resolves it through V8/CPython perf maps and /proc/<pid>/maps, and maps the deepest node_modules/ or site-packages/ frame to a real version from package.json / dist-info.
Then it:
- fingerprints behavior per
(service, package, version)— e.g.READ …/.aws/credentials,CONNECT 169.254.169.254:80 - diffs live behavior against the learned baseline
- fires always-on high-risk rules even during the learning window (no "poison the baseline" gap)
- serves alerts over REST + SSE, Prometheus metrics, and an embedded dashboard
Wrong attribution is worse than unknown — we return <unknown> rather than guess.
OpenClaw angle (demo + next): OpenClaw is a Node process; ClawHub skills are npm packages. Tier-1 npm attribution via V8 perf maps is exactly the stack for that. Demo will show which skill made a given syscall — not "something read a cred file," but skill-xyz@1.2.3 did. Goal: one-command integrate Goodman with OpenClaw, plus the same /v1/* API for SIEMs / OpenClaw / anything else without needing our UI.
How we built it
Kernel side is CO-RE C (bpf/goodman.bpf.c) with a strict byte-layout contract against Go RawEvent — silent struct drift = garbage attribution, so we lock that with offset tests.
User space is Go:
internal/loader— load/attach/ringbuf (never block the reader)internal/attribute— maps + perf-map resolve → package@version + canonical behaviorinternal/fingerprint+internal/diff— baselines and config-driven high-risk rulesinternal/api— collector, SSE, embedded React dashboard- Helm/Docker for K8s; SQLite + Postgres on one store path
I used Codex (GPT-5.6) throughout the build week — not just scaffolding. Concrete places it helped: keeping C/Go event layout in sync, attribution edge cases (container /proc/<pid>/root perf maps), rule/diff behavior, Helm/auth surfaces, dashboard wiring to live /v1/*, and tightening README + demo scripts so judges can run make demo without root. I still reviewed every change; the hard invariant (never misattribute) stayed mine.
Verification without root: make test, make smoke, make replay (real npm attack behaviors). Live eBPF path: sudo make e2e on a real kernel.
Challenges we ran into
- Attribution is the product. V8 JIT frames, container mount namespaces, relative paths +
dirfd, and "never guess a package name" fought us more than the eBPF boilerplate. - Ringbuf must stay lossy-fast. Anything that blocks the sensor hot path drops events — batching/spool to the collector had to stay boring.
- LSM enforcement is easy to get wrong. Fail-open, off by default, literal deny keys only, and LSM attach failures must not tear down detection.
- Demo vs reality. Judges often can't load BPF in a sandbox, so we invested in
make demo/ smoke / replay so the full collector→alert path is provable without root.
Accomplishments that we're proud of
- End-to-end path that actually attributes to
package@version, not just "node did a thing" - Always-on high-risk rules during learning (closes the baseline-poisoning window)
- Replay corpus for real incidents (event-stream, eslint-scope, ua-parser-js, node-ipc, etc.)
- Production-shaped deploy: Helm, auth classes, SSE dashboard, optional block mode
- API that any SIEM / agent runtime can consume without our UI
What we learned
Kernel visibility without package identity is half a product. Stack → path → package.json sounds small; it's most of the work. Also: Codex is strongest when you keep it on the invariant loop (layout tests, attribution fixtures, smoke) instead of "generate me an app."
What's next for Goodman
- OpenClaw / ClawHub demo: watch the OpenClaw Node process, attribute syscalls to specific skills, show that in the walkthrough
- One-command OpenClaw integrate (install/config so Goodman attaches to the OpenClaw runtime with minimal friction)
- SIEM-first export: keep leaning on
/v1/alerts,/v1/stream, fingerprints export — same data, any consumer - Broader skill/runtime coverage and tighter pilot packaging for teams running agent stacks in K8s
Built With
- co-re
- codex
- ebpf
- go
- gpt-5.6
- helm
- kubernetes
- libbpf
- linux
- node.js
- npm
- openclaw
- postgresql
- prometheus
- pypi
- python
- react
- rest-api
- sqlite
- typescript
- v8
- vite
Log in or sign up for Devpost to join the conversation.