Documentation
¶
Index ¶
- Constants
- func ExpandPlaceholders(data []byte) ([]byte, error)
- func LogStartupSummary(result LoadResult)
- func ValidateOperational(ctx context.Context, cfg Config) error
- func ValidateSemantic(cfg Config) error
- func ValidateStructural(cfg Config) error
- type AuthConfig
- type AuthMode
- type Config
- type ControlPlaneConfig
- type DevConfig
- type DispatcherConfig
- type Duration
- type KafkaConfig
- type LoadOptions
- type LoadResult
- type LocalIAMConfig
- type OIDCRoleMapping
- type ObservabilityConfig
- type PlatformOIDCConfig
- type Profile
- type ServerConfig
- type Source
- type StoreConfig
- type TokenEntry
- type ValidationError
- type ValidationErrors
Constants ¶
const CurrentVersion = 1
CurrentVersion is the supported config schema version.
Variables ¶
This section is empty.
Functions ¶
func ExpandPlaceholders ¶
ExpandPlaceholders expands ${VAR} in data using os.Getenv. Exported for use by the `midas config validate` CLI command.
func LogStartupSummary ¶
func LogStartupSummary(result LoadResult)
LogStartupSummary emits a structured log line summarising the active configuration. Secrets are masked:
- Store DSN: host and database only; credentials stripped.
- Auth tokens: count and principal IDs logged; token values never appear.
- OIDC client_secret: never logged under any circumstances.
func ValidateOperational ¶
ValidateOperational performs live connectivity checks. It opens and pings the configured Postgres instance. Both dev and production profiles fail on an unreachable database — there is no degraded mode.
A no-op (nil error) is returned when store.backend is "memory".
This function is intended for the `midas config validate` CLI command and for startup validation in main.go. The open+ping+close adds a small but acceptable overhead at startup.
func ValidateSemantic ¶
ValidateSemantic checks cross-field business rules that cannot be expressed as single-field constraints. It does not open any connections.
Checks:
- postgres store requires a non-empty DSN
- production profile requires postgres store + required auth mode
- required auth mode requires at least one token entry
- each token entry must have non-empty token and principal fields
- dispatcher.enabled=true requires a real publisher (not "none" or "")
- dispatcher.publisher=kafka requires at least one broker
func ValidateStructural ¶
ValidateStructural checks that all field values are within the defined enumeration of valid values. It does not check cross-field consistency (that is ValidateSemantic's job) and does not open any connections.
Checks:
- version is 0 (unset) or CurrentVersion
- profile is "dev" or "production"
- auth.mode is "open" or "required"
- store.backend is "memory" or "postgres"
- observability.log_level is debug/info/warn/error
- observability.log_format is "json" or "text"
- server.port is 1–65535
Types ¶
type AuthConfig ¶
type AuthConfig struct {
// Mode controls whether auth is enforced.
// "open" — no authentication required (dev/local only).
// "required" — bearer token mandatory on all governed routes.
Mode AuthMode `yaml:"mode"`
// Tokens lists static bearer token entries.
// Token values support placeholder expansion: "${MY_SECRET_TOKEN}".
Tokens []TokenEntry `yaml:"tokens"`
}
AuthConfig controls authentication enforcement on governance routes.
type AuthMode ¶
type AuthMode string
AuthMode controls whether inbound governance requests must carry credentials.
type Config ¶
type Config struct {
Version int `yaml:"version"`
Profile Profile `yaml:"profile"`
Server ServerConfig `yaml:"server"`
Store StoreConfig `yaml:"store"`
Auth AuthConfig `yaml:"auth"`
LocalIAM LocalIAMConfig `yaml:"local_iam"`
PlatformOIDC PlatformOIDCConfig `yaml:"platform_oidc"`
Observability ObservabilityConfig `yaml:"observability"`
ControlPlane ControlPlaneConfig `yaml:"control_plane"`
Dev DevConfig `yaml:"dev"`
Dispatcher DispatcherConfig `yaml:"dispatcher"`
Kafka KafkaConfig `yaml:"kafka"`
}
Config is the top-level runtime configuration for MIDAS. Load midas.yaml or supply env var overrides; see loader.go.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns the baseline configuration used when no file is present. All defaults are safe for local development. Production deployments are expected to supply a midas.yaml that overrides store, auth, and profile.
type ControlPlaneConfig ¶
type ControlPlaneConfig struct {
// Enabled controls whether /v1/controlplane/* routes are registered.
Enabled bool `yaml:"enabled"`
}
ControlPlaneConfig enables or disables the control plane endpoints.
type DevConfig ¶
type DevConfig struct {
// SeedDemoData seeds a minimal demonstration dataset at startup when true.
// The seed is idempotent — running it on a store that already contains the
// demo surface is a no-op. Defaults to true so that `make dev` / memory mode
// works out of the box; set false in production or when supplying real data.
SeedDemoData bool `yaml:"seed_demo_data"`
// SeedDemoUser creates a demo Local IAM user (username: demo, password: demo)
// with the platform.operator role at startup when true. The seed is idempotent.
// NEVER enable this in production — it creates a well-known credential.
SeedDemoUser bool `yaml:"seed_demo_user"`
}
DevConfig holds settings that only apply in developer / local mode. These settings are intentionally non-operational: they only affect startup behaviour (e.g. seeding) and are safe to leave disabled in production.
type DispatcherConfig ¶
type DispatcherConfig struct {
// Enabled controls whether the dispatcher goroutine is started.
Enabled bool `yaml:"enabled"`
// Publisher selects the broker implementation. Valid: "none", "kafka".
Publisher string `yaml:"publisher"`
// BatchSize is the maximum outbox rows claimed per poll cycle. Default: 100.
BatchSize int `yaml:"batch_size"`
// PollInterval is the sleep between poll cycles when the queue is empty. Default: "2s".
PollInterval Duration `yaml:"poll_interval"`
// MaxBackoff is the upper bound for exponential sleep on consecutive errors. Default: "30s".
MaxBackoff Duration `yaml:"max_backoff"`
}
DispatcherConfig controls the outbox dispatcher goroutine.
type Duration ¶
Duration wraps time.Duration to support YAML unmarshaling from strings like "30s".
func (Duration) MarshalYAML ¶
MarshalYAML serialises a Duration back to a Go duration string.
type KafkaConfig ¶
type KafkaConfig struct {
// Brokers is the list of seed broker addresses in "host:port" form.
Brokers []string `yaml:"brokers"`
// ClientID is an optional identifier sent to the broker for observability.
ClientID string `yaml:"client_id"`
// RequiredAcks controls acknowledgement level: -1=all ISRs, 0=none, 1=leader.
RequiredAcks int `yaml:"required_acks"`
// WriteTimeout bounds the per-message publish call. Zero means no timeout.
WriteTimeout Duration `yaml:"write_timeout"`
}
KafkaConfig holds Kafka broker settings for the Kafka publisher. Only meaningful when DispatcherConfig.Publisher is "kafka".
type LoadOptions ¶
type LoadOptions struct {
// ConfigFile is an explicit path to the YAML config file.
// When set, file discovery is skipped. Pass "" to use automatic discovery.
ConfigFile string
// SearchPaths overrides the default file discovery path list.
// nil → use candidatePaths (default).
// []string{} (empty, non-nil) → skip file discovery entirely.
// Useful in tests to prevent accidentally loading the repo-root midas.yaml.
SearchPaths []string
// EnvOverride replaces os.Getenv for all environment variable reads.
// Nil means use os.Getenv. Set in tests to supply a fake environment.
EnvOverride func(string) string
}
LoadOptions controls how Load discovers and reads configuration.
type LoadResult ¶
type LoadResult struct {
// Config is the fully merged configuration (defaults → file → env).
Config Config
// Sources tracks which layer each top-level section was last written by.
// Keys are YAML field names; values are SourceDefault, SourceFile, SourceEnv.
Sources map[string]Source
// File is the path to the config file that was read, or "" if none was found.
File string
// Warnings are non-fatal notices generated during loading
// (e.g. deprecated field detected).
Warnings []string
}
LoadResult is the output of a successful Load call.
func Load ¶
func Load(opts LoadOptions) (LoadResult, error)
Load discovers, reads, and returns the merged Config.
Precedence (highest to lowest):
- Environment variable overrides (MIDAS_* vars)
- Config file (midas.yaml or explicit path)
- Built-in defaults
Placeholder expansion (${VAR}) is applied AFTER env overlay so that fields overridden by MIDAS_* env vars are never expanded as placeholders — an unresolved placeholder in a file field that gets replaced by an env var never causes a startup failure. Structural and semantic validation are the caller's responsibility.
type LocalIAMConfig ¶
type LocalIAMConfig struct {
// Enabled activates local platform IAM. When true, bootstrap admin/admin is
// created on first run and the /auth/* endpoints are registered.
Enabled bool `yaml:"enabled"`
// SessionTTL controls how long a session remains valid. Default: "8h".
SessionTTL Duration `yaml:"session_ttl"`
// SecureCookies sets the Secure flag on session cookies. Enable in
// production (HTTPS). Defaults to false for local HTTP development.
SecureCookies bool `yaml:"secure_cookies"`
}
LocalIAMConfig controls local platform IAM for the Explorer/console. Local IAM provides username/password login with session cookies and is entirely separate from runtime authority (bearer-token auth on /v1/* routes).
type OIDCRoleMapping ¶
type OIDCRoleMapping struct {
// External is the group name as returned by the identity provider.
External string `yaml:"external"`
// Internal is the canonical MIDAS role (e.g. "platform.admin").
Internal string `yaml:"internal"`
}
OIDCRoleMapping maps a single external group value to a MIDAS internal role.
type ObservabilityConfig ¶
type ObservabilityConfig struct {
// LogLevel controls verbosity. Valid: "debug", "info", "warn", "error". Default: "info".
LogLevel string `yaml:"log_level"`
// LogFormat selects the output format. Valid: "json", "text". Default: "json".
LogFormat string `yaml:"log_format"`
}
ObservabilityConfig controls logging behaviour.
type PlatformOIDCConfig ¶
type PlatformOIDCConfig struct {
// Enabled activates OIDC login. When true the /auth/oidc/* endpoints are
// registered. Requires LocalIAM to also be enabled for session management.
Enabled bool `yaml:"enabled"`
// ProviderName is a display label only (e.g. "entra", "google"). Not used in auth logic.
ProviderName string `yaml:"provider_name"`
// IssuerURL is the OIDC issuer used for provider discovery.
// e.g. https://login.microsoftonline.com/<tenant>/v2.0 (Entra)
// or https://accounts.google.com (Google)
IssuerURL string `yaml:"issuer_url"`
// AuthURL overrides the discovered authorization endpoint (optional).
AuthURL string `yaml:"auth_url"`
// TokenURL overrides the discovered token endpoint (optional).
TokenURL string `yaml:"token_url"`
// ClientID is the OAuth2 application client ID. Supports ${VAR} expansion.
ClientID string `yaml:"client_id"`
// ClientSecret is the OAuth2 application client secret. Supports ${VAR} expansion.
ClientSecret string `yaml:"client_secret"`
// RedirectURL is the callback URL registered with the identity provider.
RedirectURL string `yaml:"redirect_url"`
// Scopes requested from the provider. Defaults: openid, profile, email.
Scopes []string `yaml:"scopes"`
// SubjectClaim is the ID token claim used as the principal subject. Default: "sub".
SubjectClaim string `yaml:"subject_claim"`
// UsernameClaim is the claim used as the display username.
// Default: "preferred_username" (Entra). Set to "email" for Google Workspace.
UsernameClaim string `yaml:"username_claim"`
// GroupsClaim is the claim containing group membership or domain identity.
// Default: "groups" (Entra array). Set to "hd" for Google Workspace (hosted domain).
// The value may be a JSON string or array; both are normalised to []string.
// If absent from the token, groups are treated as empty.
GroupsClaim string `yaml:"groups_claim"`
// DomainHint is passed to the provider as a login hint (optional).
DomainHint string `yaml:"domain_hint"`
// AllowedGroups restricts login to members of at least one listed group (optional).
// An empty slice allows any authenticated user.
AllowedGroups []string `yaml:"allowed_groups"`
// RoleMappings maps external group identifiers to MIDAS internal roles.
RoleMappings []OIDCRoleMapping `yaml:"role_mappings"`
// DenyIfNoRoles rejects login when no internal roles are mapped. Default: true.
DenyIfNoRoles bool `yaml:"deny_if_no_roles"`
// UsePKCE enables PKCE (Proof Key for Code Exchange). Default: true.
UsePKCE bool `yaml:"use_pkce"`
}
PlatformOIDCConfig configures OIDC-based platform login (Explorer/console SSO). This is entirely separate from runtime governance auth on /v1/* routes. The structure is provider-agnostic; any OIDC-compliant provider (e.g. Entra, Google Workspace) is supported via configuration alone.
type ServerConfig ¶
type ServerConfig struct {
// Port is the TCP port to listen on. Default: 8080.
Port int `yaml:"port"`
// ShutdownTimeout is the graceful shutdown deadline. Default: "15s".
ShutdownTimeout Duration `yaml:"shutdown_timeout"`
// ReadTimeout bounds the time to read the entire request including body.
// Default: "30s". Zero means no timeout (not recommended for production).
ReadTimeout Duration `yaml:"read_timeout"`
// ReadHeaderTimeout bounds the time to read request headers.
// Default: "10s". Zero means no timeout.
ReadHeaderTimeout Duration `yaml:"read_header_timeout"`
// WriteTimeout bounds the time to write the full response.
// Default: "60s". Zero means no timeout (not recommended for production).
WriteTimeout Duration `yaml:"write_timeout"`
// IdleTimeout bounds how long to keep idle keep-alive connections open.
// Default: "120s". Zero falls back to ReadTimeout.
IdleTimeout Duration `yaml:"idle_timeout"`
// ExplorerEnabled serves the interactive evaluation sandbox at /explorer.
// Enabled by default; set false in production if the UI is not needed.
ExplorerEnabled bool `yaml:"explorer_enabled"`
// Headless disables all browser-facing surfaces and platform-login routes.
// When true: /explorer, /auth/*, local IAM, and OIDC are not initialised.
// /v1/*, /healthz, and /readyz remain fully operational.
// Conflicts with: explorer_enabled=true, local_iam.enabled=true, platform_oidc.enabled=true.
// Default: false.
Headless bool `yaml:"headless"`
}
ServerConfig controls the HTTP listener.
type StoreConfig ¶
type StoreConfig struct {
// Backend selects the store implementation. Valid values: "memory", "postgres".
Backend string `yaml:"backend"`
// DSN is the Postgres connection string. Required when Backend is "postgres".
// Supports placeholder expansion: "${DATABASE_URL}".
DSN string `yaml:"dsn"`
}
StoreConfig selects and configures the persistence backend.
type TokenEntry ¶
type TokenEntry struct {
// Token is the bearer token value. Supports ${VAR} expansion.
Token string `yaml:"token"`
// Principal is the actor ID (e.g. "user:alice", "svc:payments").
Principal string `yaml:"principal"`
// Roles is a comma-separated list of roles (e.g. "admin,operator").
Roles string `yaml:"roles"`
}
TokenEntry represents a single static bearer token with its principal and roles.
func ParseEnvTokens ¶
func ParseEnvTokens(raw string) ([]TokenEntry, error)
ParseEnvTokens parses the MIDAS_AUTH_TOKENS wire format into []TokenEntry.
Format: semicolon-separated entries, each: token|principal-id|role1,role2
The pipe separator allows principal IDs to contain colons (e.g. "user:alice").
Example:
"secret-1|user:alice|admin,operator;secret-2|svc:deploy|operator"
type ValidationError ¶
type ValidationError struct {
Field string // e.g. "auth.mode" or "auth.tokens[0].token"
Message string
}
ValidationError describes a single validation failure with a field path.
func (ValidationError) Error ¶
func (e ValidationError) Error() string
type ValidationErrors ¶
type ValidationErrors []ValidationError
ValidationErrors is a slice of ValidationError returned as a single error. It implements the error interface so callers can treat it as a fatal startup failure without unwrapping.
func (ValidationErrors) Error ¶
func (ve ValidationErrors) Error() string