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 ¶
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 ¶
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) GetAll ¶
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.