SignalSentinel is a single-binary Go app. Start at cmd/sigsentinel/main.go, then follow:
runtime.go: app wiring, config-backed runtime methods, scan profile operations.session.go: scanner session lifecycle, reconnect logic, control intents, telemetry updates.state.go: in-process pub/sub state hub consumed by GUI and subsystems.capabilities.go: safety/availability rules for control operations.
Core internal packages:
internal/sds200/: scanner protocol client (UDP control, RTSP/FTP, telemetry parsing).internal/audio/ingest/,internal/audio/recording/,internal/audio/monitor/: ingest, activity-triggered recording, and live monitoring.internal/gui/: Fyne models/views/watchers and headless test stub.internal/activity/: activity detector state machine.internal/store/: YAML config/state document loading, defaults, and persistence.
Trace key runtime flows:
- State propagation:
cmd/sigsentinel/state.gopublishesRuntimeState; GUI/audio subsystems subscribe through the hub. - Control dispatch: GUI request mapping in
cmd/sigsentinel/main.go(mapGUIControlRequest) routes to runtime execution and the session command queue. - Audio path:
internal/audio/ingest/feedsinternal/audio/recording/andinternal/audio/monitor/.
Build-tag navigation:
internal/gui/is split by build tags (!headlessandheadlessstub implementation).- For fast CI-like behavior and most targeted tests, use
-tags headless.
Config navigation:
- Default user config lives at
~/.sigsentinel/config.yaml. - Schema/defaults are defined in
internal/store/config.go. - Startup/runtime override handling is wired in
cmd/sigsentinel/runtime.go(including flag-driven persistence flow).
Use docs/specs-sds200.md for device protocol behavior and docs/specs-sigsentinel.md for product expectations.
When adding features, wire them through the runtime path instead of patching one layer only.
New scanner control:
- Add/extend intent and params in
cmd/sigsentinel/session.go. - Enforce availability/safety in
cmd/sigsentinel/capabilities.go. - Map GUI requests in
cmd/sigsentinel/main.go(mapGUIControlRequest/ execution path).
New telemetry or status field:
- Parse/populate in
internal/sds200/. - Extend
RuntimeStateand publish viastateHub. - Map into
gui.RuntimeStateinmain.goand display ininternal/gui/.
New persisted setting:
- Add schema/defaults in
internal/store/config.go. - Apply startup/runtime handling in
cmd/sigsentinel/runtime.goand flag overrides when needed. - Surface in GUI settings model and save flow.
make build: build./bin/sigsentinel(includes Linux GUI dependency checks).make test: fast headless unit tests (-short -tags headless).make test-all: race + coverage across all packages.make lint:golangci-lint+go vet.go test -tags headless -run TestName ./internal/sds200/...: target a focused test.
Keep tests beside source files as *_test.go, and prefer table-driven tests for protocol/state transitions.
Structure and naming:
- Create one
_test.gofile per implementation file that requires testing. - Add one
func Test<FunctionName>per target function, and use table-driven coverage via slices/maps plust.Run(...)case blocks as needed. - Keep test case names to 3-5 words, lower case, with underscores.
- Call
t.Parallel()at the start of the test function when there is no shared mutable state; do not callt.Parallel()inside per-caset.Runblocks. - Use
t.TempDir()for isolated filesystem state. - For tests that perform I/O or blocking work, derive timeout-bound contexts from
t.Context().
Assertions and validation:
- Use
testifyassertions (requirefor setup/preconditions,assertfor outcome checks). - Do not add assertion messages unless they provide context that is not obvious from the failing assertion itself.