From 2b0d418ba792e2c3eb4c9fcd5bf825acaf5f0131 Mon Sep 17 00:00:00 2001 From: Markus Graf Date: Wed, 25 Mar 2026 12:19:54 +0100 Subject: [PATCH] docs(01-02): complete capture package plan - Add 01-02-SUMMARY.md with capture package implementation details - Mark requirements CAPT-01, CAPT-02, CAPT-04 complete in REQUIREMENTS.md --- .planning/REQUIREMENTS.md | 12 +- .../01-02-SUMMARY.md | 126 ++++++++++++++++++ 2 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 .planning/phases/01-capture-and-classification/01-02-SUMMARY.md diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 76c4204..a63f703 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -9,10 +9,10 @@ Requirements for initial release. Each maps to roadmap phases. ### Packet Capture -- [ ] **CAPT-01**: User can specify network interface via `-i` flag -- [ ] **CAPT-02**: User can list available network interfaces via `--list-interfaces` +- [x] **CAPT-01**: User can specify network interface via `-i` flag +- [x] **CAPT-02**: User can list available network interfaces via `--list-interfaces` - [ ] **CAPT-03**: Capture runs until user presses Ctrl+C, then gracefully flushes and saves MP3 -- [ ] **CAPT-04**: User sees a clear actionable error message when lacking capture privileges (sudo/CAP_NET_RAW hint) +- [x] **CAPT-04**: User sees a clear actionable error message when lacking capture privileges (sudo/CAP_NET_RAW hint) - [ ] **CAPT-05**: User can filter captured traffic using BPF syntax via `--filter` flag - [ ] **CAPT-06**: User can sonify a pcap file instead of live traffic via `--read` flag @@ -66,10 +66,10 @@ Which phases cover which requirements. Updated during roadmap creation. | Requirement | Phase | Status | |-------------|-------|--------| -| CAPT-01 | Phase 1 | Pending | -| CAPT-02 | Phase 1 | Pending | +| CAPT-01 | Phase 1 | Complete | +| CAPT-02 | Phase 1 | Complete | | CAPT-03 | Phase 3 | Pending | -| CAPT-04 | Phase 1 | Pending | +| CAPT-04 | Phase 1 | Complete | | CAPT-05 | Phase 4 | Pending | | CAPT-06 | Phase 4 | Pending | | CLAS-01 | Phase 1 | Complete | diff --git a/.planning/phases/01-capture-and-classification/01-02-SUMMARY.md b/.planning/phases/01-capture-and-classification/01-02-SUMMARY.md new file mode 100644 index 0000000..db0a0b1 --- /dev/null +++ b/.planning/phases/01-capture-and-classification/01-02-SUMMARY.md @@ -0,0 +1,126 @@ +--- +phase: 01-capture-and-classification +plan: 02 +subsystem: capture +tags: [go-pcap, gopacket, packet-capture, privilege-handling, cgo-free-capture] + +# Dependency graph +requires: + - phase: 01-capture-and-classification/01-01 + provides: classify/types.go with TrafficClass, ClassifiedPacket, WindowSnapshot types +provides: + - capture package with OpenCapture, ListInterfaces, StartCapture functions + - Platform-specific privilege error messages (Linux setcap hint, macOS sudo hint) + - Channel-based packet source with 512-buffer and atomic drop counter +affects: + - cmd/root.go (will call StartCapture and ListInterfaces) + - Phase 2 audio synthesis (receives packets from StartCapture channel) + +# Tech tracking +tech-stack: + added: + - github.com/packetcap/go-pcap v0.0.0-20251215 (pure-Go live capture) + - github.com/sirupsen/logrus v1.9.3 (transitive dep of go-pcap) + patterns: + - TDD RED/GREEN: write failing tests before implementation + - net.Interfaces() for listing (not pcap.FindAllDevs which go-pcap lacks) + - Non-blocking channel send with atomic.AddInt64 drop counter + - Dynamic link type via layers.LinkType(handle.LinkType()) + - runtime.GOOS switch for platform-specific error messages + +key-files: + created: + - capture/capture.go + - capture/capture_test.go + modified: + - go.mod (added go-pcap) + - go.sum (added go-pcap + logrus hashes) + +key-decisions: + - "Use net.Interfaces() for interface listing — go-pcap has no FindAllDevs equivalent" + - "StartCapture uses 512-buffered channel with atomic drop counter to avoid backpressure blocking capture goroutine" + - "Dynamic link type detection via handle.LinkType() — not hardcoded LinkTypeEthernet" + - "Permission errors wrap platform-specific messages: Linux shows setcap, macOS shows sudo" + +patterns-established: + - "Privilege detection: check error string for 'permission denied', 'operation not permitted', 'pcap_create'" + - "Platform branching: switch runtime.GOOS with linux case explicit, default for darwin/others" + - "Packet pipeline: goroutine reads from packetSource, writes to buffered channel with non-blocking select" + +requirements-completed: [CAPT-01, CAPT-02, CAPT-04] + +# Metrics +duration: 2min +completed: 2026-03-25 +--- + +# Phase 01 Plan 02: Capture Package Summary + +**Live packet capture via packetcap/go-pcap with ListInterfaces (stdlib-only), OpenCapture with platform-specific CAP_NET_RAW error messages, and non-blocking StartCapture channel pipeline with atomic drop counter** + +## Performance + +- **Duration:** ~2 min +- **Started:** 2026-03-25T11:15:54Z +- **Completed:** 2026-03-25T11:17:34Z +- **Tasks:** 1 (TDD: 2 commits — RED test + GREEN implementation) +- **Files modified:** 4 (capture.go, capture_test.go, go.mod, go.sum) + +## Accomplishments + +- capture package implements all 3 required exports: OpenCapture, ListInterfaces, StartCapture +- ListInterfaces uses net.Interfaces() — no CGo or pcap dependency for interface enumeration (CAPT-02) +- Permission error detection covers all documented error strings; returns platform-specific sudo/setcap hints (CAPT-04, D-05) +- StartCapture goroutine uses buffered channel (512) + atomic drop counter — backpressure-safe for high-traffic interfaces + +## Task Commits + +Each task was committed atomically: + +1. **Task 1 RED: failing tests for capture package** - `1eb1dd4` (test) +2. **Task 1 GREEN: implement capture package + add go-pcap** - `9446dd5` (feat) + +_Note: TDD task has two commits (test RED then implementation GREEN)_ + +## Files Created/Modified + +- `capture/capture.go` - ListInterfaces, OpenCapture, StartCapture, isPermissionError, permissionErrorMsg +- `capture/capture_test.go` - 5 tests: TestListInterfaces, TestListInterfacesFormat, TestIsPermissionError, TestIsPermissionErrorNegative, TestPermissionErrorMsg (94 lines) +- `go.mod` - Added github.com/packetcap/go-pcap v0.0.0-20251215 +- `go.sum` - Added go-pcap + logrus hashes + +## Decisions Made + +- net.Interfaces() for interface listing: go-pcap has no FindAllDevs, stdlib net package covers this without privileges +- Dynamic link type: capture uses `layers.LinkType(handle.LinkType())` rather than hardcoded `LinkTypeEthernet` — handles lo (null/loopback) and tunnel interfaces +- Non-blocking packet send: `select { case packets <- pkt: default: atomic.AddInt64(&droppedPackets, 1) }` prevents slow downstream consumer from blocking capture goroutine + +## Deviations from Plan + +None — plan executed exactly as written. go-pcap version `v0.0.0-20251215121130-f2cf9f991e7c` resolved correctly (RESEARCH.md listed the date prefix `v0.0.0-20251215` which matched). + +## Issues Encountered + +None — all tests passed on first GREEN run. + +## User Setup Required + +None — no external service configuration required. Capture at runtime requires root or CAP_NET_RAW, but build is CGo-free for the capture layer. + +## Next Phase Readiness + +- capture package is ready for consumption by cmd/root.go (Plan 03/04) +- StartCapture channel output type is `gopacket.Packet` — classifier (classify package from Plan 01) accepts these via layer assertions +- Dropped packets counter (`*int64`) available for capture-end summary statistics + +--- +*Phase: 01-capture-and-classification* +*Completed: 2026-03-25* + +## Self-Check: PASSED + +- capture/capture.go: FOUND +- capture/capture_test.go: FOUND +- 01-02-SUMMARY.md: FOUND +- commit 1eb1dd4 (test RED): FOUND +- commit 9446dd5 (feat GREEN): FOUND