config

package
v0.1.0-proto2g Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2026 License: MPL-2.0 Imports: 9 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// QUICStreamTimeout is the max time for a single QUIC stream operation.
	// Rationale: Covers network latency + agent marshaling/unmarshaling (< 10s typical).
	QUICStreamTimeout = 10 * time.Second

	// QUICIdleTimeout is the connection idle timeout before automatic closure.
	// Rationale: Balances connection reuse vs resource cleanup (1 minute idle acceptable).
	QUICIdleTimeout = 60 * time.Second
)

QUIC Transport Timeouts

View Source
var (
	// ClientPendingTimeout is the max wait for an agent to enter PENDING state.
	// Rationale: Fast-fail if supervisor doesn't acknowledge command quickly.
	ClientPendingTimeout = 2 * time.Second

	// ClientTerminalTimeout is the max wait for an agent to reach terminal state (RUNNING/STOPPED/FAILED).
	// Rationale: Covers Python startup, health checks, socket binding (< 20s for well-behaved agents).
	ClientTerminalTimeout = 20 * time.Second
)

Client Lifecycle Timeouts

View Source
var (
	// SupervisorStartDeadline is the max time for an agent to report ready after start command.
	// Rationale: Aligns with ClientTerminalTimeout to prevent supervisor-client mismatch.
	SupervisorStartDeadline = 20 * time.Second

	// SupervisorShutdownTimeout is the graceful shutdown timeout before force-kill.
	// Rationale: Allows agents to flush logs, close connections (5s is reasonable grace period).
	SupervisorShutdownTimeout = 5 * time.Second
)

Supervisor Timeouts

View Source
var (
	// TestAgentStartTimeout is the max time to wait for agent start in tests.
	// Rationale: CI environments may be slow; 2-minute buffer covers edge cases.
	TestAgentStartTimeout = 120 * time.Second

	// TestAgentStopTimeout is the max time to wait for agent stop in tests.
	// Rationale: Covers graceful shutdown + Python interpreter cleanup.
	TestAgentStopTimeout = 60 * time.Second
)

Test Timeouts (more generous for CI environments)

Functions

func AgentSearchPaths

func AgentSearchPaths() []string

AgentSearchPaths returns the system-scope search path.

It is the compatibility spelling for AgentSearchPathsFor(ScopeSystem); every existing caller is a system manager.

func AgentSearchPathsFor

func AgentSearchPathsFor(scope Scope) []string

AgentSearchPathsFor returns the ordered directories to search, highest precedence first. Discovery is first-ID-wins, so an agent found in an earlier directory MASKS one of the same ID found later - which is what makes these tiers an override mechanism rather than a concatenation.

Every directory is namespaced by the PRODUCT rather than by the kernel (GAPI-DIV-061): gapid searches /usr/lib/gapi/agents, goblind searches /usr/lib/goblin/agents. This function is reached inside goblind through agentmgr's discovery, so it is one of the kernel surfaces an operator who has never heard of gapi would otherwise meet.

THE ORDERING RULE, taken from systemd and XDG and applied to both scopes: configuration beats runtime beats data beats vendor. An operator's edit outranks a package's file, and a transient unit outranks the installed one it shadows.

System scope, highest to lowest, with <p> the product name:

  • <PREFIX>_DEV_AGENTS explicit development override
  • /etc/<p>/agents operator-authored
  • /run/<p>/agents transient, generated at runtime
  • /usr/local/lib/<p>/agents locally installed
  • /usr/lib/<p>/agents package-owned

User scope, highest to lowest:

  • <PREFIX>_DEV_AGENTS explicit development override
  • $XDG_CONFIG_HOME/<p>/agents the user's own
  • /etc/<p>/user/agents operator-provided, for all users
  • $XDG_RUNTIME_DIR/<p>/agents transient
  • $XDG_DATA_HOME/<p>/agents user-installed
  • ~/.<p>/agents LEGACY, see below
  • /usr/lib/<p>/user/agents package-owned user agents

SYSTEM SCOPE CONTAINS NO HOME-DIRECTORY PATH, and that is a security boundary rather than tidiness. agentmgr's safeToExecute already refuses world-writable or foreign-owned binaries at EXECUTION time; keeping user-writable directories out of the system list is the same defence at DISCOVERY time, and the two are not substitutes.

There is deliberately no implicit ./agents tier. It made discovery depend on the working directory a daemon happened to be started from - a daemon launched from the wrong directory silently discovered nothing, and 'agent new' run outside a checkout silently wrote a tree into whatever directory the operator was standing in. Development now names its directory explicitly through <PREFIX>_DEV_AGENTS, which is also what every test and script in this repo already did.

Environment overrides:

  • <PREFIX>_AGENT_PATH: colon-separated directories PREPENDED to the tiers below. It adds precedence; it does not replace the path.
  • <PREFIX>_AGENT_PATH_EXCLUSIVE: search ONLY what AGENT_PATH names.
  • <PREFIX>_DEV_AGENTS: highest-priority directory in either scope.
  • <PREFIX>_SKIP_SYSTEM_AGENTS: drop the package-owned tiers.

AGENT_PATH used to REPLACE the whole search path, and the replacement was load-bearing in two places rather than one, which is why the exclusive switch exists rather than the additive behaviour simply landing on its own (GAPI-DIV-063). A packaged install set AGENT_PATH to one directory, so the tiers above were dead code in the only configuration that ships; and test/adk's harness set it to fence discovery to a fixture directory, without which the checkout's own agents starve the fixtures' state transitions (GAPI-DIV-021). Additive fixes the first. The switch preserves the second, and a fence is a thing you ask for rather than a side effect of naming a directory.

func EnvKeyFor

func EnvKeyFor(path string) string

EnvKeyFor renders a dotted config path as the environment variable that overrides it: under gapid, "supervisor.pid1Mode" becomes GAPI_SUPERVISOR_PID1MODE; under goblind, GOBLIN_SUPERVISOR_PID1MODE.

The prefix was the literal "RUNTIME" until GAPI-DIV-059 and the literal "GAPI" until GAPI-DIV-061. Neither could be chosen by the process embedding the kernel, so an operator of goblind - which links this package as a library - had to configure it under a name belonging to a component they are not meant to know exists. It now comes from core/product, set once by the binary.

Both renames are HARD - no fallback reads an old spelling, decided by the operator. A deployed RUNTIME_CONFIG or, on goblind, a deployed GAPI_CONFIG therefore yields default config rather than an error, which is why each carries a release note.

Types

type Config

type Config struct {
	Transport  TransportConfig  `mapstructure:"transport"`
	Security   SecurityConfig   `mapstructure:"security"`
	Metrics    MetricsConfig    `mapstructure:"metrics"`
	Logging    LoggingConfig    `mapstructure:"logging"`
	Timeouts   TimeoutConfig    `mapstructure:"timeouts"`
	Supervisor SupervisorConfig `mapstructure:"supervisor"`
}

func Load

func Load() (*Config, error)

type FileOutputConfig

type FileOutputConfig struct {
	Enabled    bool   `mapstructure:"enabled"`
	Path       string `mapstructure:"path"`
	MaxSize    int    `mapstructure:"maxSize"`    // MB
	MaxBackups int    `mapstructure:"maxBackups"` // Number of old files to keep
	MaxAge     int    `mapstructure:"maxAge"`     // Days
	Compress   bool   `mapstructure:"compress"`
}

type LoggingConfig

type LoggingConfig struct {
	Level  string           `mapstructure:"level"`  // trace, debug, info, warn, error
	Format string           `mapstructure:"format"` // json, console
	File   FileOutputConfig `mapstructure:"file"`
	Loki   LokiOutputConfig `mapstructure:"loki"`
}

type LokiOutputConfig

type LokiOutputConfig struct {
	Enabled bool              `mapstructure:"enabled"`
	URL     string            `mapstructure:"url"`
	Labels  map[string]string `mapstructure:"labels"`
}

type MetricsConfig

type MetricsConfig struct {
	Enabled bool   `mapstructure:"enabled"`
	Addr    string `mapstructure:"addr"`
}

type PathType

type PathType int

PathType represents the type of agent path

const (
	PathTypeDevelopment PathType = iota
	PathTypeUser
	PathTypeSystem
)

func ClassifyPath

func ClassifyPath(path string) PathType

ClassifyPath reports which tier a path belongs to.

It classifies against the SAME lists that are searched, so the label a log line carries cannot drift from the precedence that produced it - they were two literal lists that disagreed about /etc before GAPI-DIV-061, and /etc's position has now changed.

func (PathType) String

func (pt PathType) String() string

PathTypeString returns a human-readable string for the path type

type Scope

type Scope int

Scope selects which tier list is searched.

It is an explicit parameter and MUST NOT be inferred from the effective uid. A system daemon commonly runs as an unprivileged service user - nix/module.nix creates exactly such a user - and deriving scope from privilege would silently flip that daemon into user scope, where it would discover a different agent set than the operator installed. systemd has the same property: 'systemctl --user' run by root manages root's USER instance, because the scope was asked for rather than deduced.

const (
	// ScopeSystem is the machine-wide manager: the daemon an operator
	// installs and an init system starts.
	ScopeSystem Scope = iota

	// ScopeUser is the per-user manager, selected by --user. The tier
	// list is defined so that implementing --user is wiring rather than
	// a redesign of the search path.
	ScopeUser
)

func (Scope) String

func (s Scope) String() string

type SecurityConfig

type SecurityConfig struct {
	VerifyKey string `mapstructure:"verifyKey"` // Path to public key
}

type ShutdownConfig

type ShutdownConfig struct {
	GracePeriod string `mapstructure:"gracePeriod"`
}

type SupervisorConfig

type SupervisorConfig struct {
	ProductionMode bool `mapstructure:"productionMode"`
	// Pid1Mode activates the Phase-0 pre-userspace boot sequence
	// (subreaper, PID-1 signals, kmsg, early mounts). Off by default:
	// gapid runs as an ordinary supervisor unless it IS init.
	Pid1Mode bool `mapstructure:"pid1Mode"`
	// NoEarlyMounts skips the mount phase (the OCI runtime owns mounts
	// in a container).
	NoEarlyMounts bool           `mapstructure:"noEarlyMounts"`
	Watchdog      WatchdogConfig `mapstructure:"watchdog"`
	Shutdown      ShutdownConfig `mapstructure:"shutdown"`
}

type TimeoutConfig

type TimeoutConfig struct {
	QUICStream         string `mapstructure:"quicStream"`
	QUICIdle           string `mapstructure:"quicIdle"`
	ClientPending      string `mapstructure:"clientPending"`
	ClientTerminal     string `mapstructure:"clientTerminal"`
	SupervisorStart    string `mapstructure:"supervisorStart"`
	SupervisorShutdown string `mapstructure:"supervisorShutdown"`
}

type TransportConfig

type TransportConfig struct {
	Type               string `mapstructure:"type"`
	Address            string `mapstructure:"address"`
	TLSCert            string `mapstructure:"tlsCert"`
	TLSKey             string `mapstructure:"tlsKey"`
	TLSCA              string `mapstructure:"tlsCa"`
	InsecureSkipVerify bool   `mapstructure:"insecureSkipVerify"`
}

type WatchdogConfig

type WatchdogConfig struct {
	Enabled  bool   `mapstructure:"enabled"`
	Device   string `mapstructure:"device"`
	Interval string `mapstructure:"interval"`
}

Jump to

Keyboard shortcuts

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