config

package
v0.4.1 Latest Latest
Warning

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

Go to latest
Published: Jun 30, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package config loads and validates /etc/mooring/config.yaml — the root of trust (plan §5.1). Everything here is fail-closed: any precondition violation returns an error and the binary refuses to boot. No web route ever reads or writes this file; it is edited only over SSH.

Index

Constants

View Source
const DefaultPath = "/etc/mooring/config.yaml"

DefaultPath is where the root-of-trust config lives in production.

View Source
const DevInsecurePermsEnv = "MOORING_DEV_INSECURE_PERMS"

DevInsecurePermsEnv, when set to a truthy value, relaxes the config-file ownership/permission enforcement for LOCAL DEVELOPMENT only. It is read from the environment (never from the config file itself) so the file can never disable its own root-of-trust checks (review #6).

Variables

This section is empty.

Functions

func DecodeKey

func DecodeKey(s string) ([]byte, error)

DecodeKey decodes a base64 (std or raw) master key.

Types

type AdminConfig

type AdminConfig struct {
	Hostname string `yaml:"hostname"`
	Listen   string `yaml:"listen"`
}

AdminConfig optionally fronts the admin UI through the edge and points at the Caddy admin endpoint (plan §6.1 SBD-1/SBD-2).

type AlertingConfig

type AlertingConfig struct {
	Enabled           bool     `yaml:"enabled"`
	EvalInterval      Duration `yaml:"eval_interval"`       // evaluator tick
	NotifyMinInterval Duration `yaml:"notify_min_interval"` // global rate limit between sends
	QuietStartHour    int      `yaml:"quiet_start_hour"`    // [0..23], -1 disables (suppresses WARNING; CRITICAL always pages)
	QuietEndHour      int      `yaml:"quiet_end_hour"`
	DeadMansURL       string   `yaml:"dead_mans_url"` // outbound heartbeat to an external cron-monitor
	DeadMansInterval  Duration `yaml:"dead_mans_interval"`
}

AlertingConfig is the Tier-1 tuning for the read-and-notify alert engine (plan §8). Channels + rules are operator-managed in the DB; this only tunes the engine. Off by default (no engine goroutines run unless enabled).

type AuthConfig

type AuthConfig struct {
	Username     string `yaml:"username"`
	PasswordHash string `yaml:"password_hash"`
	TOTPSecret   string `yaml:"totp_secret"`
}

AuthConfig holds the single operator's credentials.

type Config

type Config struct {
	BindAddr string `yaml:"bind_addr"`

	EncryptionKey         string `yaml:"encryption_key"`
	EncryptionKeyPrevious string `yaml:"encryption_key_previous"`

	IPAllowlist    []string `yaml:"ip_allowlist"`
	TrustProxy     bool     `yaml:"trust_proxy"`
	TrustedProxies []string `yaml:"trusted_proxies"`

	Auth    AuthConfig    `yaml:"auth"`
	Edge    EdgeConfig    `yaml:"edge"`
	Admin   AdminConfig   `yaml:"admin"`
	Session SessionConfig `yaml:"session"`
	Cookie  CookieConfig  `yaml:"cookie"`
	Docker  DockerConfig  `yaml:"docker"`
	Monitor MonitorConfig `yaml:"monitor"`
	Git     GitConfig     `yaml:"git"`
	GitHub  GitHubConfig  `yaml:"github"`

	CaddyEditor       EditorBlock     `yaml:"caddy_editor"`
	ComposeValidation EditorBlock     `yaml:"compose_validation"`
	Setup             SetupConfig     `yaml:"setup"`
	Retention         RetentionConfig `yaml:"retention"`
	Alerting          AlertingConfig  `yaml:"alerting"`
	Server            ServerConfig    `yaml:"server"`

	DataDir string `yaml:"data_dir"`

	// ProtectedProjects are Compose projects Mooring must never start/stop/
	// redeploy (the socket-proxy, and later the edge) — plan §3 protected set.
	ProtectedProjects []string `yaml:"protected_projects"`
	// contains filtered or unexported fields
}

Config is the typed root-of-trust document. Secret-bearing fields are kept as plain strings here (the file itself is 0600 root:root) but are never echoed: String() is overridden to redact them.

func Load

func Load(path string) (*Config, error)

Load reads, parses, and fully validates the config at path. It is the only entry point; a returned error means refuse-to-boot.

func Parse

func Parse(raw []byte) (*Config, error)

Parse decodes and validates config bytes (used by Load and by tests). Unknown keys are a hard error so a typo or smuggled key cannot slip through.

func (*Config) Allowlist

func (c *Config) Allowlist() []netip.Prefix

Allowlist returns the parsed allowlist prefixes (valid after Validate).

func (*Config) EdgeCAByName

func (c *Config) EdgeCAByName(name string) (EdgeCA, bool)

EdgeCAByName returns the configured CA with the given name.

func (*Config) HasEdgeCA

func (c *Config) HasEdgeCA(name string) bool

HasEdgeCA reports whether a named edge CA is configured.

func (*Config) IsProtectedProject

func (c *Config) IsProtectedProject(project string) bool

IsProtectedProject reports whether a compose project is in the protected set.

func (Config) String

func (c Config) String() string

String redacts the secret-bearing fields so a Config can never be logged in the clear (plan §5.5 / §15 lint).

func (*Config) TrustedProxyPrefixes

func (c *Config) TrustedProxyPrefixes() []netip.Prefix

TrustedProxyPrefixes returns the parsed trusted-proxy prefixes.

func (*Config) Validate

func (c *Config) Validate() error

Validate runs every fail-closed boot check. All violations are collected so a misconfigured operator sees them at once; any violation refuses boot.

type CookieConfig

type CookieConfig struct {
	Prefix   string `yaml:"prefix"`    // "__Host-" (default) or "__Secure-"
	BasePath string `yaml:"base_path"` // required iff prefix is __Secure-
}

CookieConfig selects the session cookie prefix model (plan §5.3). __Host- mandates Path=/ and no base path; __Secure- pairs with a base_path.

type DockerConfig

type DockerConfig struct {
	ProxyAddr     string `yaml:"proxy_addr"`
	ExternalProxy bool   `yaml:"external_proxy"`
}

DockerConfig points at the read-only docker-socket-proxy (plan §3). Mooring NEVER talks to the raw socket; ProxyAddr must be a loopback endpoint.

By default Mooring MANAGES this proxy itself: at boot it brings up the embedded, Mooring-owned read-only proxy compose so the operator never runs a docker command (they only ever write mooring.yaml). Set external_proxy: true if you run your own proxy/endpoint at proxy_addr and want Mooring to leave it alone.

type Duration

type Duration time.Duration

Duration is a yaml-friendly time.Duration ("20s", "30m", "12h").

func (Duration) D

func (d Duration) D() time.Duration

D returns the value as a time.Duration.

func (*Duration) UnmarshalYAML

func (d *Duration) UnmarshalYAML(value *yaml.Node) error

type EdgeCA

type EdgeCA struct {
	Name         string `yaml:"name"`          // [a-z][a-z0-9-]{0,30}; referenced as `ca: <name>`
	DirectoryURL string `yaml:"directory_url"` // the ACME directory URL (https)
	Email        string `yaml:"email"`         // optional ACME contact; falls back to acme_email
	TrustedRoot  string `yaml:"trusted_root"`  // optional PEM file Caddy trusts for the CA's OWN https
}

EdgeCA is an additional ACME issuer — a private/internal CA (e.g. step-ca). A mooring.yaml edge route or cert binding opts into it by Name; everything else keeps using the default edge.acme_ca. Defined ONLY here in the root-of-trust config (an app repo must never be able to introduce a trusted CA).

type EdgeConfig

type EdgeConfig struct {
	Mode             EdgeMode `yaml:"mode"`
	ACMEEmail        string   `yaml:"acme_email"`
	ACMECA           string   `yaml:"acme_ca"`
	CAs              []EdgeCA `yaml:"cas"` // extra named issuers (private CAs) routes/cert-bindings can opt into by name
	ApplyProbeWindow Duration `yaml:"apply_probe_window"`
	L4Enabled        bool     `yaml:"l4_enabled"`      // own a managed L4 (TCP/UDP) load balancer (nginx-stream)
	L4NginxDigest    string   `yaml:"l4_nginx_digest"` // pinned SHA-256 of the nginx binary (optional)
}

EdgeConfig configures the managed edge (plan §3.1, §6).

type EdgeMode

type EdgeMode string

EdgeMode is the managed/external switch (plan §3.1). Default managed.

const (
	EdgeManaged  EdgeMode = "managed"
	EdgeExternal EdgeMode = "external"
)

type EditorBlock

type EditorBlock struct {
	Mode EditorMode `yaml:"mode"`
}

EditorBlock is the strict|review knob shared by the Caddy editor and the compose importer.

type EditorMode

type EditorMode string

EditorMode gates how far the Caddy editor / compose importer may soften lints.

const (
	EditorStrict EditorMode = "strict"
	EditorReview EditorMode = "review"
)

type GitConfig

type GitConfig struct {
	PollInterval *Duration `yaml:"poll_interval"`
}

GitConfig tunes the connected-repo auto-fetch poller. So a repo connected in the dashboard "just works" with no webhook setup, Mooring fetches every connected repo on this cadence — READ-PLANE ONLY: the poller never deploys, it just surfaces an "update available" the operator then deploys with a click. (Push-to-deploy stays an explicit opt-in via the webhook + auto_deploy, never the background poller.)

PollInterval is a pointer so an omitted key (nil → the 2 min default) is distinguishable from an explicit "poll_interval: 0" (disables polling). A negative value also disables.

func (GitConfig) PollIntervalD

func (g GitConfig) PollIntervalD() time.Duration

PollIntervalD returns the effective poll interval (0 when disabled / unset).

type GitHubConfig

type GitHubConfig struct {
	ClientID     string `yaml:"client_id"`
	ClientSecret string `yaml:"client_secret"`
}

GitHubConfig holds the optional "Connect with GitHub" OAuth App credentials. The operator registers an OAuth App once in their GitHub settings and pastes the id + secret here (install-time, SSH — never a browser). When both are set, the dashboard offers one-click repo connect (pick a repo; Mooring installs a read-only deploy key for it — no key pasting). Empty = the feature is simply off.

func (GitHubConfig) Enabled

func (g GitHubConfig) Enabled() bool

Enabled reports whether GitHub connect is configured.

type MonitorConfig

type MonitorConfig struct {
	PollInterval     Duration `yaml:"poll_interval"`
	MetricsRetention Duration `yaml:"metrics_retention"`
}

MonitorConfig tunes the read-plane poller (plan §4 read plane).

type RetentionConfig

type RetentionConfig struct {
	Interval      Duration `yaml:"interval"`        // how often the retention pass runs
	EventsMaxAge  Duration `yaml:"events_max_age"`  // audit rows older than this are prunable
	EventsMaxRows int      `yaml:"events_max_rows"` // hard cap on audit rows (oldest trimmed)
	ArchiveMaxMB  int      `yaml:"archive_max_mb"`  // rotate the security-archive NDJSON past this
}

RetentionConfig is the Tier-1 (SSH-only, SIGHUP-reloadable) audit-retention block (plan §16.1). It bounds the events/audit table so it can never become the disk-wedge that kills the write plane — while NEVER silently dropping a security row (those are archived to NDJSON first, fail-closed).

type ServerConfig

type ServerConfig struct {
	// FileRoots are directories the operator may browse READ-ONLY in the Server
	// tab (e.g. an app's log dir). Each needs a stable name (used in the URL).
	FileRoots []ServerFileRoot `yaml:"file_roots"`
	// DebCacheDir is the directory the operator downloads Mooring .deb packages
	// into; the Server tab can delete OLD ones there (never the running version),
	// behind password+TOTP re-auth. Must be absolute. It also must be within the
	// service's writable paths for the delete to succeed (see docs).
	DebCacheDir string `yaml:"deb_cache_dir"`
}

ServerConfig configures the read-only "Server" tab (host inspection + cleanup). Both knobs are OPT-IN and default to off/empty: with no file_roots the file view is disabled entirely (fail-closed), and with no deb_cache_dir the old-.deb cleanup is unavailable. The host monitor (CPU/memory/disk/processes) needs no config. Secret/state paths (the data dir, config, keys, DB) are ALWAYS denied to the file view regardless of what's listed here — see internal/web.

type ServerFileRoot

type ServerFileRoot struct {
	Name string `yaml:"name"` // [a-z0-9][a-z0-9-]{0,30}; used in the URL/UI
	Path string `yaml:"path"` // absolute directory path
}

ServerFileRoot is one named, browsable directory.

type SessionConfig

type SessionConfig struct {
	IdleTimeout     Duration `yaml:"idle_timeout"`
	AbsoluteTimeout Duration `yaml:"absolute_timeout"`
}

SessionConfig holds idle/absolute timeouts (plan §5.3).

type SetupConfig

type SetupConfig struct {
	Enabled bool `yaml:"enabled"`
	// Image is the digest-pinned jail base image (the throwaway container backend).
	Image string `yaml:"image"`
	// Resource limits — counted against the global one-docker-child semaphore.
	WallClock   Duration `yaml:"wall_clock"`    // hard wall-clock timeout
	CPUs        string   `yaml:"cpus"`          // docker --cpus value, e.g. "1.0"
	MemoryMB    int      `yaml:"memory_mb"`     // hard memory cap (MemorySwapMax=0)
	PidsLimit   int      `yaml:"pids_limit"`    // max processes
	ScratchMB   int      `yaml:"scratch_mb"`    // writable scratch quota
	OutputCapKB int      `yaml:"output_cap_kb"` // captured stdout/stderr cap
}

SetupConfig gates the Mode-3 setup-script sandbox (plan §7/§9, OFF by default, hard-gated). When enabled, scripts run in a throwaway jail with the limits below; the binary refuses to boot if the host can't provide a working sandbox (plan §5.1), and a live self-test runs before EVERY execution.

Jump to

Keyboard shortcuts

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