app

package
v0.13.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 9, 2026 License: GPL-2.0 Imports: 48 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func OpenAuthStore

func OpenAuthStore(dbPath string) (*webauth.AuthStore, func(), error)

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

func OpenStoreAndAuth(dbPath string) (*configstore.Store, *webauth.AuthStore, func(), error)

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

func QueryModemVersion(path string) (string, error)

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

func ResolveModemPath(explicit string) (string, error)

ResolveModemPath figures out where to find the graywolf-modem binary. The search order is:

  1. explicit path (used verbatim, no existence check so the resulting error message points at the user-supplied path)
  2. $GRAYWOLF_MODEM env var
  3. 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
  4. ./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/
  5. $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:

  1. New(cfg, logger) builds an empty App.
  2. Run(ctx) (or, in tests, directly populating startOrder and calling Start/Stop) wires services and drives the lifecycle.

func New

func New(cfg Config, logger *slog.Logger) *App

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

func (a *App) Config() 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

func (a *App) KissTncDropped(ifaceID uint32) uint64

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

func (a *App) KissTncQueueOverflow(ifaceID uint32) uint64

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

func (a *App) Run(ctx context.Context) error

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

func (a *App) RxFanoutDropped() uint64

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

func (a *App) Start(ctx context.Context) error

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

func (a *App) Stop(shutdownCtx context.Context) error

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

func ParseFlags(args []string) (Config, error)

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

func (c Config) FullVersion() string

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

func (c Config) Validate() error

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.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL