Documentation
¶
Overview ¶
Package settings is the typed-sectioned config layer.
Each known section is a Go type with a Validate() method, registered statically in this package's init via Register(). The DB stores one row per section in the `settings` table; the value is opaque JSONB, the typed struct enforces shape on read and write.
Adding a new section: create <section>.go with the typed struct, implement Validate() error, call Register() from its init().
Hot-path consumers read from the in-memory Cache populated by the catalog reconciler; the Store is admin-plane only.
Index ¶
- Constants
- Variables
- func Governs(r Reader, op Op, kind, ownerKind string) error
- func Names() []string
- func OAuthSection(provider string) string
- func Register(sec Section)
- func RegisterOAuthProvider(provider string)
- func SeedDir(ctx context.Context, store *Store, dir string) ([]string, error)
- func SetSchemaRef(name, ref string)
- type AuthOIDC
- type CatalogSource
- type Governance
- type Inference
- type MutationError
- type OAuthProvider
- type Op
- type Parsing
- type PayloadClickHouse
- type PayloadFile
- type PayloadLogging
- type PayloadS3
- type ProxyMode
- type Reader
- type Row
- type Section
- type SectionName
- type Store
- type UsageClickHouse
- type UsageFile
- type UsageLogging
Constants ¶
const ( SectionGovernanceProvider = "governance:provider" SectionGovernanceHost = "governance:host" SectionGovernanceModel = "governance:model" SectionGovernancePolicy = "governance:policy" )
Governance section keys. Colon-namespaced so they double as future RBAC permission targets (governance:<resource>). Only the catalog-managed kinds need a section; purely user-owned kinds fall through to the user tier in Governs.
const AuthOIDCSection = "auth:oidc"
AuthOIDCSection is the section name.
const OAuthSectionPrefix = "oauth:"
OAuthSectionPrefix namespaces per-provider OAuth sections (oauth:anthropic, oauth:openai, …), mirroring the governance:<kind> convention.
const SectionCatalogSource = "catalog-source"
SectionCatalogSource is the section key for the catalog-source marker — the record of which public-catalog version the DB's template rows were last seeded from.
const SectionInference = "inference"
SectionInference is the section key for normal-mode inference behavior knobs — settings that affect /v1/* traffic served via an authenticated RelayKey. Distinct from proxy-mode (BYO-creds), which has its own section.
const SectionParsing = "parsing"
SectionParsing is the section key for request-parsing behavior knobs — how much structure the relay extracts from inbound request bodies before dispatch. Applies across flows (authenticated /v1/* and proxy-mode), so it is its own section rather than living under inference or proxy-mode.
const SectionPayloadLogging = "payload-logging"
SectionPayloadLogging is the section key for the request/response body capture observer's runtime config. Mutable at runtime via the settings API — the payload observer reconciles the live value and hot-swaps its sink (toggle, backend, bucket, credentials) without a restart.
const SectionProxyMode = "proxy-mode"
SectionProxyMode is the section key for the proxy-mode settings — the singleton that gates anonymous-key (BYO upstream creds) inference traffic served by the future app/proxy flow.
const SectionUsageLogging = "usage-logging"
SectionUsageLogging is the section key for the log (usage) event sink's backend selection. The log observer reconciles the live value and hot-swaps its sink + reader without a restart. Mirrors payload-logging, minus the per-request opt-in (logging is constant — every request).
Connection-level config (DSNs) stays bootstrap-tier (boot env), the same way the PG DSN does: you need a connection before you can read settings. Only the backend selection + safe per-backend knobs live here.
Variables ¶
var GovernanceSections = []string{ SectionGovernanceProvider, SectionGovernanceHost, SectionGovernanceModel, SectionGovernancePolicy, }
GovernanceSections lists every registered governance:<kind> section key.
Functions ¶
func Governs ¶
Governs decides whether op may be applied to a row of the given kind with the given ownerKind. It is the single source of truth for mutation governance; httpapi calls it and maps a non-nil result to 403.
Owner tiers are hardcoded invariants (not operator-toggleable):
- system → never deleted; edited only via limited APIs (the settings API and specialized endpoints), never generic CRUD. Editing or deleting a system row can break the whole router.
- user → always allowed (the row is the caller's). RBAC will later add an owner.id == caller match here.
- else (provider/host-owned, i.e. catalog-managed) → consult the kind's governance:<kind> section; absent or unregistered ⇒ the safe default (edit allowed, delete denied).
func Names ¶
func Names() []string
Names returns all registered section names in lexical order. Used by the list endpoint and the cache to enumerate sections.
func OAuthSection ¶ added in v0.4.2
OAuthSection returns the settings section name for a provider.
func Register ¶
func Register(sec Section)
Register adds sec to the section registry. Called from each section's init(). Panics on duplicate registration — the typo would otherwise be silent.
func RegisterOAuthProvider ¶ added in v0.4.2
func RegisterOAuthProvider(provider string)
RegisterOAuthProvider registers the oauth:<provider> settings section so its config can be seeded and edited via the settings API. Call once per provider (e.g. from a vendor wiring file in the composition root) before settings are seeded or served. Panics on duplicate registration.
func SeedDir ¶
SeedDir loads settings from manifest YAML in dir and upserts each section that has NO existing DB row — **seed-if-absent**. It never clobbers a runtime change or a prior seed, so a live `PUT /settings/...` stays authoritative. This is the first-boot / airgapped path; managed deployments are configured at runtime via the settings API.
Settings use the same Kubernetes-style manifest envelope as every other catalog resource — `apiVersion` + `kind: Setting` + `metadata.name` (the section key) + `spec` (the section's typed value). Files may hold multiple `---`-separated docs. metadata.name selecting an unregistered section, a spec that fails the section's Decode, or a non-Setting kind in the tree are all hard errors (fail fast on a typo). A missing dir is a no-op.
Returns the section names actually seeded, lexically sorted.
func SetSchemaRef ¶
func SetSchemaRef(name, ref string)
SetSchemaRef records the OpenAPI component name for section's typed value. Called by the HTTP layer right after huma registers the typed GET/PUT pair, so the per-section schema id is discoverable via the list / sections endpoints.
Types ¶
type AuthOIDC ¶ added in v0.7.0
type AuthOIDC struct {
Enabled bool `json:"enabled"`
// Issuer is the provider's issuer URL; endpoints are discovered from
// its metadata document.
Issuer string `json:"issuer,omitempty"`
ClientID string `json:"clientId,omitempty"`
// ClientSecretEnv names the env var holding the client secret.
// Indirection keeps the secret out of the settings row (world-readable
// to any authenticated operator).
ClientSecretEnv string `json:"clientSecretEnv,omitempty"`
// RedirectURL is this relay's public callback,
// e.g. https://relay.example.com/api/auth/oidc/callback.
RedirectURL string `json:"redirectUrl,omitempty"`
// Scopes defaults to ["openid", "profile", "email"].
Scopes []string `json:"scopes,omitempty"`
// AuthParams are extra authorization-URL query params some providers
// require beyond the OIDC standard set (e.g. WorkOS AuthKit needs
// {"provider": "authkit"}). Provider-specific values live here in
// config, never in code.
AuthParams map[string]string `json:"authParams,omitempty"`
// Registration gates first-login auto-provisioning: "open" creates a
// user row on first successful OIDC login; "closed" (default) rejects
// subjects with no existing user row.
Registration string `json:"registration,omitempty"`
}
AuthOIDC is the auth:oidc settings section: inbound OpenID Connect login for the control plane. Disabled by default — the zero value changes nothing about a deployment. When enabled, GET /auth/oidc/start redirects to the provider and /auth/oidc/callback exchanges the code and mints a normal relay session, so everything downstream of login is unchanged.
The provider is generic OIDC: any issuer publishing a discovery document (RFC 8414 / OpenID discovery) works. The client secret is referenced by env var name, never stored in the section value.
func AuthOIDCFrom ¶ added in v0.7.0
AuthOIDCFrom reads the typed section from a settings Reader, tolerating absent or mistyped values (returns the zero value → disabled).
func (*AuthOIDC) EffectiveScopes ¶ added in v0.7.0
EffectiveScopes returns Scopes or the OIDC default set.
func (*AuthOIDC) OpenRegistration ¶ added in v0.7.0
OpenRegistration reports whether first-login auto-provisioning is on.
type CatalogSource ¶ added in v0.5.0
type CatalogSource struct {
// Version is the catalog ref (tag) the last versioned seed ran from,
// e.g. "v0.1.0". Empty when the catalog was seeded without a version
// (local dir / embedded first-boot seed) or never seeded.
Version string `json:"version,omitempty"`
// SeededAt is when that seed completed (RFC 3339).
SeededAt string `json:"seededAt,omitempty"`
}
CatalogSource records the provenance of the seeded catalog. Written by the bootstrap seeder after a successful versioned seed; compared against RELAY_CATALOG_VERSION at boot to decide whether a re-seed is due. An operator can blank Version via the settings API to force a re-seed on the next boot. Re-seeds only touch pristine template rows: operator- edited (dirty) rows are skipped and overlays re-merge at snapshot load, so user changes survive.
func (*CatalogSource) Validate ¶ added in v0.5.0
func (c *CatalogSource) Validate() error
type Governance ¶
Governance is the per-resource mutation policy: whether a catalog-managed row of a given kind may be edited or deleted through the generic control CRUD API. One value type is shared by every governance:<kind> section so the shape — and the decision logic in Governs — stays uniform.
These toggles are a speed-bump against accidental mutation, not a wall: editing catalog rows is rare but safe (the router self-heals; nothing hard-depends on a specific row surviving), so AllowEdit defaults true. Deleting is the click-around risk, so AllowDelete defaults false and an operator flips it on when they actually mean to prune.
func (*Governance) Validate ¶
func (g *Governance) Validate() error
type Inference ¶
type Inference struct {
// AllowMissingPolicy permits a RelayKey with an empty Spec.PolicyID
// to reach inference endpoints. The request bypasses the per-policy
// authorization gate: any model served by any host the relay has
// hostkeys for is reachable, with no policy-level rate limits (system
// ratelimits still apply). When false, requests from such keys are
// rejected with 403.
//
// Default false. Turn on only for self-hosted setups where the
// operator is the caller (single-tenant) and wants a god-mode key.
AllowMissingPolicy bool `json:"allowMissingPolicy"`
}
Inference controls authenticated-flow behavior.
type MutationError ¶
MutationError is returned by Governs when an operation is not permitted. The control layer maps it to a 403.
func (*MutationError) Error ¶
func (e *MutationError) Error() string
type OAuthProvider ¶ added in v0.4.2
type OAuthProvider struct {
oauth.ProviderConfig
}
OAuthProvider is the typed value of an oauth:<provider> settings section: the flow config (authorize/token endpoints, client id, scopes, authorize params) used to acquire and refresh that provider's OAuth credentials.
The relay core ships only this generic shape. Concrete provider values are operator/community config — register a provider's section with RegisterOAuthProvider and supply the values via the settings API or a config/settings/oauth-<provider>.yaml. No provider is registered by default.
func (*OAuthProvider) Validate ¶ added in v0.4.2
func (c *OAuthProvider) Validate() error
Validate enforces the minimum a flow + refresh needs.
type Parsing ¶
type Parsing struct {
// RichParsing extracts per-request metadata and messages from the
// body (for attribution / observability). When false the relay
// reads only the minimal fields needed to route (model, stream),
// leaving metadata and messages unparsed — lower CPU on the hot
// path, no body-level observability.
//
// Default true. Hot-swappable: a change takes effect on the next
// request within a reconcile interval, no restart.
RichParsing bool `json:"richParsing"`
}
Parsing controls inbound request-body parsing depth.
type PayloadClickHouse ¶
type PayloadClickHouse struct {
// RetentionDays overrides the MergeTree TTL; 0 uses the backend default.
RetentionDays int `json:"retentionDays,omitempty"`
// WALDir overrides the local WAL segment directory; empty uses the
// boot default.
WALDir string `json:"walDir,omitempty"`
}
PayloadClickHouse configures the ClickHouse backend (Langfuse-style: text bodies as ZSTD String columns, queryable). The DSN is NOT stored here — it reuses the relay's boot CH connection (RELAY_CH_DSN, the same cluster the usage sink uses), so no credentials live in this row. Only the per-backend knobs that are safe to hot-swap live here.
type PayloadFile ¶
type PayloadFile struct {
Path string `json:"path"`
}
PayloadFile configures the JSONL file backend.
type PayloadLogging ¶
type PayloadLogging struct {
// Enabled is the global master switch. When false the observer
// produces nothing regardless of per-request opt-in.
Enabled bool `json:"enabled"`
// Backend selects the sink: "file" (default), "s3", or "clickhouse".
Backend string `json:"backend"`
// MaxBytes caps each stored body, measured on the DECODED plaintext
// (compressed upstream bodies are decoded before capture); 0 = unlimited.
MaxBytes int `json:"maxBytes"`
File PayloadFile `json:"file"`
S3 PayloadS3 `json:"s3"`
CH PayloadClickHouse `json:"clickhouse"`
}
PayloadLogging configures the payloadlog observer. Off by default; the per-request opt-in (policy/relaykey) still gates capture on top of Enabled. Credentials are secret.Refs resolved via pkg/secret, so they can live in env, encrypted-PG, or a future external backend — never as plaintext in this row.
func (*PayloadLogging) Validate ¶
func (p *PayloadLogging) Validate() error
Validate is enforced before any write. Only meaningful when Enabled — a disabled section can hold partial config (e.g. while an operator fills in S3 details before flipping it on).
type PayloadS3 ¶
type PayloadS3 struct {
Endpoint string `json:"endpoint"`
Bucket string `json:"bucket"`
Region string `json:"region,omitempty"`
Prefix string `json:"prefix,omitempty"`
UseSSL bool `json:"useSSL"`
AccessKey secret.Ref `json:"accessKey"`
SecretKey secret.Ref `json:"secretKey"`
}
PayloadS3 configures the object-store backend. AccessKey/SecretKey are secret.Refs (kind env or stored), resolved at sink-build time.
type ProxyMode ¶
type ProxyMode struct {
// Enabled turns the proxy flow on. When false, requests without a
// valid relay key get 401, regardless of any other field below.
Enabled bool `json:"enabled"`
// AllowUnauthenticated lets proxy callers reach the upstream with
// no relay-side auth — the inbound Authorization header is forwarded
// as-is. When false, the proxy flow still requires a relay key
// (proxied traffic gets logged against that key) but the upstream
// credential comes from the caller.
AllowUnauthenticated bool `json:"allowUnauthenticated"`
}
ProxyMode controls whether the relay accepts inference requests where the caller brings their own upstream provider key instead of a relay key. The flow lives in app/proxy (not yet built); this section gates the dispatch at the inference handler.
type Reader ¶
Reader is the narrow read surface Governs needs — satisfied by *catalog.Catalog. Settings cache reads are lock-free and total (a registered section always returns at least its Defaults).
type Row ¶
Row is a single settings row, decoded into a typed value via its section's Decode func. UpdatedAt is informational.
type Section ¶
type Section struct {
Name string
Description string
Defaults func() any
Decode func([]byte) (any, error)
SchemaRef string
}
Section describes one known settings section. Defaults returns a fresh zero-value instance; Decode parses raw JSON into a typed value and validates it; SchemaRef is the OpenAPI component name of the typed value's schema (set automatically by the per-section registerSettingsSection helper); Description is operator-facing prose explaining the section's purpose.
type SectionName ¶
type SectionName string
SectionName is a typed string whose OpenAPI schema is a string enum of every registered section. Use it in handler request/response types instead of bare `string` so the generated spec carries the closed set of valid section keys.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is the data-access layer for settings rows.
func (*Store) Get ¶
Get returns the typed value for section. When no row exists, the section's Defaults() is returned with UpdatedAt zero — so the read path is total: every registered section always has a value.
type UsageClickHouse ¶
type UsageClickHouse struct {
RetentionDays int `json:"retentionDays,omitempty"`
WALDir string `json:"walDir,omitempty"`
}
UsageClickHouse holds the safe-to-hot-swap CH knobs. The DSN reuses the boot CH connection (bootstrap-tier), so no credentials live in this row.
type UsageFile ¶
type UsageFile struct {
Path string `json:"path,omitempty"`
}
UsageFile configures the JSONL file backend.
type UsageLogging ¶
type UsageLogging struct {
// Backend selects the sink+reader: "file" (default), "clickhouse",
// "postgres", or "valkey".
Backend string `json:"backend"`
File UsageFile `json:"file"`
CH UsageClickHouse `json:"clickhouse"`
}
UsageLogging selects and tunes the log/usage event backend. Per the config direction (minimize env), the backend choice lives here, not in env; the legacy RELAY_EVENTLOG_BACKEND is honored only as an interim fallback when this section is unset, until the YAML→DB settings seed lands.
func (*UsageLogging) Validate ¶
func (u *UsageLogging) Validate() error
Validate enforces the backend enum + non-negative knobs.