Files
yoloyolo/.planning/research/STACK.md
T
2026-03-26 16:54:31 +01:00

8.1 KiB
Raw Blame History

Technology Stack

Project: NetSynth v1.1 — Custom Sound Mappings Researched: 2026-03-26 Scope: Additions/changes only. Existing stack (gopacket, go-pcap, go-lame, cobra) is validated and unchanged.


Existing Stack (Do Not Re-research)

Technology Version Status
github.com/gopacket/gopacket v1.5.0 Validated in v1.0, unchanged
github.com/packetcap/go-pcap v0.0.0-20251215 Validated in v1.0, unchanged
github.com/sjzar/go-lame v0.0.9 Validated in v1.0, unchanged
github.com/spf13/cobra v1.10.2 Validated in v1.0, unchanged
Hand-rolled sine oscillator + EMA Validated in v1.0, extend in place
Ordered []Rule classifier Validated in v1.0, extend in place

New Dependencies for v1.1

TOML Config Parsing

Technology Version Purpose Why
github.com/BurntSushi/toml v1.6.0 Parse netsynth.toml config files Single-function toml.Decode() into a struct. The MetaData.Undecoded() method catches unknown keys in user configs — surfacing typos like frequncy rather than silently ignoring them. This is the right behavior for a config file tool. v1.6.0 released December 2025, Go 1.18+ required. Zero indirect dependencies.

Version confirmed: v1.6.0, December 18, 2025, via pkg.go.dev and GitHub releases page.

Why not pelletier/go-toml v2: go-toml v2.3.0 (March 2026) is faster but the performance difference is irrelevant — config is read once at startup. go-toml v2's Strict mode can detect unknown keys but requires more setup than BurntSushi's MetaData.Undecoded(). BurntSushi's API is simpler for this use case and has clearer error message patterns for user-facing config mistakes.

Config Auto-Discovery

No new dependency. Use Go stdlib only:

// Probe order: --config flag > ./netsynth.toml > ~/.config/netsynth/config.toml
func findConfigPath(flagValue string) (string, bool) {
    if flagValue != "" {
        return flagValue, true
    }
    if _, err := os.Stat("./netsynth.toml"); err == nil {
        return "./netsynth.toml", true
    }
    if dir, err := os.UserConfigDir(); err == nil {
        p := filepath.Join(dir, "netsynth", "config.toml")
        if _, err := os.Stat(p); err == nil {
            return p, true
        }
    }
    return "", false
}

os.UserConfigDir() returns $XDG_CONFIG_HOME if set, else $HOME/.config on Linux/macOS — confirmed against Go stdlib docs. No third-party XDG library needed.

Additional Waveform Types

No new dependency. Extend the existing synth.Oscillator in place.

Square, sawtooth, and triangle are pure math — each is ~3 lines. The existing oscillator uses a phase accumulator (0.01.0 range), which is the right representation for all four waveforms:

// Waveform enum addition to synth package
type Waveform int

const (
    WaveformSine     Waveform = iota
    WaveformSquare
    WaveformSawtooth
    WaveformTriangle
)

// Per-sample generation (replaces math.Sin call in Advance())
func sample(phase float64, w Waveform) float64 {
    switch w {
    case WaveformSquare:
        if phase < 0.5 { return 1.0 }
        return -1.0
    case WaveformSawtooth:
        return 2*phase - 1.0
    case WaveformTriangle:
        if phase < 0.5 { return 4*phase - 1.0 }
        return 3.0 - 4*phase
    default: // WaveformSine
        return math.Sin(2 * math.Pi * phase)
    }
}

The Oscillator struct gains a Waveform field; Advance() dispatches to sample(). Harmonics still work the same way — each harmonic's phase is phase * ratio, which maps correctly for all waveform types.


Installation Delta

# Add only this new dependency
go get github.com/BurntSushi/toml@v1.6.0

No changes to build flags. CGO_ENABLED=1 still required for go-lame.


Integration Points

Where Config Feeds Existing Code

The TOML config needs to override two existing data structures:

  1. synth.ClassFreqConfigs (map in synth/config.go) — user can override BaseHz and add a Waveform field per class
  2. classify.DefaultRules (slice in classify/rules.go) — user can prepend custom rules before the defaults

The config loader should apply overrides at startup before any other initialization. The cleanest integration is:

cmd/netsynth/main.go
  -> config.Load(path)          // returns *AppConfig
  -> classify.MergeRules(cfg)   // prepend user rules to DefaultRules
  -> synth.ApplyOverrides(cfg)  // patch ClassFreqConfigs entries

Both classify.DefaultRules and synth.ClassFreqConfigs are currently package-level vars — they can be replaced or cloned at startup without changing the downstream pipeline.

TOML Struct Shape

The config schema maps naturally to the existing types:

# netsynth.toml
[[rules]]
protocol = "tcp"
dst_port = 8443
class    = "my-https-alt"

[sounds.my-https-alt]
frequency = 195.0
waveform  = "square"

[sounds.ICMP]
frequency = 80.0   # override built-in
waveform  = "triangle"
type AppConfig struct {
    Rules  []RuleConfig              `toml:"rules"`
    Sounds map[string]SoundConfig    `toml:"sounds"`
}

type RuleConfig struct {
    Protocol string `toml:"protocol"`
    DstPort  uint16 `toml:"dst_port"`
    Class    string `toml:"class"`
}

type SoundConfig struct {
    Frequency float64 `toml:"frequency"`
    Waveform  string  `toml:"waveform"`  // "sine"|"square"|"sawtooth"|"triangle"
}

Use toml.Decode() and check meta.Undecoded() to warn on unknown keys.


What NOT to Add

Avoid Why What to Do Instead
adrg/xdg or any XDG library os.UserConfigDir() in stdlib already handles $XDG_CONFIG_HOME on Linux — confirmed Use os.UserConfigDir() directly
pelletier/go-toml v2 No advantage over BurntSushi for a single-file startup read; MetaData.Undecoded() in BurntSushi is more ergonomic for typo detection github.com/BurntSushi/toml
spf13/viper Massive dependency (brings in 20+ transitive deps) for a use case that is one TOML file — Viper adds remote config, env var binding, hot reload, none of which are needed BurntSushi/toml + manual flag override
Any waveform/audio library Square/sawtooth/triangle are 3 lines of math each; no library adds value Extend synth.Oscillator in place
gopkg.in/yaml.v3 or JSON config TOML is explicitly specified for this milestone and is the right format for user-editable config files (comments supported, less noisy than JSON) TOML only

Version Compatibility

Package Version Compatible With Notes
BurntSushi/toml v1.6.0 Go 1.18+ No issues with Go 1.24
os.UserConfigDir() stdlib Go 1.13+ Returns $XDG_CONFIG_HOME or $HOME/.config on Linux

Confidence Assessment

Area Confidence Source
BurntSushi/toml v1.6.0 version HIGH pkg.go.dev confirmed, GitHub releases confirmed
os.UserConfigDir() XDG behavior HIGH Official Go stdlib docs at pkg.go.dev/os
Waveform math (no library needed) HIGH Trivial math, Dylan Meeus Go audio blog confirms the same approach
go-toml v2.3.0 version HIGH pkg.go.dev confirmed
Recommendation of BurntSushi over go-toml v2 MEDIUM Based on API ergonomics for the specific Undecoded() use case; both would work

Sources

  • pkg.go.dev/github.com/BurntSushi/toml — v1.6.0 confirmed, December 18, 2025
  • github.com/BurntSushi/toml/releases — v1.6.0 release notes, TOML 1.1 enabled by default
  • pkg.go.dev/github.com/pelletier/go-toml/v2 — v2.3.0 confirmed, March 24, 2026
  • pkg.go.dev/os#UserConfigDir — XDG_CONFIG_HOME behavior on Linux confirmed via official Go docs
  • dylanmeeus.github.io/posts/audio-from-scratch-pt8/ — Go waveform synthesis from scratch, confirms no library needed
  • github.com/golang/go/issues/76320 — UserConfigDir XDG_CONFIG_HOME discussion (Nov 2025), confirms existing stdlib support on Linux

Stack research for: NetSynth v1.1 — Custom Sound Mappings milestone Researched: 2026-03-26