systemconfig

package
v0.8.4 Latest Latest
Warning

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

Go to latest
Published: Oct 9, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package systemconfig is the runtime config store. Operator-tunable values land here as one row per key in the system_config table, with JSONB values typed by per-config-domain structs.

Spec: specs/services/connectivity-config.spec.yaml (the first consumer). Future config domains (compliance scheduler, retention, alert thresholds) land alongside without schema changes.

Key namespaces are managed inline (one per config struct). The Get/Set helpers operate on raw JSONB; consumers wrap them with a typed Load/Save (e.g., LoadConnectivity, SetConnectivity).

Defaults: when no row exists for a key, Load* returns the baked-in defaults the calling consumer defines. No "initial seed" migration — the absence of a row is itself a meaningful state ("never configured, defaults apply").

Audit: every Set emits audit.SystemConfigChanged with {config_key, old_value, new_value, changed_by}. The old_value is captured in the same transaction so concurrent writes can't race it into incoherence.

Index

Constants

View Source
const (
	KeyConnectivity = "connectivity"
	KeyIntelligence = "intelligence"
	KeySecurity     = "security"
	KeyDiscovery    = "discovery"
	KeyScan         = "scan"
	KeyScanVars     = "scan_variables"
	KeyCompliance   = "compliance"
)

Key namespaces. One constant per config domain — keep them grouped here so the audit trail's config_key field has a stable enum.

View Source
const (
	ScanIntervalMinFloor = 5
	ScanIntervalMaxCap   = 2880
)

ScanIntervalMinFloor / ScanIntervalMaxCap bound the ladder values in minutes. They mirror scheduler.MinIntervalFloor / MaxIntervalCap; duplicated as ints here so systemconfig does not import the scheduler package.

View Source
const (
	ScanVarsMaxCount    = 200
	ScanVarsMaxValueLen = 4096
	ScanVarsMaxNameLen  = 128
)

Bounds for ScanVariables.Validate. The corpus uses ~20 variables; 200 names is far above any legitimate use. Values are shell-safe template substitutions (paths, hostnames, banner text) — 4 KiB covers a long login banner with margin.

View Source
const ComplianceMaxEnabledFrameworks = 32

ComplianceMaxEnabledFrameworks caps the allowlist. The corpus has a handful of families (stig, cis, nist_800_53, pci_dss_4, srg); 32 is far above any real use and just blocks an abuse shape.

Variables

View Source
var ErrInvalidConfig = errors.New("systemconfig: invalid config")

ErrInvalidConfig is returned by validation when a field is out of bounds. Wrap with %w so callers can errors.Is(err, ErrInvalidConfig).

Functions

This section is empty.

Types

type ComplianceConfig added in v0.4.0

type ComplianceConfig struct {
	DefaultFramework string `json:"default_framework"`
	// EnabledFrameworks is the Phase 2 allowlist: the framework FAMILY ids an
	// admin has opted the org into. Empty means ALL families found in the
	// corpus are available as lenses (the factory default and every existing
	// stored row). A non-empty list restricts the lens picker + the frameworks
	// endpoint to these families.
	EnabledFrameworks []string `json:"enabled_frameworks,omitempty"`
}

ComplianceConfig holds org-wide compliance-DISPLAY settings. Phase 1 carries only the default lens: the framework FAMILY the score surfaces (dashboard avg-compliance, hosts list, host detail) default to. An empty value means "All rules" — the full Kensa corpus, an honest framework-agnostic baseline and the factory default. A non-empty value is a family id (e.g. "stig", "cis", "nist_800_53") resolved per-host to the OS-specific corpus key at query time (internal/framework); an unknown/absent family falls back to all-rules.

func DefaultCompliance added in v0.4.0

func DefaultCompliance() ComplianceConfig

DefaultCompliance returns the baked-in default: All rules (empty lens), all families enabled.

func (ComplianceConfig) Validate added in v0.4.0

func (c ComplianceConfig) Validate() error

Validate bounds the default family id and the enabled-frameworks allowlist. An empty default (All rules) is always valid. When the allowlist is non-empty, a non-empty default MUST be one of the enabled families, so an admin cannot default the org to a hidden family.

type ConnectivityConfig

type ConnectivityConfig struct {
	// Per-state probe intervals in seconds. v1.1.0 NEW.
	OnlineSec      int `json:"online_sec"`
	DegradedSec    int `json:"degraded_sec"`
	CriticalSec    int `json:"critical_sec"`
	DownSec        int `json:"down_sec"`
	MaintenanceSec int `json:"maintenance_sec"`

	TimeoutSec           int  `json:"timeout_sec"`
	UnreachableThreshold int  `json:"unreachable_threshold"`
	RateLimit            int  `json:"rate_limit"`
	MaintenanceGlobal    bool `json:"maintenance_global"`
}

ConnectivityConfig is the typed shape stored under KeyConnectivity.

Spec: services-connectivity-config v1.1.0 C-01 + C-02. Per-state intervals replace the v1.0.0 single IntervalSec — the liveness loop computes each host's next_probe_at from the band the host is in (Online / Degraded / Critical / Down).

Maintenance band stays here as a persisted config so the UI can edit it, but the loop doesn't read it yet — per-host maintenance is in the backlog. MaintenanceGlobal still pauses the whole loop.

func DefaultConnectivity

func DefaultConnectivity() ConnectivityConfig

DefaultConnectivity returns the baked-in defaults — the band table in services-connectivity-config v1.1.0.

Online      — 15m (consec=0, reachable: nothing's changing)
Degraded    —  5m (consec>=1, reachable: watch closely)
Critical    —  2m (consec<3, unreachable: confirm fast)
Down        — 30m (consec>=3: back off, don't hammer dead)
Maintenance — 60m (UI/persisted band, not yet auto-applied)

func (ConnectivityConfig) Validate

func (c ConnectivityConfig) Validate() error

Validate enforces the bounds in services-connectivity-config C-01. Returns a wrapped ErrInvalidConfig naming the offending field.

type DiscoveryConfig

type DiscoveryConfig struct {
	IntervalSec          int  `json:"interval_sec"`
	RateLimit            int  `json:"rate_limit"`
	DetectOnFirstContact bool `json:"detect_on_first_contact"`
	MaintenanceGlobal    bool `json:"maintenance_global"`
}

DiscoveryConfig is the typed shape stored under KeyDiscovery.

Spec: system-discovery-scheduler v1.0.0 C-04 + C-05.

IntervalSec is the per-host cadence the scheduler treats as "due again"; a host with os_discovered_at older than now() - IntervalSec re-enters listDiscoveryTargets. RateLimit caps the number of jobs enqueued per tick — bounds the thundering-herd shape when many hosts hit NULL os_discovered_at at once. DetectOnFirstContact gates the host-create auto-enqueue (when false, new hosts stay NULL until the sweeper or an explicit operator click finds them). MaintenanceGlobal pauses the entire scheduler loop.

func DefaultDiscovery

func DefaultDiscovery() DiscoveryConfig

DefaultDiscovery returns the baked-in defaults.

IntervalSec          — 86400  (24h per host)
RateLimit            —    25  (max jobs enqueued per tick)
DetectOnFirstContact —  true  (host create still auto-fingerprints)
MaintenanceGlobal    — false

func (DiscoveryConfig) Validate

func (c DiscoveryConfig) Validate() error

Validate enforces the bounds in system-discovery-scheduler C-04, C-05. Returns a wrapped ErrInvalidConfig naming the offending field.

type EmitFunc

type EmitFunc func(ctx context.Context, code audit.Code, ev audit.Event)

EmitFunc mirrors audit.Emit's signature so callers can substitute a no-op or capture-into-buffer for tests. Production wires audit.Emit.

type IntelligenceConfig

type IntelligenceConfig struct {
	IntervalSec       int  `json:"interval_sec"`
	RateLimit         int  `json:"rate_limit"`
	MaintenanceGlobal bool `json:"maintenance_global"`
}

IntelligenceConfig is the typed shape stored under KeyIntelligence.

Spec: system-intelligence-scheduler v1.0.0 C-06 + C-07.

IntervalSec is the per-host cadence the scheduler advances next_intelligence_at by after a successful RunCycle. RateLimit caps the bounded worker pool the scheduler uses to dispatch per-tick. MaintenanceGlobal pauses the entire loop (mirrors the connectivity flag).

func DefaultIntelligence

func DefaultIntelligence() IntelligenceConfig

DefaultIntelligence returns the baked-in defaults.

IntervalSec       — 3600 (1 hour per host)
RateLimit         —   10 (concurrent RunCycles per scheduler)
MaintenanceGlobal — false

func (IntelligenceConfig) Validate

func (c IntelligenceConfig) Validate() error

Validate enforces the bounds in system-intelligence-scheduler C-06, C-07. Returns a wrapped ErrInvalidConfig naming the offending field.

type ScanConfig

type ScanConfig struct {
	Enabled bool `json:"enabled"`

	// Per-state scan intervals in minutes — the tier ladder.
	UnknownMins         int `json:"unknown_mins"`
	CriticalMins        int `json:"critical_mins"`
	NonCompliantMins    int `json:"non_compliant_mins"`
	PartialMins         int `json:"partial_mins"`
	MostlyCompliantMins int `json:"mostly_compliant_mins"`
	CompliantMins       int `json:"compliant_mins"`

	// RateLimit caps hosts dispatched per scheduler tick (1..100).
	RateLimit int `json:"rate_limit"`
	// MaintenanceGlobal pauses the entire dispatch loop (mirrors the
	// connectivity / intelligence / discovery flags).
	MaintenanceGlobal bool `json:"maintenance_global"`
}

ScanConfig is the typed shape stored under KeyScan — the adaptive compliance scan scheduler's operator-editable knobs.

Spec: system-scheduler v3.0.0 C-01 (tier ladder from systemconfig, replacing the v2 signed schedules-policy file per scan plan decision #4) + api-system-scan-config.

The six *Mins fields are the tier ladder: how long after a scan a host in that compliance state waits before its next scheduled scan. Riskier states re-scan sooner. Values are minutes; the scheduler clamps them into [5m, 48h] at load time (C-08), and Normalize applies the same clamp at PUT time so what the operator reads back is what the scheduler runs.

func DefaultScan

func DefaultScan() ScanConfig

DefaultScan returns the baked-in defaults. Auto-scan is ON by default (the OpenWatch OS model is auto-scan centric); the ladder re-scans riskier states sooner.

unknown            —  360m (6h: classified on first scan anyway)
critical           —  240m (4h)
non_compliant      —  480m (8h)
partial            —  720m (12h)
mostly_compliant   — 1440m (24h)
compliant          — 2880m (48h ceiling)
rate_limit         —    25 hosts per tick

func (ScanConfig) Normalize

func (c ScanConfig) Normalize() ScanConfig

Normalize returns a copy with every ladder value clamped into [ScanIntervalMinFloor, ScanIntervalMaxCap] and the rate limit into [1, 100]. PUT /system/scan/config clamps rather than rejects (scan plan Phase 4: "server clamps to the scheduler's bounds") so operator typos degrade safely instead of bouncing the whole save.

func (ScanConfig) Validate

func (c ScanConfig) Validate() error

Validate is satisfied by construction after Normalize — kept so the store's Set path stays uniform with the sibling configs. It rejects values Normalize would have fixed, catching callers that skip it.

type ScanVariables

type ScanVariables map[string]string

ScanVariables is the typed shape stored under KeyScanVars: operator overrides for the kensa rule-template variables, name -> value. Only OVERRIDES are stored — names absent here resolve to kensa's built-in defaults at rule-load time (LoadRules merges caller vars over BuiltInVars). The global tier only; per-group/per-host tiers are a later phase per the ratified Kensa boundary.

Spec: api-system-scan-config v1.1.0 (scan variables endpoints).

func (ScanVariables) Validate

func (v ScanVariables) Validate() error

Validate bounds the override map. Name VALIDITY (is this a variable the corpus actually uses?) is the HTTP handler's job — it holds the kensa variable catalog; this store-side check only blocks abuse shapes (oversized maps, empty or huge names, huge values).

type SecurityConfig

type SecurityConfig struct {
	AllowCredentialSudoPassword bool `json:"allow_credential_sudo_password"`
	// WarnDaysBeforePasswordExpiry is how many days ahead of a host
	// user's password expiry the daily sweep raises a notification.
	// Default 14; must be in [1, 365]. Spec system-account-policy C-03.
	WarnDaysBeforePasswordExpiry int `json:"warn_days_before_password_expiry"`
}

SecurityConfig is the typed shape stored under KeySecurity.

Spec: system-ssh-connectivity v1.1.0 C-09..C-12.

AllowCredentialSudoPassword is the policy knob that gates the sudo -S password fallback in the SSH dial layer. Defaults to TRUE: OpenWatch supports the full SSH matrix out of the box (key or password auth, NOPASSWD or password sudo), so a host that needs a sudo password is reachable on a fresh install with no extra configuration. The flag is retained as a kill-switch — a site that forbids feeding credential passwords to remote sudo can set it false, and every privilege path degrades to `sudo -n` only.

func DefaultSecurity

func DefaultSecurity() SecurityConfig

DefaultSecurity returns the baked-in defaults. The sudo -S password fallback is ON by default (kill-switch, not opt-in).

func (SecurityConfig) Validate

func (c SecurityConfig) Validate() error

Validate bounds the password-expiry warn window to [1, 365] days. A zero value is treated as "unset" and coerced to the default by the resolver, so it is not itself an error here.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store reads and writes typed config from system_config.

func NewStore

func NewStore(pool *pgxpool.Pool, emit EmitFunc) *Store

NewStore returns a Store backed by the given pool. emit may be audit.Emit in production; tests can pass a capture function.

func (*Store) LoadCompliance added in v0.4.0

func (s *Store) LoadCompliance(ctx context.Context) (ComplianceConfig, error)

LoadCompliance returns the persisted compliance-display config, or DefaultCompliance (All rules) when no row exists. Only returns an error for DB / unmarshal failures.

func (*Store) LoadConnectivity

func (s *Store) LoadConnectivity(ctx context.Context) (ConnectivityConfig, error)

LoadConnectivity returns the persisted ConnectivityConfig OR DefaultConnectivity when no row exists for KeyConnectivity. Only returns an error for DB / unmarshal failures — "no row" is not an error.

Spec services-connectivity-config AC-01 / C-02.

func (*Store) LoadDiscovery

func (s *Store) LoadDiscovery(ctx context.Context) (DiscoveryConfig, error)

LoadDiscovery returns the persisted DiscoveryConfig OR DefaultDiscovery when no row exists for KeyDiscovery. Only returns an error for DB / unmarshal failures.

Spec api-system-discovery-config AC-01.

func (*Store) LoadIntelligence

func (s *Store) LoadIntelligence(ctx context.Context) (IntelligenceConfig, error)

LoadIntelligence returns the persisted IntelligenceConfig OR DefaultIntelligence when no row exists for KeyIntelligence. Only returns an error for DB / unmarshal failures.

Spec api-system-intelligence-config AC-01.

func (*Store) LoadScan

func (s *Store) LoadScan(ctx context.Context) (ScanConfig, error)

LoadScan returns the persisted ScanConfig OR DefaultScan when no row exists for KeyScan. Only returns an error for DB / unmarshal failures — "no row" is not an error.

Spec api-system-scan-config AC-01.

func (*Store) LoadScanVars

func (s *Store) LoadScanVars(ctx context.Context) (ScanVariables, error)

LoadScanVars returns the persisted operator variable overrides, or an empty map when none exist. Only returns an error for DB / unmarshal failures.

Spec api-system-scan-config v1.1.0.

func (*Store) LoadSecurity

func (s *Store) LoadSecurity(ctx context.Context) (SecurityConfig, error)

LoadSecurity returns the persisted SecurityConfig OR DefaultSecurity when no row exists for KeySecurity. Only returns an error for DB / unmarshal failures.

Spec system-ssh-connectivity v1.1.0 C-09.

func (*Store) SetCompliance added in v0.4.0

func (s *Store) SetCompliance(ctx context.Context, cfg ComplianceConfig, changedBy string) (ComplianceConfig, error)

SetCompliance validates and persists the compliance-display config, emitting a SystemConfigChanged audit event with the old + new value.

func (*Store) SetConnectivity

func (s *Store) SetConnectivity(ctx context.Context, cfg ConnectivityConfig, changedBy string) error

SetConnectivity validates the input, UPSERTs the row, and emits audit.SystemConfigChanged with the old + new snapshot captured in the same transaction.

Spec services-connectivity-config AC-02 / C-03 / C-07.

func (*Store) SetDiscovery

func (s *Store) SetDiscovery(ctx context.Context, cfg DiscoveryConfig, changedBy string) error

SetDiscovery validates the input, UPSERTs the row, and emits audit.SystemConfigChanged with the old + new snapshot captured in the same transaction.

Spec api-system-discovery-config AC-03 / AC-05 / AC-07.

func (*Store) SetIntelligence

func (s *Store) SetIntelligence(ctx context.Context, cfg IntelligenceConfig, changedBy string) error

SetIntelligence validates the input, UPSERTs the row, and emits audit.SystemConfigChanged with the old + new snapshot captured in the same transaction.

Spec api-system-intelligence-config AC-03 / AC-05 / AC-08.

func (*Store) SetScan

func (s *Store) SetScan(ctx context.Context, cfg ScanConfig, changedBy string) (ScanConfig, error)

SetScan normalizes (clamps) the input, validates, UPSERTs the row, and emits audit.SystemConfigChanged with the old + new snapshot. Returns the normalized config that was actually persisted so the handler can echo back the clamped values.

Spec api-system-scan-config AC-03 (clamp-not-reject) / AC-05.

func (*Store) SetScanVars

func (s *Store) SetScanVars(ctx context.Context, vars ScanVariables, changedBy string) error

SetScanVars validates and persists the FULL override map (PUT replaces, not merges) and emits audit.SystemConfigChanged with the old + new snapshots.

Spec api-system-scan-config v1.1.0.

Jump to

Keyboard shortcuts

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