Documentation
¶
Overview ¶
Package app is the graywolf composition root. It parses flags, wires every runtime component (configstore, modembridge, TX governor, KISS, AGW, digipeater, GPS, beacon, iGate, HTTP) into an App, and exposes a single Run entry point that the main shim calls.
Splitting this out of main makes startup/shutdown ordering testable and gives new services a single, obvious place to be wired in.
Index ¶
- func OpenAuthStore(dbPath string) (*webauth.AuthStore, func(), error)
- func OpenStoreAndAuth(dbPath string) (*configstore.Store, *webauth.AuthStore, func(), error)
- func QueryModemVersion(path string) (string, error)
- func ResolveModemPath(explicit string) (string, error)
- type App
- func (a *App) Config() Config
- func (a *App) KissTncDropped(ifaceID uint32) uint64
- func (a *App) KissTncQueueOverflow(ifaceID uint32) uint64
- func (a *App) Run(ctx context.Context) error
- func (a *App) RxFanoutDropped() uint64
- func (a *App) Start(ctx context.Context) error
- func (a *App) Stop(shutdownCtx context.Context) error
- type Config
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func OpenAuthStore ¶
OpenAuthStore opens the configstore at dbPath and returns a ready webauth.AuthStore along with a cleanup function the caller must defer. It is the lightweight entry point used by the auth CLI subcommand, which needs database access without the full App lifecycle (no bridge, no governor, no HTTP server).
The returned cleanup closes the underlying configstore; calling it twice is safe.
func OpenStoreAndAuth ¶
OpenStoreAndAuth is like OpenAuthStore but also exposes the underlying *configstore.Store so callers (currently only the auth CLI's set-password path) can write raw updates via store.DB().Save.
func QueryModemVersion ¶
QueryModemVersion runs `<path> --version` and returns its stdout trimmed of whitespace. The Rust side formats it to exactly match graywolf's own FullVersion() so string equality is sufficient.
This deliberately uses a short context.WithTimeout derived from context.Background(): it runs during early startup before the App context exists, and its timeout is the only thing preventing a hung modem binary from wedging startup forever.
func ResolveModemPath ¶
ResolveModemPath figures out where to find the graywolf-modem binary. The search order is:
- explicit path (used verbatim, no existence check so the resulting error message points at the user-supplied path)
- $GRAYWOLF_MODEM env var
- sibling of the current executable — handles the installed case (/usr/bin/graywolf → /usr/bin/graywolf-modem) and the dev case (./bin/graywolf → ./bin/graywolf-modem) uniformly
- ./target/release/graywolf-modem — lets a developer run the freshly-built Go binary straight from the repo root against a cargo release build without staging into bin/
- $PATH lookup as a last resort
Types ¶
type App ¶
type App struct {
// contains filtered or unexported fields
}
App is the graywolf composition root. It holds the resolved Config, a logger, and — once Start has run — a slice of live components in the order they were brought up. Stop tears them down in reverse.
Construction is a two-phase process:
- New(cfg, logger) builds an empty App.
- Run(ctx) (or, in tests, directly populating startOrder and calling Start/Stop) wires services and drives the lifecycle.
func New ¶
New returns an App with the given config and logger. It does not open any resources; call Run (or Start) to bring the app online.
func (*App) Config ¶
Config returns the App's resolved Config. Exposed for tests and for the few places in wiring that need to read a value after construction.
func (*App) KissTncDropped ¶
KissTncDropped returns the cumulative rate-limiter drop count for the KISS interface with the given DB ID. Zero if the interface is not running or the limiter is in unlimited mode. Phase 5 uses this for the per-interface rate-limit counter.
func (*App) KissTncQueueOverflow ¶
KissTncQueueOverflow returns the cumulative per-interface ingress queue overflow count for the KISS interface with the given DB ID. Zero if the interface is not running. Phase 5 uses this for the per-interface queue-overflow counter.
func (*App) Run ¶
Run brings every wired component online, blocks until ctx is done, then tears everything back down with a derived shutdown context bounded by Config.ShutdownTimeout. Run returns the first non-nil error from startup or shutdown.
The shutdown context is derived from context.Background() because ctx itself has already been cancelled by the time shutdown begins; deriving from ctx would yield an already-dead deadline. This is one of only two deliberate context.Background() uses in pkg/app; QueryModemVersion in modem.go is the other, and for the same kind of reason (it runs before the App context exists).
func (*App) RxFanoutDropped ¶
RxFanoutDropped returns the number of KISS-TNC inbound frames dropped at the shared RX fanout channel because the single consumer was behind (non-blocking send failed). Modem-RX never increments this — its producer is a blocking send. Phase 5 wires this into a Prometheus counter.
func (*App) Start ¶
Start iterates startOrder and brings every component online, in order. The first start error short-circuits the loop — only the components that came up successfully are recorded into a.started, so a subsequent Stop tears down exactly that prefix.
func (*App) Stop ¶
Stop shuts down every component that Start successfully brought up, in reverse of the startup order. A component's stop error is logged and collected but does not abort the loop — the next component gets its turn regardless so that a crashed service cannot strand an OS-level resource (socket, goroutine, file descriptor) owned by a later component.
Stop is idempotent: calling it twice drains the started slice and the second call is a no-op.
type Config ¶
type Config struct {
// DBPath is the path to the SQLite config database (-config).
DBPath string
// ModemPath is the explicit path to the graywolf-modem binary
// (-modem). Empty means auto-resolve via resolveModemPath.
ModemPath string
// HTTPAddr is the web server listen address (-http).
HTTPAddr string
// ShutdownTimeout bounds how long Stop will wait for components to
// exit cleanly (-shutdown-timeout).
ShutdownTimeout time.Duration
// HistoryDBPath is the path to the optional position-history SQLite
// database (-history-db). The web UI toggles enabled/disabled but
// the path is set here so service managers (systemd, Windows SCM)
// can point it at a writable location.
HistoryDBPath string
// TileCacheDir is the directory for the offline PMTiles cache
// (-tile-cache-dir). The directory is created on startup if missing.
// Used by Plan 2's offline downloads; Plan 1 only establishes the
// path and ensures it exists.
TileCacheDir string
// FlacFile, when non-empty, overrides the first audio device with a
// FLAC file for offline testing (-flac).
FlacFile string
// Debug enables debug-level logging (-debug).
Debug bool
// LogBufferRamdisk forces the logbuffer to land on tmpfs even when
// graywolf is not running on an SD-card-backed system
// (--logbuffer-ramdisk). Useful for desktop/server operators who
// want the same write-burst characteristics as the Pi default.
LogBufferRamdisk bool
// SessionMaxAge, when non-zero, overrides the webauth package's
// default session cookie lifetime. Zero means use the webauth
// default (currently 7 days). Threaded through wireHTTP into
// webauth.Handlers; not yet surfaced as a CLI flag.
SessionMaxAge time.Duration
// Version and GitCommit are injected by the main shim from
// -ldflags-provided build constants. They are not parsed from
// command-line flags.
Version string
GitCommit string
}
Config is the fully-resolved runtime configuration for an App instance. Every field corresponds to either a command-line flag or a value injected by the main shim at build time (Version, GitCommit). New fields must be added here rather than threaded through Run as extra parameters.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns a Config populated with the same defaults ParseFlags applies when no flags are provided. Tests that need a minimal-but-valid Config should start from this.
func ParseFlags ¶
ParseFlags parses the graywolf command-line flags (everything after the program name, i.e. os.Args[1:]) into a Config. It uses a fresh flag.FlagSet in ContinueOnError mode so callers get real errors instead of an os.Exit, making it testable.
Help output is written to os.Stderr. When the user passes -h or --help the FlagSet prints its usage and ParseFlags returns the sentinel flag.ErrHelp wrapped with %w, so the caller can detect it via errors.Is and exit 0 rather than 2.
Version and GitCommit are left zero; the main shim sets them from its build-time ldflags constants before calling app.New.
func (Config) FullVersion ¶
FullVersion returns the display-format version string shared with graywolf-modem, e.g. "v0.7.13-abcdef1". The Rust side must produce a byte-identical string so the startup banner's mismatch check works.
func (Config) Validate ¶
Validate performs basic sanity checks on the Config. It is intentionally cheap: filesystem checks (does DBPath's directory exist, is FlacFile readable) are deferred to the actual Start path so that a programmer can construct a Config in a test without having the real paths present.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package ingress defines an in-process typed identifier for the origin of an RX frame flowing through the modem-RX fanout.
|
Package ingress defines an in-process typed identifier for the origin of an RX frame flowing through the modem-RX fanout. |
|
Package txbackend contains the per-channel TX dispatcher that replaces the single modem Sender function graywolf used before Phase 3 of the KISS TCP-client / channel-backing plan.
|
Package txbackend contains the per-channel TX dispatcher that replaces the single modem Sender function graywolf used before Phase 3 of the KISS TCP-client / channel-backing plan. |