Help us shape the future of Go! Take the 2026 Go Developer Survey

settings

package
v0.37.0 Latest Latest
Warning

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

Go to latest
Published: Oct 18, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package settings persists app-level configuration (AI provider keys, charging cost, Rivian session) in the shared Postgres database.

Storage shape: one row per (user_id, namespace) in the user_settings table, with the value column holding a JSONB object of string keys. The public Store API keeps the flat key/value illusion the Manager, charging and rivian helpers were built against — the namespace is derived from the key's first segment ("ai.*" → "ai", "charging.*" → "charging", "rivian.*" → "rivian").

Index

Constants

View Source
const (
	DefaultGasPricePerGallon = 4.0
	DefaultComparisonMPG     = 20.0
)

Defaults for the gas-equivalent comparison chip on the trip planner. $4/gal and 20 MPG approximate a same-class ICE pickup; both are user-editable.

View Source
const (
	ProviderOpenAI    = "openai"
	ProviderAnthropic = "anthropic"
	ProviderGemini    = "gemini"
)

Supported AI providers.

View Source
const (
	DefaultGPSMissingPct float64 = 0.40
	DefaultGPSStaleSec   int     = 300
	DefaultGPSJumpCount  int     = 2
)

Defaults for the GPS accuracy heuristic. Tuned to surface drives with genuinely bad GPS (long tunnels, modem-side dropouts) while ignoring single anomalies and brief parking-garage stalls.

View Source
const (
	DefaultOpenAIModel      = "gpt-4o-mini"
	DefaultAnthropicModel   = "claude-haiku-4-5-20251001"
	DefaultGeminiModel      = "gemini-2.5-flash"
	DefaultOpenAIImageModel = "gpt-image-1"
	DefaultGeminiImageModel = "gemini-2.5-flash-image"
	// Speech-to-text defaults. Whisper still has a single public model;
	// Gemini uses whatever text model you point at /generateContent with
	// audio inline. gemini-2.5-flash is the cheapest multimodal tier.
	DefaultOpenAISpeechModel = "whisper-1"
	DefaultGeminiSpeechModel = "gemini-2.5-flash"
)

Defaults used when nothing is stored and no env override is present.

View Source
const (
	ImageProviderOpenAI = "openai"
	ImageProviderGemini = "gemini"
)

Supported image generation providers. Anthropic isn't in this set because the Messages API has no image-generation surface.

View Source
const (
	SpeechProviderOpenAI = "openai"
	SpeechProviderGemini = "gemini"
)

Supported speech-to-text providers. Same caveat as image: Anthropic has no audio-input API yet.

View Source
const (
	DriveModeEveryday    = "EVERYDAY"
	DriveModeDistance    = "DISTANCE"
	DriveModeSport       = "SPORT"
	DriveModeWinter      = "WINTER"
	DriveModeOffRoadAuto = "OFF_ROAD_AUTO"
)

Valid driveMode values for Rivian's planTrip2 enum. Names cribbed from the home-assistant-rivian integration's DRIVE_MODE_MAP and confirmed against the live gateway. The mobile app's user-facing labels don't match the enum; mapping below.

EVERYDAY               → "All-Purpose"  (default)
DISTANCE               → "Conserve"     (range-extension)
SPORT                  → "Sport"
WINTER                 → "Snow"
OFF_ROAD_AUTO          → "All-Terrain"

DISTANCE produces materially different plans (one fewer stop + shorter route on long trips) so the knob is load-bearing.

View Source
const DefaultHomeCurrency = "USD"

DefaultHomeCurrency is the ISO-4217 code used when the operator hasn't set anything. Picked to match the app's target audience; the UI exposes the field so it can be changed.

View Source
const FavoritesMax = 25

FavoritesMax caps how many favorites we'll persist. Plenty for a human roster, keeps the JSON blob bounded.

View Source
const LabelMaxLen = 80

LabelMaxLen caps the human label so a rogue paste doesn't blow up the UI chip. Address-shape strings often go ~120 chars; we trim hard so chips stay compact.

Variables

This section is empty.

Functions

func AsOverrides added in v0.18.84

func AsOverrides(networks []ChargingNetwork) []dcfcrates.NetworkOverride

AsOverrides converts a persisted network list into the slug-keyed override slice the tripadvice cost estimator consumes. Custom user-added rows (no Slug) are passed through with their Name so the substring matcher in tripadvice can still hit them.

func ResetChargingNetworks added in v0.18.84

func ResetChargingNetworks(ctx context.Context, s *Store) error

ResetChargingNetworks clears the user's persisted overrides so the next Get returns the seed defaults. Drives the "Reset to defaults" button on the Settings panel.

func RivianReceiptsOptIn added in v0.36.10

func RivianReceiptsOptIn(ctx context.Context, s *Store) bool

RivianReceiptsOptIn reports whether the user opted in. Off by default.

func SetChargingConfig

func SetChargingConfig(ctx context.Context, s *Store, cfg ChargingConfig) error

SetChargingConfig persists the provided cost settings. Negative prices are rejected by coercing to zero so the UI can't persist a nonsensical value.

func SetChargingNetworks

func SetChargingNetworks(ctx context.Context, s *Store, networks []ChargingNetwork) error

SetChargingNetworks persists the provided price book, overwriting any prior value. Empty names and non-positive prices are dropped silently so a partially filled UI form can't corrupt the list. Pass an empty slice to clear the user's overrides; the next Get will return the defaults again.

func SetHomeLocation

func SetHomeLocation(ctx context.Context, s *Store, h HomeLocation) error

SetHomeLocation persists a home location. Pass an empty HomeLocation (Set=false) to clear it.

func SetPlannerFavorites added in v0.18.77

func SetPlannerFavorites(ctx context.Context, s *Store, list []PlannerFavorite) error

SetPlannerFavorites persists the favorites list. Validates each entry's shape (non-zero coords, non-empty label) and caps the total at FavoritesMax — invalid entries are silently dropped to keep the persisted blob clean.

func SetPlannerPrefs

func SetPlannerPrefs(ctx context.Context, s *Store, p PlannerPrefs) error

SetPlannerPrefs persists the defaults. Empty DriveMode clears the stored value; nil HasAdapter clears the stored value.

func SetRivianReceiptsOptIn added in v0.36.10

func SetRivianReceiptsOptIn(ctx context.Context, s *Store, on bool) error

SetRivianReceiptsOptIn records the choice.

Types

type AIConfig

type AIConfig struct {
	Provider       string // "", "openai", "anthropic", "gemini"
	OpenAIModel    string
	OpenAIKey      string
	AnthropicModel string
	AnthropicKey   string
	GeminiModel    string
	GeminiKey      string

	// Image generation is a separate pipeline from text analysis and so
	// has its own provider + per-provider model overrides. The API keys
	// are shared with the text providers above.
	ImageProvider    string // "", "openai", "gemini"
	ImageOpenAIModel string
	ImageGeminiModel string

	// Speech-to-text pipeline (voice notes). Same shape as Image.
	SpeechProvider    string // "", "openai", "gemini"
	SpeechOpenAIModel string
	SpeechGeminiModel string

	// RecapWeatherEnabled toggles the optional weather lookup that
	// runs during trip recap generation. Off by default; the feature
	// hits an external HTTP API (Open-Meteo) so it must be opt-in.
	RecapWeatherEnabled bool

	// GPS accuracy heuristic thresholds applied client-side on the
	// drive detail page. See DefaultGPS* for the meaning of each.
	GPSMissingPct float64
	GPSStaleSec   int
	GPSJumpCount  int
}

AIConfig is the raw persisted AI configuration. Keys are never returned from handlers; this struct is the internal representation.

type AIImagePublic

type AIImagePublic struct {
	Provider  string `json:"provider"`            // "", openai|gemini
	Effective string `json:"effective,omitempty"` // what auto-selection picked
	// Per-provider model overrides. Empty string means "use the default".
	OpenAIModel string `json:"openai_model,omitempty"`
	GeminiModel string `json:"gemini_model,omitempty"`
	Ready       bool   `json:"ready"`
}

AIImagePublic is the subset surfaced to the Settings UI. Per-provider key presence is derived from the top-level Providers map, so we only repeat the model choices and the selected provider here.

type AIPublic

type AIPublic struct {
	Provider  string             `json:"provider"` // "", openai|anthropic|gemini
	Effective string             `json:"effective_provider,omitempty"`
	Model     string             `json:"effective_model,omitempty"`
	Providers map[string]ProInfo `json:"providers"`
	Ready     bool               `json:"ready"`

	// Image is the public view of the image-generation configuration.
	Image AIImagePublic `json:"image"`
	// Speech is the public view of the voice-transcription configuration.
	Speech AISpeechPublic `json:"speech"`
}

AIPublic is the redacted view returned to the UI. Keys are reported only as "set" / "not set"; the actual secret never leaves the server.

type AISpeechPublic

type AISpeechPublic struct {
	Provider    string `json:"provider"`
	Effective   string `json:"effective,omitempty"`
	OpenAIModel string `json:"openai_model,omitempty"`
	GeminiModel string `json:"gemini_model,omitempty"`
	Ready       bool   `json:"ready"`
}

AISpeechPublic mirrors AIImagePublic for the speech-to-text pipeline.

type AIUpdate

type AIUpdate struct {
	Provider       *string `json:"provider,omitempty"`
	OpenAIModel    *string `json:"openai_model,omitempty"`
	OpenAIKey      *string `json:"openai_api_key,omitempty"`
	AnthropicModel *string `json:"anthropic_model,omitempty"`
	AnthropicKey   *string `json:"anthropic_api_key,omitempty"`
	GeminiModel    *string `json:"gemini_model,omitempty"`
	GeminiKey      *string `json:"gemini_api_key,omitempty"`

	// Image generation settings.
	ImageProvider    *string `json:"image_provider,omitempty"`
	ImageOpenAIModel *string `json:"image_openai_model,omitempty"`
	ImageGeminiModel *string `json:"image_gemini_model,omitempty"`

	// Speech-to-text settings.
	SpeechProvider    *string `json:"speech_provider,omitempty"`
	SpeechOpenAIModel *string `json:"speech_openai_model,omitempty"`
	SpeechGeminiModel *string `json:"speech_gemini_model,omitempty"`
}

AIUpdate is the JSON body accepted by PUT /api/settings/ai. A nil pointer means "leave this field alone"; an explicit empty string clears a key.

type ChargingConfig

type ChargingConfig struct {
	// HomePricePerKWh is the retail cost in HomeCurrency per kilowatt
	// hour. Zero means "not configured"; in that case estimated cost
	// is not computed.
	HomePricePerKWh float64 `json:"home_price_per_kwh"`
	// HomeCurrency is the ISO-4217 code displayed next to estimated
	// cost. Defaults to USD.
	HomeCurrency string `json:"home_currency"`
	// GasPricePerGallon and ComparisonMPG feed the gas-equivalent chip
	// on the trip planner. Zero on either disables the chip.
	GasPricePerGallon float64 `json:"gas_price_per_gallon"`
	ComparisonMPG     float64 `json:"comparison_mpg"`
}

ChargingConfig is the user-configurable cost-of-energy settings applied locally to estimate the price of sessions Rivian reports as free (home AC, L2 on non-RAN chargers, etc.).

func GetChargingConfig

func GetChargingConfig(ctx context.Context, s *Store) (ChargingConfig, error)

GetChargingConfig returns the stored home-charging cost settings, filling defaults where unset.

type ChargingNetwork

type ChargingNetwork struct {
	Name         string  `json:"name"`
	PricePerKWh  float64 `json:"price_per_kwh"`
	Currency     string  `json:"currency"`
	MemberPrice  float64 `json:"member_price_per_kwh,omitempty"`
	MemberActive bool    `json:"member_active,omitempty"`
	MemberPlan   string  `json:"member_plan,omitempty"`
	Slug         string  `json:"slug,omitempty"`
	// Preferred = "rank this network high in Rivian's planner".
	// Forwarded as networkPreferences[].preference=1 on plan
	// requests. Unset / false sends nothing for that network.
	Preferred bool `json:"preferred,omitempty"`
}

ChargingNetwork is one entry in the price book. Two use cases share this row:

  1. Manual charge-session pricing on the Charges page (one-click "apply EA rate" for a recorded session). Uses Name + PricePerKWh + Currency.
  2. Trip planner per-stop cost estimation. Adds MemberPrice + MemberActive so the planner can quote both "guest" and "your actual" rates without us tracking memberships behind the scenes. Slug ties a row to the built-in dcfcrates.Networks table for match-pattern lookup (so a charger name like "Electrify America - Pflugerville" still maps to the EA row without the user re-entering patterns).

New fields are all optional with omitempty so the manual-pricing UI keeps deserialising older JSON blobs unchanged.

func DefaultChargingNetworks added in v0.18.84

func DefaultChargingNetworks() []ChargingNetwork

DefaultChargingNetworks returns the seed list derived from dcfcrates.Networks. Same order as the built-in table so the Settings panel renders predictably across reloads.

func GetChargingNetworks

func GetChargingNetworks(ctx context.Context, s *Store) ([]ChargingNetwork, error)

GetChargingNetworks returns the configured price book. When the store has no entry yet, returns the seed defaults from dcfcrates.Networks so the Settings UI always has something to show on first load. The seed result is NOT persisted - the user has to hit Save once before their list diverges from the seed.

type Factory

type Factory struct {
	// contains filtered or unexported fields
}

Factory hands out per-user *Store instances. The settings store is just (db, userID), so a per-user instance is essentially free — caching is purely so callers reuse the same pointer across requests and don't churn the heap on every handler hit.

func NewFactory

func NewFactory(d *sql.DB) *Factory

NewFactory wraps the shared pool.

func (*Factory) For

func (f *Factory) For(userID uuid.UUID) *Store

For returns the *Store for userID, constructing one on first sight.

func (*Factory) RivianReceiptsOptIn added in v0.36.10

func (f *Factory) RivianReceiptsOptIn(ctx context.Context, userID uuid.UUID) bool

RivianReceiptsOptIn is the user's receipts opt-in by id, for callers without a request context (the sync loop).

type GPSPublic

type GPSPublic struct {
	MissingPct float64 `json:"missing_pct"`
	StaleSec   int     `json:"stale_sec"`
	JumpCount  int     `json:"jump_count"`
}

GPSPublic is the redacted view of the GPS accuracy thresholds. Surfaced through /api/config so every page that needs to apply them (drive detail, live panel) reads one source.

type GPSUpdate

type GPSUpdate struct {
	MissingPct *float64 `json:"missing_pct,omitempty"`
	StaleSec   *int     `json:"stale_sec,omitempty"`
	JumpCount  *int     `json:"jump_count,omitempty"`
}

GPSUpdate is the JSON body accepted by PUT /api/admin/settings/gps.

type HomeLocation

type HomeLocation struct {
	Set       bool    `json:"set"`
	Latitude  float64 `json:"latitude"`
	Longitude float64 `json:"longitude"`
	// Label is the short, user-visible name surfaced everywhere
	// the point is referenced (trip planner From: Home, drive
	// timeline "departed Home"). Defaults to "Home" on save when
	// the client doesn't provide one — keeps planner UI compact.
	Label string `json:"label,omitempty"`
	// Address is the full geocoded address kept for context on
	// the Settings card. Never used as the primary label.
	Address string `json:"address,omitempty"`
}

HomeLocation is the user's "home" base — used by the trip planner to offer a one-click Origin / Destination preset, and (eventually) by the recap path to recognise round trips that end at home.

Set is the boolean the UI uses to decide whether to render the Home preset button at all. Latitude/Longitude are the resolved coords; Label is the human-readable name (city + state) so the UI doesn't need to reverse-geocode every time.

func GetHomeLocation

func GetHomeLocation(ctx context.Context, s *Store) (HomeLocation, error)

GetHomeLocation reads the saved home location. Returns Set=false when no location has been configured (zero-value HomeLocation).

type KV

type KV interface {
	GetAll(ctx context.Context) (map[string]string, error)
	Set(ctx context.Context, key, value string) error
	Delete(ctx context.Context, key string) error
}

KV is the minimal key/value backing store the Manager needs. Both the per-user *Store in this package and the install-wide *appsettings.Store satisfy it. The Manager itself doesn't care which it talks to — but in practice Rivolt wires it to appsettings (AI keys are an install concern: the operator pays the LLM bill).

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager owns the current AI configuration and the corresponding Analyzer. It is concurrency-safe; handlers call Analyzer() on every request.

func NewManager

func NewManager(ctx context.Context, store KV, envSeed AIConfig) (*Manager, error)

NewManager loads any persisted config and builds the initial Analyzer. envSeed is merged in for values that are not yet stored, so first-boot behaviour matches the previous env-only configuration.

func (*Manager) Analyzer

func (m *Manager) Analyzer() *ai.Analyzer

Analyzer returns the currently configured analyzer, or nil if AI is disabled (no key for the selected provider).

func (*Manager) GPSPublic

func (m *Manager) GPSPublic() GPSPublic

GPSPublic returns the current GPS accuracy thresholds.

func (*Manager) GeminiCreds

func (m *Manager) GeminiCreds() (apiKey, model string)

GeminiCreds returns the currently stored Gemini API key and model. The caller must treat the key as a secret and never log it. Used by the profile-image generation endpoint which doesn't go through Analyzer.

func (*Manager) ImageCreds

func (m *Manager) ImageCreds() (provider, apiKey, model string)

ImageCreds returns the provider, API key, and model to use for image generation. Provider is one of "openai" or "gemini", or the empty string when no image-capable provider has a key configured. Respects the user's explicit ImageProvider setting and falls back to auto-selection otherwise.

func (*Manager) ListModels

func (m *Manager) ListModels(ctx context.Context, provider string) ([]string, error)

ListModels fetches the catalogue of usable models for a provider, using the stored API key. The caller is expected to have already saved a key; we deliberately don't accept an ad-hoc key here so the UI has one place (PUT /api/settings/ai) that persists secrets.

func (*Manager) OpenAICreds

func (m *Manager) OpenAICreds() (apiKey, model string)

OpenAICreds returns the stored OpenAI API key and text model. Used by non-Analyzer endpoints (e.g. Whisper transcription) that need OpenAI auth directly.

func (*Manager) Provider

func (m *Manager) Provider() ai.Provider

Provider returns the active ai.Provider, or nil when AI is disabled. Features outside of shot analysis (coach, comparator, ask, digest, profile-name) call this directly so they can wrap the provider in their own task-specific helper.

func (*Manager) Public

func (m *Manager) Public() AIPublic

Public returns the redacted settings DTO.

func (*Manager) RecapPublic

func (m *Manager) RecapPublic() RecapPublic

RecapPublic returns the redacted recap settings DTO.

func (*Manager) RecapWeatherEnabled

func (m *Manager) RecapWeatherEnabled() bool

RecapWeatherEnabled returns the current value of the recap weather enrichment toggle. Cheap; safe to call per request.

func (*Manager) SpeechCreds

func (m *Manager) SpeechCreds() (provider, apiKey, model string)

SpeechCreds returns the provider, API key, and model to use for voice transcription. Same contract as ImageCreds: empty provider means "no speech-capable provider has a key configured".

func (*Manager) Update

func (m *Manager) Update(ctx context.Context, patch AIUpdate) (AIPublic, error)

Update applies the patch, persists it, and rebuilds the analyzer. Returns the new public view (or an error if the patch is invalid).

func (*Manager) UpdateGPS

func (m *Manager) UpdateGPS(ctx context.Context, patch GPSUpdate) (GPSPublic, error)

UpdateGPS applies the patch and persists. Values are clamped to non-negative ranges so a malformed payload can't silently disable the heuristic by way of a negative threshold.

func (*Manager) UpdateRecap

func (m *Manager) UpdateRecap(ctx context.Context, patch RecapUpdate) (RecapPublic, error)

UpdateRecap applies the patch and persists. Doesn't touch the AI analyzer because recap settings are orthogonal to provider config.

func (*Manager) VisionCreds

func (m *Manager) VisionCreds() (provider, apiKey, model string)

VisionCreds returns the provider, API key and model to use for image UNDERSTANDING (vision input, e.g. scanning a coffee bag). This is distinct from ImageCreds, which generates images. Vision follows the main chat provider so the bag gets read by whichever LLM you've chosen to analyse your shots. Supported providers: "openai" and "gemini". Anthropic Claude supports vision but isn't wired here yet; if the user's chat provider is Anthropic we fall back to whichever of OpenAI/Gemini has a key. Returns empty provider when no vision- capable key is set.

type NetworkPref added in v0.20.0

type NetworkPref struct {
	NetworkID  string
	Preference int
}

NetworkPref is one entry in a Rivian networkPreferences[] array. Defined here (not in rivian/) because settings is imported by rivian; the api layer maps these to rivian.NetworkPreference.

func NetworkPreferenceList added in v0.20.0

func NetworkPreferenceList(networks []ChargingNetwork) []NetworkPref

NetworkPreferenceList returns Rivian networkId entries for the networks the user marked Preferred. Empty when the user picked none — caller then omits networkPreferences entirely.

Wire shape mirrors the Rivian Android app (3.6 + 3.12 confirmed via apktool): NetworkPreferencesInput has a single `networkId` field; no `preference` value is sent. The app submits ONLY the preferred IDs; networks omitted from the list are not treated as "deprioritise" but as "no opinion." Preference=1 is included on the Go-side struct so the rivian client still has a value, but the GraphQL serializer is what determines whether it goes on the wire.

Networks span multiple operator IDs (RAN = RAN+RWN; FLO = FLOC+FLOU); all share the user's per-slug toggle and we emit one row per ID.

type PlannerFavorite added in v0.18.77

type PlannerFavorite struct {
	ID        string  `json:"id"`
	Label     string  `json:"label"`
	Latitude  float64 `json:"latitude"`
	Longitude float64 `json:"longitude"`
}

PlannerFavorite is one saved place — coordinates plus a short user-visible name. ID is a UUID minted client-side so deletes and renames don't need a separate lookup.

func GetPlannerFavorites added in v0.18.77

func GetPlannerFavorites(ctx context.Context, s *Store) ([]PlannerFavorite, error)

GetPlannerFavorites returns the user's saved favorites. Returns nil (not an error) when nothing has been saved or the stored blob fails to parse — a corrupt blob is treated the same as "not configured" so a single bad write doesn't permanently break the planner page.

type PlannerPrefs

type PlannerPrefs struct {
	// DriveMode is one of EVERYDAY / DISTANCE / SPORT / WINTER /
	// OFF_ROAD_AUTO. Empty string means "no default — let Rivian
	// pick" (the gateway's default is EVERYDAY, so empty and
	// EVERYDAY produce the same plan).
	DriveMode string `json:"drive_mode"`
	// HasAdapter — does the vehicle have the Tesla NACS adapter?
	// Pointer so absent/false/true are all distinguishable on the
	// wire; default is unset.
	HasAdapter *bool `json:"has_adapter,omitempty"`
}

PlannerPrefs are the user's saved trip-planner defaults. The SPA pre-fills the per-trip form from these so the user doesn't have to pick the same drive mode every time.

func GetPlannerPrefs

func GetPlannerPrefs(ctx context.Context, s *Store) (PlannerPrefs, error)

GetPlannerPrefs returns the saved planner defaults. Returns the zero-value PlannerPrefs when nothing has been configured.

type ProInfo

type ProInfo struct {
	Model  string `json:"model"`
	HasKey bool   `json:"has_key"`
}

ProInfo is the per-provider card content for the settings UI.

type RecapPublic

type RecapPublic struct {
	WeatherEnabled bool `json:"weather_enabled"`
}

RecapPublic is the redacted view of the install-wide recap config returned to the UI. Lives in its own DTO (and on its own endpoint) because the recap toggle isn't an AI configuration knob — it's a data-egress switch (Open-Meteo, see internal/weather) that happens to be consumed by an AI feature. Surfacing it under "AI" sent the wrong signal about what disabling AI does/doesn't hide.

type RecapUpdate

type RecapUpdate struct {
	WeatherEnabled *bool `json:"weather_enabled,omitempty"`
}

RecapUpdate is the JSON body accepted by PUT /api/settings/recap. Same nil-pointer-means-untouched contract as AIUpdate.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is a thin key/value wrapper around a Postgres connection pool scoped to a single user.

func OpenStore

func OpenStore(db *sql.DB, userID uuid.UUID) (*Store, error)

OpenStore binds a pooled connection to a user_id. The pool is owned by the caller; Store does not close it.

func (*Store) Close

func (s *Store) Close() error

Close is a no-op; the pool lifecycle is handled by main.

func (*Store) Delete

func (s *Store) Delete(ctx context.Context, key string) error

Delete removes a key. No-op if absent.

func (*Store) GetAll

func (s *Store) GetAll(ctx context.Context) (map[string]string, error)

GetAll returns every stored key/value pair across every namespace, flattened. Keys keep their "ai.openai.model" / "charging.home_currency" shape so callers that were built against the old sqlite KV table don't need to know about namespaces.

func (*Store) Set

func (s *Store) Set(ctx context.Context, key, value string) error

Set writes a single value. Empty string is a valid value ("cleared by operator"); callers that want to delete should use Delete instead. Writes are atomic at the (user_id, namespace) row level.

Jump to

Keyboard shortcuts

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