Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / internal/platform/config/config.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 39df94f92b2a33f852481fe1954dabed841524bb |
| Branch | w0-aedt-compliance |
| Path or archive member | internal/platform/config/config.go |
| Original SHA-256 | fc958e92958b7e120cf054fffdddc7e4cf4096198d284aaa6ad1eadbaf311ae1 |
| Git blob SHA | 7308e11088281df7f69f228e4315220a526d0344 |
| Reading-copy SHA-256 | fc958e92958b7e120cf054fffdddc7e4cf4096198d284aaa6ad1eadbaf311ae1 |
Recovered source transcript
Download complete source text ↓285 lines · 11.7 KB
// Package config loads and validates the application's runtime configuration.
//
// Cloud selection is CONFIGURATION, not code (the cloud-portability mandate): the same binary runs
// on AWS GovCloud or Azure Government by flipping MM_CLOUD and the provider settings. Nothing here
// imports a cloud SDK — adapters live behind interfaces in their own packages and are wired in
// cmd/api from this config.
package config
import (
"encoding/base64"
"errors"
"fmt"
"os"
"strconv"
"strings"
)
// Env is the deployment environment.
type Env string
const (
EnvDevelopment Env = "development"
EnvProduction Env = "production"
)
// Cloud is the target government cloud. Local is for dev (MinIO, self-hosted embeddings, no managed services).
type Cloud string
const (
CloudLocal Cloud = "local"
CloudAWSGovCloud Cloud = "aws_govcloud"
CloudAzureGov Cloud = "azure_gov"
)
// EmbeddingProvider selects the embedding adapter. "fake" is a deterministic offline provider for
// dev/tests; "openai" is any OpenAI-compatible endpoint (self-hosted vLLM — the production CUI path —
// or OpenAI); "bedrock"/"azure_openai" are the managed gov-cloud adapters (wired at deploy time).
type EmbeddingProvider string
const (
ProviderFake EmbeddingProvider = "fake"
ProviderOpenAI EmbeddingProvider = "openai"
ProviderBedrock EmbeddingProvider = "bedrock"
ProviderAzureOpenAI EmbeddingProvider = "azure_openai"
)
// Embeddings configures the embedding provider. Dimension MUST match the vector(N) column width in
// the schema (default 384); changing the provider's native dimension is a re-embed migration, not a
// config flip (STACK_DECISIONS.md §6).
type Embeddings struct {
Provider EmbeddingProvider
Endpoint string // base URL for the OpenAI-compatible / Azure endpoint
Model string // e.g. "text-embedding-3-large", "amazon.titan-embed-text-v2:0", a local model id
Dimension int
APIKey string // bearer/api-key; for Bedrock/Azure-Entra the adapter uses cloud credentials instead
}
// Auth holds the server side secrets and tunables for the authentication module. Every secret here
// loads from the environment (the root owned secrets file), never the repo or the browser bundle.
type Auth struct {
PepperVersion int // active credential pepper version stored on new credentials
Pepper []byte // active credential pepper key (HMAC applied before PBKDF2)
PepperPrevious []byte // optional prior pepper key (version PepperVersion minus 1) for rotation
TOTPKEKVersion int // active TOTP secret KEK version
TOTPKEK []byte // 32 byte AES 256 key encrypting TOTP secrets at rest
CSRFKey []byte // HMAC key binding a CSRF token to its session
RecoveryLookupKey []byte // HMAC key for recovery code lookup tags
TOTPIssuer string // issuer shown in the authenticator app
SessionIdleSeconds int // idle timeout (NIST 800-171 3.1.11 default 900)
SessionAbsoluteSeconds int // absolute session cap (default 43200, 12 hours)
PendingAbsoluteSeconds int // auth_pending (pre MFA) session cap (default 300)
InviteExpirySeconds int // invite and activation link lifetime (default 259200, 72 hours)
CookieSecure bool // Secure attribute on the session cookie (true except plain http dev)
}
// Config is the fully-resolved, validated application configuration.
type Config struct {
Env Env
HTTPAddr string
AppDatabaseURL string // NOBYPASSRLS app role DSN
MigratorDatabaseURL string // BYPASSRLS migrator role DSN (used by goose only)
ComplianceDatabaseURL string // compliance role DSN, the only path that reads the eeo schema; empty disables adverse impact reporting (503)
DecisionRetentionDays int // retention window for identifiable screening artifacts after a decision (default 90)
Cloud Cloud
Embeddings Embeddings
RequireFIPS bool // when true, cmd/api aborts unless crypto/fips140.Enabled()
AllowDevHeaderAuth bool // dev only X-Org-ID resolver toggle; MUST be false in production
AllowedOrigins []string // CSRF Origin allowlist for unsafe verbs; empty disables the check (dev)
TrustedProxyHeader string // header carrying the real client IP (set by a trusted reverse proxy); empty uses the TCP peer
Auth Auth
}
// Load reads configuration from the environment, applies defaults, and validates it.
func Load() (Config, error) {
c := Config{
Env: Env(getDefault("MM_ENV", string(EnvDevelopment))),
HTTPAddr: getDefault("MM_HTTP_ADDR", ":8080"),
AppDatabaseURL: os.Getenv("MM_DATABASE_URL"),
MigratorDatabaseURL: os.Getenv("MM_MIGRATOR_DATABASE_URL"),
ComplianceDatabaseURL: os.Getenv("MM_COMPLIANCE_DATABASE_URL"),
DecisionRetentionDays: getInt("MM_DECISION_RETENTION_DAYS", 90),
Cloud: Cloud(getDefault("MM_CLOUD", string(CloudLocal))),
Embeddings: Embeddings{
Provider: EmbeddingProvider(getDefault("MM_EMBED_PROVIDER", string(ProviderFake))),
Endpoint: os.Getenv("MM_EMBED_ENDPOINT"),
Model: os.Getenv("MM_EMBED_MODEL"),
Dimension: getInt("MM_EMBED_DIMENSION", 384),
APIKey: os.Getenv("MM_EMBED_API_KEY"),
},
RequireFIPS: getBool("MM_REQUIRE_FIPS", false),
AllowDevHeaderAuth: getBool("MM_ALLOW_DEV_HEADER_AUTH", false),
AllowedOrigins: splitCSV(os.Getenv("MM_ALLOWED_ORIGINS")),
TrustedProxyHeader: os.Getenv("MM_TRUSTED_PROXY_HEADER"),
Auth: Auth{
PepperVersion: getInt("MM_CREDENTIAL_PEPPER_VERSION", 1),
Pepper: getBytes("MM_CREDENTIAL_PEPPER"),
PepperPrevious: getBytes("MM_CREDENTIAL_PEPPER_PREVIOUS"),
TOTPKEKVersion: getInt("MM_TOTP_KEK_VERSION", 1),
TOTPKEK: getBytes("MM_TOTP_KEK"),
CSRFKey: getBytes("MM_CSRF_KEY"),
RecoveryLookupKey: getBytes("MM_RECOVERY_LOOKUP_KEY"),
TOTPIssuer: getDefault("MM_TOTP_ISSUER", "missionMATCH"),
SessionIdleSeconds: getInt("MM_SESSION_IDLE_SECONDS", 900),
SessionAbsoluteSeconds: getInt("MM_SESSION_ABSOLUTE_SECONDS", 43200),
PendingAbsoluteSeconds: getInt("MM_SESSION_PENDING_SECONDS", 300),
InviteExpirySeconds: getInt("MM_INVITE_EXPIRY_SECONDS", 259200),
CookieSecure: getBool("MM_COOKIE_SECURE", true),
},
}
if err := c.Validate(); err != nil {
return Config{}, err
}
return c, nil
}
// Validate enforces invariants and environment-specific requirements. Errors are joined so a
// misconfigured deploy surfaces every problem at once.
func (c Config) Validate() error {
var errs []error
switch c.Env {
case EnvDevelopment, EnvProduction:
default:
errs = append(errs, fmt.Errorf("MM_ENV %q invalid (want development|production)", c.Env))
}
switch c.Cloud {
case CloudLocal, CloudAWSGovCloud, CloudAzureGov:
default:
errs = append(errs, fmt.Errorf("MM_CLOUD %q invalid (want local|aws_govcloud|azure_gov)", c.Cloud))
}
switch c.Embeddings.Provider {
case ProviderFake, ProviderOpenAI, ProviderBedrock, ProviderAzureOpenAI:
default:
errs = append(errs, fmt.Errorf("MM_EMBED_PROVIDER %q invalid", c.Embeddings.Provider))
}
if c.Embeddings.Dimension <= 0 {
errs = append(errs, errors.New("MM_EMBED_DIMENSION must be > 0"))
}
// OpenAI-compatible providers need an endpoint + model.
if c.Embeddings.Provider == ProviderOpenAI || c.Embeddings.Provider == ProviderAzureOpenAI {
if c.Embeddings.Endpoint == "" {
errs = append(errs, fmt.Errorf("MM_EMBED_ENDPOINT required for provider %q", c.Embeddings.Provider))
}
if c.Embeddings.Model == "" {
errs = append(errs, fmt.Errorf("MM_EMBED_MODEL required for provider %q", c.Embeddings.Provider))
}
}
// TOTP secrets are AES 256, so the KEK must be exactly 32 bytes whenever it is set, in any env.
if len(c.Auth.TOTPKEK) != 0 && len(c.Auth.TOTPKEK) != 32 {
errs = append(errs, errors.New("MM_TOTP_KEK must decode to exactly 32 bytes (AES 256)"))
}
if c.Auth.PepperVersion <= 0 {
errs = append(errs, errors.New("MM_CREDENTIAL_PEPPER_VERSION must be positive"))
}
if c.Auth.SessionIdleSeconds <= 0 || c.Auth.SessionAbsoluteSeconds <= 0 || c.Auth.PendingAbsoluteSeconds <= 0 {
errs = append(errs, errors.New("session timeout seconds must all be positive"))
}
if c.DecisionRetentionDays <= 0 {
errs = append(errs, errors.New("MM_DECISION_RETENTION_DAYS must be positive"))
}
if c.Env == EnvProduction {
if c.AppDatabaseURL == "" {
errs = append(errs, errors.New("MM_DATABASE_URL required in production"))
}
if c.Embeddings.Provider == ProviderFake {
errs = append(errs, errors.New("the fake embedding provider must not be used in production"))
}
if c.Cloud == CloudLocal {
errs = append(errs, errors.New("MM_CLOUD must be a government cloud in production"))
}
// The dev X-Org-ID resolver may never be the production tenant source.
if c.AllowDevHeaderAuth {
errs = append(errs, errors.New("MM_ALLOW_DEV_HEADER_AUTH must be false in production"))
}
// FIPS must be enforced, not optional, so the validated crypto path is the one that runs.
if !c.RequireFIPS {
errs = append(errs, errors.New("MM_REQUIRE_FIPS must be true in production"))
}
// Every auth secret must be present so the validated crypto has its keys.
if len(c.Auth.Pepper) == 0 {
errs = append(errs, errors.New("MM_CREDENTIAL_PEPPER required in production"))
}
if len(c.Auth.TOTPKEK) != 32 {
errs = append(errs, errors.New("MM_TOTP_KEK (32 byte AES 256 key) required in production"))
}
if len(c.Auth.CSRFKey) == 0 {
errs = append(errs, errors.New("MM_CSRF_KEY required in production"))
}
if len(c.Auth.RecoveryLookupKey) == 0 {
errs = append(errs, errors.New("MM_RECOVERY_LOOKUP_KEY required in production"))
}
// The CSRF Origin allowlist must be enforced; an empty list disables it (a dev only path).
if len(c.AllowedOrigins) == 0 {
errs = append(errs, errors.New("MM_ALLOWED_ORIGINS required in production (the CSRF Origin allowlist must be enforced)"))
}
// Both cookies use the __Host- prefix, which a browser only accepts when Secure is set; a non
// Secure production deploy silently breaks all auth and exposes the token.
if !c.Auth.CookieSecure {
errs = append(errs, errors.New("MM_COOKIE_SECURE must be true in production (the __Host- cookie prefix requires Secure)"))
}
}
return errors.Join(errs...)
}
// IsProduction reports whether this is a production deployment.
func (c Config) IsProduction() bool { return c.Env == EnvProduction }
func getDefault(key, def string) string {
if v := strings.TrimSpace(os.Getenv(key)); v != "" {
return v
}
return def
}
func getInt(key string, def int) int {
if v := strings.TrimSpace(os.Getenv(key)); v != "" {
if n, err := strconv.Atoi(v); err == nil {
return n
}
}
return def
}
func getBool(key string, def bool) bool {
if v := strings.TrimSpace(os.Getenv(key)); v != "" {
if b, err := strconv.ParseBool(v); err == nil {
return b
}
}
return def
}
// splitCSV splits a comma separated env value into trimmed, non empty entries.
func splitCSV(v string) []string {
if strings.TrimSpace(v) == "" {
return nil
}
parts := strings.Split(v, ",")
out := make([]string, 0, len(parts))
for _, p := range parts {
if t := strings.TrimSpace(p); t != "" {
out = append(out, t)
}
}
return out
}
// getBytes loads a secret key from the environment. The value is decoded as standard base64 when it
// parses as such, otherwise the raw bytes of the string are used, so an operator can supply either a
// base64 key or a raw passphrase. An unset variable yields nil.
func getBytes(key string) []byte {
v := strings.TrimSpace(os.Getenv(key))
if v == "" {
return nil
}
if decoded, err := base64.StdEncoding.DecodeString(v); err == nil {
return decoded
}
return []byte(v)
}