kbconfig

package
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Jun 11, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package kbconfig vendors the canonical KBConfig Go types from sageox-mono's packages/kb/config.go into the ox CLI. The mono package is the source of truth — ox imports nothing from mono (mono is not a Go module dependency), so this is a hand-maintained vendored copy.

Drift between this file and packages/kb/config.go in mono is a security concern: ResolveEffectiveMode encodes a privacy invariant (safety inversion on session_recording) and field names participate in the wire format read by ox. Run scripts/check-kbconfig-drift.sh on every PR that touches this directory.

ox is a READER of the KB config envelope. The mono service writes .sageox/config.yaml during reconcile; the public PATCH /config endpoint validates inputs. ox does NOT write the envelope and does NOT carry the banner-comment generation or the committed_at field on writes.

Index

Constants

View Source
const (
	SessionRecordingAuto     = "auto"
	SessionRecordingManual   = "manual"
	SessionRecordingDisabled = "disabled"
)

Session recording modes. Text slugs per project convention (no enums). These mirror sageox-mono's packages/kb/config.go constants. Drift checked by scripts/check-kbconfig-drift.sh.

View Source
const (
	VisibilityPrivate = "private"
	VisibilityTeam    = "team"
)

Visibility values. Text slugs per project convention (no enums).

View Source
const (
	KBTypePersonal = "personal"
	KBTypeProfile  = "profile"
	KBTypeTeam     = "team"
	KBTypeRepo     = "repo"
	KBTypeCustom   = "custom"
	KBTypeChannel  = "channel" // pre-shipped before mono rollout
)

KB type constants vendored from mono. The canonical ox KBType definition lives in internal/api/kb.go (typed as api.KBType); this package keeps a parallel set of untyped string constants so kbconfig-internal logic (defaults, validation, drift) doesn't pull in the api package and create an import cycle.

KBTypeChannel is pre-shipped here ahead of mono's channel rollout per /grill-me Q12: defaulting unknown-type-to-manual is the privacy-safe behavior, but having the constant lets us hard-code the expected default and exercise it in tests.

View Source
const ConfigVersion = 1

ConfigVersion mirrors mono's packages/kb/config.go ConfigVersion. Bump only when the schema semantics change (additive fields are backward-compatible).

View Source
const ConfigYAMLPath = ".sageox/config.yaml"

ConfigYAMLPath is the path inside a KB's git repo for the canonical config file. Mirrors mono's packages/kb/config_yaml.go ConfigYAMLPath.

Variables

ValidSessionRecordingModes is the closed set of allowed values.

View Source
var ValidVisibilities = []string{VisibilityPrivate, VisibilityTeam}

ValidVisibilities is the closed set of allowed visibility slugs.

Functions

func DefaultSessionRecordingMode

func DefaultSessionRecordingMode(kbType string) string

DefaultSessionRecordingMode returns the per-KB-type default recording mode. Mirrors mono's packages/kb/config.go DefaultSessionRecordingMode byte-for-byte.

Work surfaces default to auto (personal, team, repo) — these are the bubbles people actively work *in* and recording is the value.

Presence surfaces default to manual (profile, custom, channel) — these are curated public/broadcast bubbles where silent auto-recording would surprise contributors.

Unknown / forward-compat KB types fall through to manual: when in doubt, the privacy-safer choice is to require an explicit opt-in to recording.

Changing a default here flips behavior for every bubble that omits the field. Don't move a type from manual to auto without a comms plan and a one-shot migration that stamps the previous default into still-unset rows.

func ResolveEffectiveMode

func ResolveEffectiveMode(userMode, kbMode string) string

ResolveEffectiveMode combines the user-layer and KB-layer recording modes into the single mode that governs a session. THE only function call site for combining recording layers — every consumer (ox CLI session start, daemon recording gate, future MCP handlers) MUST route through here so the safety-inversion invariant cannot be sidestepped.

INVARIANT (load-bearing — privacy guarantee):

If EITHER userMode == "disabled" OR kbMode == "disabled", the result is
"disabled". This is a logical OR, NOT a precedence rule. A user opting
out of recording cannot be overridden by a KB admin; a KB admin
disabling recording cannot be overridden by an individual user. Both
sides have veto power. Standard precedence (user > kb) only applies
when neither side is "disabled".

Empty string for either layer means "use the other layer's value"; callers that want a default should resolve it before calling this function (see DefaultSessionRecordingMode).

This function MUST stay byte-identical to mono's packages/kb/config.go ResolveEffectiveMode. Drift here is a security bug; scripts/check-kbconfig-drift.sh runs in CI to catch it.

func ValidateConfig

func ValidateConfig(c KBConfig) error

ValidateConfig is the semantic gate for a parsed KBConfig. Every code path that reads a config from disk or the wire and intends to act on it should route through here. ox does not write configs, but ValidateConfig still runs on every read so an upstream-malformed value surfaces before downstream code makes a decision based on it.

Intentionally LENIENT on unknown fields — forward compat for fields a newer mono build wrote that this ox binary doesn't know about. The YAML / JSON decoder enforces structure; ValidateConfig enforces semantics of known fields.

Returns a wrapped error naming the offending field so the message threads through to UI / log without ambiguity.

func ValidateSessionRecordingMode

func ValidateSessionRecordingMode(m string) error

ValidateSessionRecordingMode returns nil if m is one of the known mode slugs. Empty string is rejected — callers that want "not set" should not invoke validation in the first place (Mode is a *string for that reason).

func ValidateVisibility

func ValidateVisibility(v string) error

ValidateVisibility returns nil if v is a known visibility slug. Empty string is rejected — Visibility uses omitempty on its tag, so an unset value never reaches validation.

Types

type ConfigYAMLEnvelope

type ConfigYAMLEnvelope struct {
	// CommittedAt is tolerated on read. Treat as advisory metadata only.
	CommittedAt *time.Time `yaml:"committed_at,omitempty"`

	Version  int            `yaml:"version,omitempty"`
	Features FeaturesConfig `yaml:"features"`
	System   SystemConfig   `yaml:"system,omitempty"`
}

ConfigYAMLEnvelope is the on-disk shape of .sageox/config.yaml in a KB tree (NOT the project-side binding file). Mirrors mono's envelope.

ox reads this format; ox does NOT write it. Writes are server-side (mono reconcile) or hand-edit only. The banner header that mono prepends is a YAML comment — yaml.v3 ignores comments on parse so no special handling is needed.

committed_at is tolerated on read for forward/backward compatibility with older mono builds that may have written the field; it is intentionally surfaced at the envelope level (rather than embedded inside SystemConfig) because mono's current schema places it at the file root. ox callers should ignore this value — git history is the canonical source for "when did this last change".

func UnmarshalConfigYAML

func UnmarshalConfigYAML(data []byte) (*ConfigYAMLEnvelope, error)

UnmarshalConfigYAML parses a .sageox/config.yaml body into an envelope.

Strict by default via KnownFields(true) — mirrors mono's read-side strictness so a typo or wrong field name surfaces immediately rather than silently zeroing out a value. The envelope itself is intentionally a superset of mono's KBConfig (it carries committed_at), so the strict decode still tolerates files written by mono today.

func (*ConfigYAMLEnvelope) ToKBConfig

func (e *ConfigYAMLEnvelope) ToKBConfig() KBConfig

ToKBConfig projects an envelope onto the canonical KBConfig type, dropping the read-only committed_at envelope field. Callers that need the parsed KBConfig (for ValidateConfig, ResolveEffectiveMode inputs, etc.) should go through this method so committed_at semantics stay isolated to the envelope.

type FeaturesConfig

type FeaturesConfig struct {
	// Murmurs controls the murmur feature on this bubble.
	Murmurs MurmursConfig `json:"murmurs" yaml:"murmurs"`

	// Visibility controls bubble discoverability. See VisibilityPrivate /
	// VisibilityTeam. Empty string is treated as VisibilityPrivate by
	// downstream consumers.
	Visibility string `json:"visibility,omitempty" yaml:"visibility,omitempty"`

	// SessionRecording controls AI-coworker session recording for the
	// bubble. Tri-state per ox CLI's existing vocabulary so the two
	// surfaces stay in lockstep. Defaults vary by KB type — see
	// DefaultSessionRecordingMode.
	SessionRecording SessionRecordingConfig `json:"session_recording" yaml:"session_recording"`
}

FeaturesConfig is the user-facing toggle set. Mirrors mono.

type KBConfig

type KBConfig struct {
	// Version tracks the schema shape so future migrations can detect old
	// payloads. Reads tolerate Version==0 (treat as current).
	Version int `json:"version" yaml:"version"`

	// Features is the user-controllable namespace.
	Features FeaturesConfig `json:"features" yaml:"features"`

	// System is the internal namespace. Mono mutates it via internal code
	// paths only; ox treats it as read-only and tolerates new fields.
	System SystemConfig `json:"system" yaml:"system"`
}

KBConfig is the per-bubble dynamic configuration. Two namespaces with different write paths in mono:

  • Features (user-facing) — owners toggle these from the KB settings page.
  • System (internal) — set by mono workflows / watchman only.

ox reads both namespaces; ox never writes either.

type MurmursConfig

type MurmursConfig struct {
	Enabled *bool `json:"enabled,omitempty" yaml:"enabled,omitempty"`
}

MurmursConfig holds murmur-feature toggles. Enabled is a *bool for the same "omitted vs explicit false" reason as SessionRecordingConfig.Mode.

type SessionRecordingConfig

type SessionRecordingConfig struct {
	Mode *string `json:"mode,omitempty" yaml:"mode,omitempty"`
}

SessionRecordingConfig holds the per-bubble recording policy.

Mode is *string so a patch can distinguish "field omitted, keep current value" from "explicitly set". After defaults are applied the pointer is non-nil.

type SystemConfig

type SystemConfig struct {
	// ConfigCommittedAt is tolerated on read. ox does not populate it on
	// any write path. Treat as advisory metadata only — git history is the
	// canonical "when did this last change" source.
	ConfigCommittedAt *time.Time `json:"config_committed_at,omitempty" yaml:"config_committed_at,omitempty"`
}

SystemConfig is the internal namespace.

Mono's current SystemConfig is an empty struct — the earlier ConfigCommittedAt field was removed in favor of relying on git history. ox keeps a tolerated, read-only ConfigCommittedAt pointer here so that envelopes written by older mono builds (or by hand-edited test files) don't trip strict-unmarshal errors. ox never writes this field; it is preserved on read for forward/backward compatibility and ignored by all downstream consumers. See bead ox-jzlm and the [HUMAN] pushback bead.

Jump to

Keyboard shortcuts

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