Documentation
¶
Overview ¶
Package settings holds Ken's operator-editable runtime configuration. Values come from env/compiled defaults, are overridden by rows in app_setting (written by the web UI), and are exposed as an atomically-swapped Snapshot that live consumers (rate limiter, login guard, client-IP resolver, ACME host policy) read without a restart.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var Fields = []Field{ {Key: "rl_enabled", Group: "Rate limiting", Label: "Enabled", Type: "bool", Live: true, Get: func(v Values) string { return boolStr(v.RLEnabled) }, Set: func(v *Values, s string) error { v.RLEnabled = truthy(s); return nil }}, intField("rl_ip_rpm", "Rate limiting", "Per-IP requests / minute", "Sustained per-IP request rate.", func(v Values) int { return v.IPPerMin }, func(v *Values, n int) { v.IPPerMin = n }, 1, 1_000_000), intField("rl_ip_burst", "Rate limiting", "Per-IP burst", "Bucket size — absorbs short bursts (page loads).", func(v Values) int { return v.IPBurst }, func(v *Values, n int) { v.IPBurst = n }, 1, 1_000_000), intField("rl_token_rpm", "Rate limiting", "Per-token requests / minute", "Per agent token (MCP).", func(v Values) int { return v.TokenPerMin }, func(v *Values, n int) { v.TokenPerMin = n }, 1, 1_000_000), intField("rl_token_burst", "Rate limiting", "Per-token burst", "", func(v Values) int { return v.TokenBurst }, func(v *Values, n int) { v.TokenBurst = n }, 1, 1_000_000), intField("rl_block_after", "Rate limiting", "Auto-block after", "Consecutive over-limit rejections before an IP is blocked (0 = never block).", func(v Values) int { return v.BlockAfter }, func(v *Values, n int) { v.BlockAfter = n }, 0, 1_000_000), intField("rl_lockout_sec", "Rate limiting", "Auto-block lockout (seconds)", "", func(v Values) int { return v.LockoutSec }, func(v *Values, n int) { v.LockoutSec = n }, 1, 7*24*3600), {Key: "rl_allow_cidrs", Group: "Rate limiting", Label: "Always-allowed CIDRs", Type: "cidrs", Live: true, Help: "Comma-separated CIDRs exempt from rate limiting (loopback is always exempt).", Get: func(v Values) string { return v.AllowCIDRs }, Set: setCIDRs(func(v *Values, s string) { v.AllowCIDRs = s })}, intField("login_max_fails", "Login", "Max failed logins", "Failures from one IP before a lockout.", func(v Values) int { return v.LoginMaxFails }, func(v *Values, n int) { v.LoginMaxFails = n }, 1, 10000), intField("login_lockout_sec", "Login", "Login lockout (seconds)", "", func(v Values) int { return v.LoginLockoutSec }, func(v *Values, n int) { v.LoginLockoutSec = n }, 1, 7*24*3600), intField("session_ttl_hours", "Session", "Session lifetime (hours)", "New sessions only.", func(v Values) int { return v.SessionTTLHours }, func(v *Values, n int) { v.SessionTTLHours = n }, 1, 24*30), {Key: "trusted_proxies", Group: "Network", Label: "Trusted proxy CIDRs", Type: "cidrs", Live: true, Help: "X-Forwarded-For is honored only from these peers. Blank = none. Sensitive: over-broad values let a client forge its IP.", Get: func(v Values) string { return v.TrustedProxies }, Set: setCIDRs(func(v *Values, s string) { v.TrustedProxies = s })}, {Key: "tls_mode", Group: "TLS", Label: "TLS mode", Type: "enum", ReadOnly: true, Help: "Set via KEN_TLS in the unit; a mode switch (off/acme/file) needs a service restart.", Get: func(v Values) string { return v.TLSMode }}, {Key: "tls_domains", Group: "TLS", Label: "ACME domains", Type: "domains", Live: true, Help: "Comma-separated hostnames the Let's Encrypt cert is issued for. Live (acme mode); a new host is issued on demand.", Get: func(v Values) string { return v.TLSDomains }, Set: setDomains(func(v *Values, s string) { v.TLSDomains = s })}, {Key: "tls_email", Group: "TLS", Label: "ACME account email", Type: "email", ReadOnly: true, Help: "Set via KEN_TLS_EMAIL; used when registering the Let's Encrypt account (not editable here).", Get: func(v Values) string { return v.TLSEmail }}, {Key: "curation_langs", Group: "Curation", Label: "Curation language(s)", Type: "langs", Live: true, Help: "Comma-separated language codes you can read (e.g. fr,zh). Agents are told to author entries in these so you can review and promote them; proposals outside them are flagged on the review queue. Blank = off.", Get: func(v Values) string { return v.CurationLangs }, Set: setLangs(func(v *Values, s string) { v.CurationLangs = s })}, }
Fields is the ordered registry that drives both the form and validation.
Functions ¶
This section is empty.
Types ¶
type Field ¶
type Field struct {
Key, Group, Label, Help, Type string // type: int | bool | cidrs | domains | langs | email | text | enum
Live bool // applies live vs needs a restart
ReadOnly bool // display only
Get func(Values) string
Set func(*Values, string) error
}
Field describes one editable setting: how to render it and how to parse/validate it into Values.
type Live ¶
type Live struct {
// contains filtered or unexported fields
}
Live holds the current snapshot atomically and applies edits.
func New ¶
New builds a Live seeded with defaults (env-derived). Call Load to fold in the persisted overrides.
func (*Live) Apply ¶
func (l *Live) Apply(ctx context.Context, form map[string]string, updater string) (*Snapshot, []string)
Apply validates the submitted form, persists the diffs from default, and swaps the new snapshot in live. Returns the resulting snapshot and any field errors (on error nothing is persisted or applied).
type Snapshot ¶
type Snapshot struct {
Values
Resolver *clientip.Resolver
AllowNets []*net.IPNet
Domains []string
// CurationLangSet is the normalized curation languages (lowercased BCP-47
// PRIMARY subtags, de-duplicated, in order) — read by the MCP instructions
// and (later) the review-queue guardrail. Empty ⇒ feature off.
CurationLangSet []string
}
Snapshot is Values plus the derived objects consumers read.
type Values ¶
type Values struct {
RLEnabled bool
IPPerMin int
IPBurst int
TokenPerMin int
TokenBurst int
BlockAfter int
LockoutSec int
AllowCIDRs string
LoginMaxFails int
LoginLockoutSec int
SessionTTLHours int
TrustedProxies string
TLSMode string // read-only display (a mode switch needs a listener restart)
TLSDomains string // acme hostnames — live
TLSEmail string // acme account email — live (affects new registrations)
// CurationLangs is the operator's comma-separated list of language codes the
// human curator can read (e.g. "fr,zh"). Blank ⇒ the feature is off: agents get
// no language guidance and nothing is flagged. Stored verbatim; the normalized
// set consumers read is derived into Snapshot.CurationLangSet.
CurationLangs string
}
Values are the raw, editable settings.
func DefaultsFromEnv ¶
func DefaultsFromEnv() Values
DefaultsFromEnv builds the baseline from the same env vars the components read, so the settings UI starts from what the operator configured at launch.