Documentation
¶
Overview ¶
Package runtime is the persistence layer for tiny operator-toggleable settings that need to survive process restart and be writable at runtime from an API endpoint (e.g. PUT /api/config/media).
The file lives at <DataDir>/runtime_overrides.json and is the highest- precedence config layer per phase-k-media-contract.md § 6:
default < yaml < env < runtime_overrides.json
Why a separate package (not config.go):
- config.Load() reads a static YAML at startup; this layer is writable at runtime by the API server. Keeping it separate makes the "static vs mutable" boundary explicit.
- Pointer-valued fields (*bool) distinguish "no override set" from "override set to false" so absence falls through to env/yaml/default.
Atomicity: SaveOverride writes a sibling .tmp file and renames; readers never observe a partial JSON document.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func SaveOverride ¶
SaveOverride atomically updates one or more fields in runtime_overrides.json by:
- Loading the current file (or empty Overrides if missing).
- Applying mutate(&ov) so the caller can set just the field it owns without clobbering siblings.
- Marshalling, writing to a .tmp sibling, then os.Rename — readers see either the old file or the new one, never a torn write.
The directory is created with 0o700 if it does not exist; matches the rest of the data dir tree (the writer + capture also use 0o700) since the same tree carries raw API keys via JSONL siblings.
Types ¶
type MediaOverrides ¶
type MediaOverrides struct {
SaveAttachments *bool `json:"save_attachments,omitempty"`
}
MediaOverrides toggles the media extraction subsystem at runtime.
SaveAttachments is *bool so we can distinguish:
- nil: no override; use yaml or hardcoded default
- &true: force extraction on
- &false: force extraction off
type Overrides ¶
type Overrides struct {
Media MediaOverrides `json:"media"`
Plugins *PluginsOverride `json:"plugins,omitempty"`
Retention *RetentionOverrides `json:"retention,omitempty"`
}
Overrides is the on-disk shape of runtime_overrides.json.
Every nested struct uses pointer fields so absent == nil (== "fall through to the layer below"); a present false value is meaningful and MUST not collapse into "unset".
func LoadOverrides ¶
LoadOverrides reads <dataDir>/runtime_overrides.json.
A missing file is NOT an error: it returns the zero Overrides{} so callers can treat "no override" as the common case without special- casing os.IsNotExist at every callsite.
A malformed file IS an error — silently swallowing a parse failure would hide configuration drift from the operator.
type PluginInstanceOverride ¶
type PluginInstanceOverride struct {
Type string `json:"type"`
ID string `json:"id"`
Enabled *bool `json:"enabled,omitempty"`
Config map[string]any `json:"config,omitempty"`
}
PluginInstanceOverride is one configured plugin instance as stored on disk. Field semantics:
- Type, ID: identifying tuple. Operator chooses ID (unique across all instances). Both are required when an instance is persisted.
- Enabled: *bool because a single-instance PATCH may carry "config only" or "enabled only"; nil means "do not change this field" when applied by PUT /api/config/plugins/{id}. For full-list PUTs (the common path) clients SHOULD send a concrete bool.
- Config: per-type config blob. nil pointer (map==nil) means "do not change this field" on PATCH; an empty map means "clear overrides for this instance."
The pointer-on-Enabled choice mirrors MediaOverrides.SaveAttachments — absent in the wire format MUST be distinguishable from `false`.
type PluginsOverride ¶
type PluginsOverride struct {
Instances []PluginInstanceOverride `json:"instances"`
}
PluginsOverride is the runtime-override block for the plugin instance list, mirroring plugin-b-c-spec §3.3.2 / §3.3.3.
The bug-prone distinction is between a nil pointer and a non-nil pointer with an empty Instances slice:
- Overrides.Plugins == nil → no override; YAML wins.
- Overrides.Plugins != nil with Instances == nil OR len() == 0 → "operator turned all plugins off"; override wins, no plugins run.
- Overrides.Plugins != nil with a non-empty Instances → full replace of the YAML list.
Because of that, Instances has NO omitempty: an explicit empty array must round-trip through json.Marshal → json.Unmarshal as the same non-nil empty slice we just wrote.
type RetentionOverrides ¶ added in v0.1.1
type RetentionOverrides struct {
MaxBytes *int64 `json:"max_bytes,omitempty"`
MaxAgeDays *int `json:"max_age_days,omitempty"`
WarnAtPercent *int `json:"warn_at_percent,omitempty"`
}
RetentionOverrides toggles the storage-coordinator retention loop at runtime. Mirrors storage.RetentionConfig but with pointer fields so "operator set max_bytes to 0" (i.e. disable the byte cap) is distinguishable from "operator hasn't touched this knob" (fall through to default).
- nil pointer (Overrides.Retention == nil): no override at all; the coord starts with retention disabled per startup default.
- non-nil pointer, all sub-pointers nil: deliberately-empty override written by an earlier PUT; equivalent to retention disabled.
- any sub-pointer set: apply that value to coord.UpdateConfig at startup, then leave the rest at storage.RetentionConfig defaults.