Documentation
¶
Overview ¶
Package addons manages Postgres and Redis addon containers for apps (E2.3): hardened containers on the owning app's network, generated credentials stored age-encrypted and injected into the app as ordinary write-only secrets, and encrypted backups to S3-compatible storage (see backup.go). Everything reaching Docker is a typed struct — no addon name, password or option ever touches a shell (kills the CVE-2025-66209/66210/66211 class).
Index ¶
- Constants
- Variables
- func ContainerName(app, name string) string
- func SecretName(kind string) string
- func VolumeName(app, name string) string
- type BackupView
- type Service
- func (s *Service) Backup(ctx context.Context, actor deploy.Actor, appName, name string) (BackupView, error)
- func (s *Service) Create(ctx context.Context, actor deploy.Actor, appName, kind, name string) (View, error)
- func (s *Service) Delete(ctx context.Context, actor deploy.Actor, appName, name string, removeData bool) error
- func (s *Service) Get(ctx context.Context, actor deploy.Actor, appName, name string) (View, error)
- func (s *Service) List(ctx context.Context, actor deploy.Actor, appName string) ([]View, error)
- func (s *Service) ListBackups(ctx context.Context, actor deploy.Actor, appName, name string, limit int64) ([]BackupView, error)
- func (s *Service) Restore(ctx context.Context, actor deploy.Actor, appName, backupID, targetAddon string) (err error)
- func (s *Service) RunBackup(ctx context.Context, backupID string) (err error)
- func (s *Service) RunScheduler(ctx context.Context, interval, tick time.Duration)
- func (s *Service) ScheduleDue(ctx context.Context, interval time.Duration) int
- type View
- type Worker
Constants ¶
const ( BackupQueued = "queued" BackupRunning = "running" BackupDone = "done" BackupFailed = "failed" )
Backup states.
const ( KindPostgres = "postgres" KindRedis = "redis" )
Kinds of addons.
const ( PostgresImage = "postgres:17-alpine" RedisImage = "redis:7-alpine" )
Pinned images.
const ( StateRunning = "running" StateStopped = "stopped" StateFailed = "failed" )
States.
const ( PermAppsManage = deploy.PermAppsManage PermAppsView = deploy.PermAppsView PermDestructive = "destructive" )
Permissions (mirrors internal/auth by name).
Variables ¶
var ( // ErrInvalid wraps validation failures (HTTP 400). ErrInvalid = deploy.ErrInvalid // ErrNotFound wraps unknown apps/addons/backups (HTTP 404). ErrNotFound = deploy.ErrNotFound // ErrForbidden is returned when the actor lacks a permission (HTTP 403). ErrForbidden = deploy.ErrForbidden )
Functions ¶
func ContainerName ¶
ContainerName / VolumeName / SecretName derive the fixed names for an addon.
func SecretName ¶
func VolumeName ¶
Types ¶
type BackupView ¶
type BackupView struct {
ID string `json:"id"`
AddonID string `json:"addon_id"`
State string `json:"state"`
ObjectKey string `json:"object_key,omitempty"`
SizeBytes int64 `json:"size_bytes"`
Error string `json:"error,omitempty"`
StartedAt string `json:"started_at"`
FinishedAt string `json:"finished_at,omitempty"`
}
BackupView is the API representation of a backup.
type Service ¶
type Service struct {
Store *store.Store
Docker *docker.Client
Keeper *secrets.Keeper
Redactor *secrets.Redactor
Audit audit.Sink
Cfg config.Config
Queue *queue.Client
// S3 is the backup target; nil keeps encrypted backups on the local disk only.
S3 *backup.S3
// S3Prefix namespaces object keys (default "redoubt").
S3Prefix string
Logger *slog.Logger
Now func() time.Time
// TestContainers labels containers redoubt.test=1 (test-suite only).
TestContainers bool
HealthTimeout time.Duration
}
Service is the addon service layer (shared by API, UI and CLI).
func (*Service) Backup ¶
func (s *Service) Backup(ctx context.Context, actor deploy.Actor, appName, name string) (BackupView, error)
Backup records a backup and enqueues the job (transactionally).
func (*Service) Create ¶
func (s *Service) Create(ctx context.Context, actor deploy.Actor, appName, kind, name string) (View, error)
Create provisions an addon: generates a password, stores it encrypted, starts the hardened container on the app's network, and injects the connection URL as an app secret.
func (*Service) Delete ¶
func (s *Service) Delete(ctx context.Context, actor deploy.Actor, appName, name string, removeData bool) error
Delete removes the addon container (and its data volume only when removeData, which requires the destructive permission) and the injected secret.
func (*Service) ListBackups ¶
func (s *Service) ListBackups(ctx context.Context, actor deploy.Actor, appName, name string, limit int64) ([]BackupView, error)
ListBackups lists an addon's backups, newest first.
func (*Service) Restore ¶
func (s *Service) Restore(ctx context.Context, actor deploy.Actor, appName, backupID, targetAddon string) (err error)
Restore loads a backup into targetAddon (same kind), replacing its contents.
func (*Service) RunBackup ¶
RunBackup executes a queued backup: dump inside a hardened job container, age-encrypt, upload (or keep locally when no S3 is configured), record.
func (*Service) RunScheduler ¶
RunScheduler enqueues a backup for every addon whose last successful backup is older than interval, checking every tick. It blocks until ctx is done.
type View ¶
type View struct {
ID string `json:"id"`
App string `json:"app"`
Kind string `json:"kind"`
Name string `json:"name"`
ContainerName string `json:"container_name"`
VolumeName string `json:"volume_name"`
Version string `json:"version"`
State string `json:"state"`
Hardened bool `json:"hardened"`
Problems []string `json:"problems,omitempty"`
// SecretName is the app secret that carries the connection URL (write-only).
SecretName string `json:"secret_name"`
CreatedAt string `json:"created_at"`
}
View is the API representation of an addon (never includes the password).
type Worker ¶
type Worker struct {
river.WorkerDefaults[queue.BackupArgs]
S *Service
}
Worker is the River worker for BackupArgs.