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

appsettings

package
v0.40.21 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package appsettings is the install-wide key/value store backing the AI provider configuration (and any future install-scoped setting). It mirrors the per-user internal/settings.Store API — GetAll / Set / Delete returning string values — so the existing settings.Manager can be pointed at it without the Manager caring whether it's reading user_settings or app_settings.

Why a separate table

Per-user settings (charging cost, Rivian session, push subscription) live in user_settings. AI provider keys are install-wide because the deployer pays the LLM bill — a single shared key, set once by the admin, used by every user. Storing it in user_settings would require duplicating the row across every user (and rotating it across every user on key change), or picking an arbitrary "tenant" user to host it on. Both are worse than a dedicated table.

Encryption

Values are envelope-encrypted at the application layer using the same crypto.Sealer that backs internal/secrets. The AAD is a fixed system UUID so a key can't be moved between rows. The `value` column is BYTEA — a `pg_dump` does not exfiltrate the AI keys without also leaking RIVOLT_KEK.

Index

Constants

This section is empty.

Variables

View Source
var SystemUUID = uuid.MustParse("00000000-0000-0000-0000-000000000001")

SystemUUID is the AAD bound into every sealed app_settings row. It's fixed-known so rotation tooling can re-wrap rows in place without per-row metadata. The all-zeros UUID would also work; using a non-nil constant just makes it easy to grep for in audit logs.

Functions

This section is empty.

Types

type Store

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

Store is the install-wide KV. Concurrency-safe (the underlying pool handles concurrency, and the sealer is stateless).

func New

func New(db *sql.DB, sealer crypto.Sealer) (*Store, error)

New builds a Store. db must be non-nil; sealer must be non-nil in production but a NoopSealer is accepted so unit tests don't need to stand up a KEK.

func (*Store) Delete

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

Delete removes a key. Idempotent.

func (*Store) GetAll

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

GetAll returns every key/value pair, decrypted. A row that fails to decrypt is skipped (logged at the caller via the returned error from Open being ErrSealedBlob) — but we never take the whole settings read down because of one corrupt row, for the same reason settings.Store doesn't: an operator rotating a KEK should still be able to load the rest of the install.

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 admin"); callers that want to delete the row should call Delete.

func (*Store) Version added in v0.40.7

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

Version is a cheap fingerprint of the whole table: the row count and the newest updated_at. Every write moves it (a Set bumps updated_at, a Delete changes the count), so a replica can poll it to learn that another replica changed a setting. Rows are few (tens), so this is one trivial query.

Jump to

Keyboard shortcuts

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