--- phase: 01-capture-and-classification plan: 01 type: execute wave: 1 depends_on: [] files_modified: - go.mod - go.sum - classify/rules.go - classify/classifier.go - classify/classifier_test.go - classify/types.go autonomous: true requirements: [CLAS-01] must_haves: truths: - "Each of 10+ protocol classes (ICMP, DNS, HTTPS, HTTP, SSH, SMTP, NTP, DHCP, other-TCP, other-UDP, unknown) is correctly identified from packet headers" - "Classification rules are stored in a config-driven slice/struct, not hardcoded switch statements" - "Unrecognized traffic is grouped as 'unknown' class" artifacts: - path: "classify/types.go" provides: "TrafficClass type, ClassifiedPacket struct, WindowSnapshot struct" exports: ["TrafficClass", "ClassifiedPacket", "WindowSnapshot"] - path: "classify/rules.go" provides: "Rule struct and DefaultRules slice with 10+ protocol rules" contains: "DefaultRules" - path: "classify/classifier.go" provides: "Classify function that matches packets against rule table" exports: ["Classify", "NewClassifier"] - path: "classify/classifier_test.go" provides: "Unit tests for all 10+ protocol classes" min_lines: 80 key_links: - from: "classify/classifier.go" to: "classify/rules.go" via: "Classifier uses DefaultRules slice" pattern: "DefaultRules" - from: "classify/classifier.go" to: "classify/types.go" via: "Returns TrafficClass from Classify function" pattern: "TrafficClass" --- Bootstrap Go project and implement the protocol classification engine with full test coverage. Purpose: Establish the Go module, core types shared across all packages, and the config-driven classifier that maps packets to one of 10+ traffic classes. This is the foundation every other plan depends on. Output: Compiling Go module with classify package, passing unit tests for all protocol classes. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/01-capture-and-classification/01-CONTEXT.md @.planning/phases/01-capture-and-classification/01-RESEARCH.md Task 1: Install Go 1.24, initialize module, and create shared types go.mod, classify/types.go .planning/phases/01-capture-and-classification/01-RESEARCH.md 1. Install Go 1.24+ from https://go.dev/dl/ (NOT apt which gives 1.22). Download the linux-arm64 tarball: - wget https://go.dev/dl/go1.24.1.linux-arm64.tar.gz - sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.24.1.linux-arm64.tar.gz - Ensure /usr/local/go/bin is in PATH (export PATH=$PATH:/usr/local/go/bin) - Verify: `go version` must show go1.24.x 2. Initialize the Go module: - cd /home/dev/workspace/yoloyolo - go mod init github.com/netsynth/netsynth - mkdir -p classify capture aggregate cmd/netsynth 3. Create classify/types.go with the following types (these are the contracts for all packages): ```go package classify // TrafficClass represents a classified network traffic category. type TrafficClass string const ( ClassICMP TrafficClass = "ICMP" ClassDNS TrafficClass = "DNS" ClassHTTPS TrafficClass = "HTTPS" ClassHTTP TrafficClass = "HTTP" ClassSSH TrafficClass = "SSH" ClassSMTP TrafficClass = "SMTP" ClassNTP TrafficClass = "NTP" ClassDHCP TrafficClass = "DHCP" ClassOtherTCP TrafficClass = "other-TCP" ClassOtherUDP TrafficClass = "other-UDP" ClassUnknown TrafficClass = "unknown" ) // AllClasses returns all known traffic classes in display order. func AllClasses() []TrafficClass { return []TrafficClass{ ClassICMP, ClassDNS, ClassHTTPS, ClassHTTP, ClassSSH, ClassSMTP, ClassNTP, ClassDHCP, ClassOtherTCP, ClassOtherUDP, ClassUnknown, } } // ClassifiedPacket pairs a raw packet's classification result with metadata. type ClassifiedPacket struct { Class TrafficClass SrcPort uint16 DstPort uint16 Protocol string // "tcp", "udp", "icmp" Length int } // WindowSnapshot holds aggregated packet counts for a time window. // This is the contract between the aggregation stage and future audio synthesis (Phase 2). type WindowSnapshot struct { Counts map[TrafficClass]int64 TotalPackets int64 WindowIndex int } ``` 4. Fetch Phase 1 dependencies: - go get github.com/gopacket/gopacket@v1.5.0 - go get github.com/packetcap/go-pcap@latest - go get github.com/spf13/cobra@v1.10.2 - go mod tidy cd /home/dev/workspace/yoloyolo && go version | grep -q "go1.24" && go build ./classify/... - `go version` output contains "go1.24" - go.mod contains `module github.com/netsynth/netsynth` - go.mod contains `github.com/gopacket/gopacket` - go.mod contains `github.com/packetcap/go-pcap` - go.mod contains `github.com/spf13/cobra` - classify/types.go contains `type TrafficClass string` - classify/types.go contains `ClassICMP`, `ClassDNS`, `ClassHTTPS`, `ClassHTTP`, `ClassSSH`, `ClassSMTP`, `ClassNTP`, `ClassDHCP`, `ClassOtherTCP`, `ClassOtherUDP`, `ClassUnknown` - classify/types.go contains `type ClassifiedPacket struct` - classify/types.go contains `type WindowSnapshot struct` - `go build ./classify/...` exits 0 Go 1.24 installed, module initialized with all Phase 1 deps, classify/types.go compiles with all 11 traffic classes and shared types. Task 2: Implement config-driven classifier with tests for all protocol classes classify/rules.go, classify/classifier.go, classify/classifier_test.go classify/types.go .planning/phases/01-capture-and-classification/01-RESEARCH.md - TestClassifyICMP: ICMP packet (no port) -> ClassICMP - TestClassifyDNS_UDP: UDP dst port 53 -> ClassDNS - TestClassifyDNS_TCP: TCP dst port 53 -> ClassDNS - TestClassifyHTTPS: TCP dst port 443 -> ClassHTTPS - TestClassifyHTTP: TCP dst port 80 -> ClassHTTP - TestClassifySSH: TCP dst port 22 -> ClassSSH - TestClassifySMTP: TCP dst port 25 -> ClassSMTP - TestClassifyNTP: UDP dst port 123 -> ClassNTP - TestClassifyDHCP: UDP dst port 67 or 68 -> ClassDHCP - TestClassifyOtherTCP: TCP dst port 8080 (unmatched) -> ClassOtherTCP - TestClassifyOtherUDP: UDP dst port 9999 (unmatched) -> ClassOtherUDP - TestClassifyUnknown: Packet with no TCP/UDP/ICMP layer -> ClassUnknown - TestRulesAreOrderDependent: First matching rule wins 1. Create classify/rules.go with Rule struct and DefaultRules per D-01 and D-02: ```go package classify // Rule defines a classification rule. Protocol is "tcp", "udp", or "icmp". // DstPort 0 means match any port for this protocol. // Rules are evaluated in order; first match wins. type Rule struct { Protocol string DstPort uint16 Class TrafficClass } // DefaultRules is the D-01 protocol map — ordered, first-match-wins. // Per D-02: config-driven slice, not a switch statement. var DefaultRules = []Rule{ {Protocol: "icmp", DstPort: 0, Class: ClassICMP}, {Protocol: "udp", DstPort: 53, Class: ClassDNS}, {Protocol: "tcp", DstPort: 53, Class: ClassDNS}, {Protocol: "tcp", DstPort: 443, Class: ClassHTTPS}, {Protocol: "tcp", DstPort: 80, Class: ClassHTTP}, {Protocol: "tcp", DstPort: 22, Class: ClassSSH}, {Protocol: "tcp", DstPort: 25, Class: ClassSMTP}, {Protocol: "udp", DstPort: 123, Class: ClassNTP}, {Protocol: "udp", DstPort: 67, Class: ClassDHCP}, {Protocol: "udp", DstPort: 68, Class: ClassDHCP}, // Catch-alls (must be last): {Protocol: "tcp", DstPort: 0, Class: ClassOtherTCP}, {Protocol: "udp", DstPort: 0, Class: ClassOtherUDP}, } ``` 2. Create classify/classifier.go: ```go package classify import ( "github.com/gopacket/gopacket" "github.com/gopacket/gopacket/layers" ) // Classifier classifies packets against a rule table. type Classifier struct { rules []Rule } // NewClassifier creates a classifier with the given rules. func NewClassifier(rules []Rule) *Classifier { return &Classifier{rules: rules} } // Classify inspects a gopacket.Packet and returns a ClassifiedPacket. // It checks ICMP first, then extracts TCP/UDP port info and matches against rules. // Per D-03: returns ClassUnknown if no rule matches. func (c *Classifier) Classify(pkt gopacket.Packet) ClassifiedPacket { result := ClassifiedPacket{ Class: ClassUnknown, Length: len(pkt.Data()), } // Check ICMP if pkt.Layer(layers.LayerTypeICMPv4) != nil || pkt.Layer(layers.LayerTypeICMPv6) != nil { result.Protocol = "icmp" // Match against rules for _, rule := range c.rules { if rule.Protocol == "icmp" { result.Class = rule.Class return result } } return result } // Check TCP if tcpLayer := pkt.Layer(layers.LayerTypeTCP); tcpLayer != nil { tcp := tcpLayer.(*layers.TCP) result.Protocol = "tcp" result.SrcPort = uint16(tcp.SrcPort) result.DstPort = uint16(tcp.DstPort) for _, rule := range c.rules { if rule.Protocol == "tcp" && (rule.DstPort == 0 || rule.DstPort == result.DstPort) { result.Class = rule.Class return result } } return result } // Check UDP if udpLayer := pkt.Layer(layers.LayerTypeUDP); udpLayer != nil { udp := udpLayer.(*layers.UDP) result.Protocol = "udp" result.SrcPort = uint16(udp.SrcPort) result.DstPort = uint16(udp.DstPort) for _, rule := range c.rules { if rule.Protocol == "udp" && (rule.DstPort == 0 || rule.DstPort == result.DstPort) { result.Class = rule.Class return result } } return result } // No recognized transport layer -> ClassUnknown per D-03 return result } ``` 3. Write tests in classify/classifier_test.go using gopacket test packet construction: - Build synthetic packets using gopacket.SerializeBuffer with layers.Ethernet + layers.IPv4 + layers.TCP/UDP/ICMP headers - Test each of the 10+ protocol classes plus ClassUnknown - Test that rule order matters (first match wins) Run RED phase first: write all tests, verify they fail (classifier not yet imported or logic wrong). Then GREEN: implement until all pass. cd /home/dev/workspace/yoloyolo && go test -v -count=1 ./classify/... - classify/rules.go contains `type Rule struct` - classify/rules.go contains `var DefaultRules = []Rule{` - classify/rules.go contains at least 12 Rule entries (10 specific + 2 catch-all) - classify/classifier.go contains `func (c *Classifier) Classify(pkt gopacket.Packet) ClassifiedPacket` - classify/classifier.go contains `func NewClassifier(rules []Rule) *Classifier` - classify/classifier.go does NOT contain `switch` keyword (per D-02) - classify/classifier_test.go contains `TestClassify` with subtests for ICMP, DNS, HTTPS, HTTP, SSH, SMTP, NTP, DHCP, OtherTCP, OtherUDP, Unknown - `go test -v -count=1 ./classify/...` exits 0 with all subtests passing Classifier passes tests for all 11 traffic classes. Rules stored as config-driven slice (D-02). First-match-wins ordering verified. No switch statements in classification logic. - `go version` shows 1.24+ - `go build ./classify/...` compiles without errors - `go test -v -count=1 ./classify/...` all tests pass - `go vet ./classify/...` no issues - No import of `google/gopacket` anywhere (must be `gopacket/gopacket`) - Go 1.24 installed and working - Module initialized with gopacket, go-pcap, cobra dependencies - 11 TrafficClass constants defined - ClassifiedPacket and WindowSnapshot types defined (shared contracts) - Config-driven classifier with 12+ rules in a slice (not switch) - All protocol class unit tests passing After completion, create `.planning/phases/01-capture-and-classification/01-01-SUMMARY.md`