app

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 7, 2026 License: MIT Imports: 32 Imported by: 0

Documentation

Overview

Package app is the assembly point: it reads configuration from the environment, picks the Postgres or SQLite repositories, builds the domain services and HTTP handlers, registers the routes behind the auth, token scope, rate limit and audit middleware, and owns the server's lifecycle.

Most of what lives here is wiring rather than logic, and the wiring is the hazard — several of the connections it makes fail at runtime rather than at compile time. A domain that is not registered with the reconciler still serves writes; a route class not handled in middleware.go still answers. CONTRIBUTING.md enumerates those sites under "Adding a config domain".

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewRouter

func NewRouter(
	h *servicesettings.Handler,
	fh *flagsconfig.Handler,
	lh *localization.Handler,
	sec *security,
	health http.HandlerFunc,
	inventory http.Handler,
	auditLog http.Handler,
) *http.ServeMux

NewRouter registers the API. Every write goes through sec.guard, which declares how that route's target environment is found so a token can only reach the environments its scope names; every read goes through sec.read, which declares how that route's response is narrowed to the same scope. The probe and scrape routes are the only open ones.

Types

type App

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

func NewApp

func NewApp(cfg *Config) (*App, error)

func (*App) Close

func (a *App) Close() error

Close releases what NewApp opened, for a caller that never reached Run — a test, or a startup that failed after the app was built. Run does its own ordered shutdown instead, because there the listener has to stop and the reconciler has to be told before the database can go.

func (*App) Handler

func (a *App) Handler() http.Handler

Handler is the middleware chain and router exactly as the server serves them. It exists for internal/pgintegration: this package's own end-to-end tests all run on SQLite, and the behaviour that differs between the two — the scope lookup's bind syntax, the audit columns' declared widths — is therefore reachable only from a test outside this package, which cannot build the unexported security type NewRouter takes. Nothing in the binary calls it.

func (*App) Run

func (a *App) Run() error

type Config

type Config struct {
	// DBDriver selects the storage backend: "postgres" (production, the source
	// of truth) or "sqlite" (local test stack only — see internal/database/sqlite.go).
	DBDriver     string
	DBConnString string
	ServerPort   string

	// Connection pool bounds. A zero field takes the database package's own
	// default; they are read here so every tunable an operator sets is visible
	// in one place rather than in whichever package happens to consume it.
	DBMaxOpenConns    int
	DBMaxIdleConns    int
	DBConnMaxLifetime time.Duration
	DBConnMaxIdleTime time.Duration

	// Messaging / JetStream
	NATSURL        string
	NATSCreds      string // optional path to a .creds file
	PublishEnabled bool
	NATSReplicas   int // KV bucket replicas: 1 dev, 3 prod cluster

	// Reconciler
	ReconcileInterval time.Duration

	// Security
	//
	// AdminTokens is the named-token configuration (see parseAdminTokens):
	//   ADMIN_TOKENS=alice:*:s3cr3t,ci-dev:1|2:another-secret
	// AdminToken is the single shared token form, for a deployment that does
	// not need per-environment scopes; it is recorded as the "shared"
	// full-scope actor.
	AdminTokens string
	AdminToken  string

	// WriteRateLimit bounds writes per caller per minute; <= 0 disables it.
	WriteRateLimit int

	// TLS: when both are set the admin API serves HTTPS, otherwise plain HTTP
	// with a startup warning.
	TLSCertFile string
	TLSKeyFile  string

	// Observability. LogFormat "json" emits ECS documents for Elasticsearch;
	// "text" is the readable local-development shape.
	LogLevel       string
	LogFormat      string
	ServiceName    string
	ServiceVersion string
}

func LoadConfig

func LoadConfig() *Config

func (*Config) LogOptions

func (c *Config) LogOptions() obs.LogOptions

LogOptions is the logging configuration in the form the obs package wants.

type Inventory

type Inventory struct {
	Flags           []InventoryFlag            `json:"flags"`
	ServiceSettings []InventoryServiceSettings `json:"serviceSettings"`
	Localization    []InventoryLoc             `json:"localization"`
}

Inventory lists the editable rows with their primary keys. Admin tools need this because the IDs an update targets are data, not something a caller can know up front. Read-only, and it reuses the reconciler's list queries rather than adding new ones.

Each of the three collections is paged independently by the same ?limit and ?offset, so a caller walks all three together; the reconcile queries return every row and the page is taken here, which is why the limit matters — the response is the whole configuration estate otherwise.

Taking the page here also means ?limit bounds only the response, never the read: ListAllForReconcile has no LIMIT and no WHERE, so every request scans all three tables and materialises every document whatever page it was asked for. The read guard meters this route per credential for that reason (middleware.go), which bounds how often that happens but not what one call costs. Closing it properly needs the listers to take a limit, an offset and the caller's scope so the database does the paging — that is a change to the three repository packages, not to this handler.

type InventoryFlag

type InventoryFlag struct {
	ID            int64  `json:"id"` // CONFIG_FLAG_VALUE.ID — the update target
	EnvironmentID int64  `json:"environmentId"`
	FlagKey       string `json:"flagKey"`
	Enabled       int64  `json:"enabled"`
	Value         string `json:"value"`
}

type InventoryLoc

type InventoryLoc struct {
	ID             int64           `json:"id"`
	MicroserviceID int64           `json:"microserviceId"`
	EnvironmentID  int64           `json:"environmentId"`
	Locale         string          `json:"locale"`
	BundleJSON     json.RawMessage `json:"bundleJson"`
}

type InventoryServiceSettings

type InventoryServiceSettings struct {
	ID             int64           `json:"id"`
	MicroserviceID int64           `json:"microserviceId"`
	EnvironmentID  int64           `json:"environmentId"`
	SettingsJSON   json.RawMessage `json:"settingsJson"`
}

Jump to

Keyboard shortcuts

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