Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / cmd/api/main.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | cmd/api/main.go |
| Original SHA-256 | 11131ecec2b241a471c16e441cff44ba6535687ff1fba50b9e8722d509f76492 |
| Git blob SHA | 9c6c45e149e99abb15a728e160fb6115a03df17c |
| Reading-copy SHA-256 | 11131ecec2b241a471c16e441cff44ba6535687ff1fba50b9e8722d509f76492 |
Recovered source transcript
Download complete source text ↓864 lines · 44.5 KB
// Command api is the missionMATCH HTTP server entrypoint.
//
// It wires configuration, the FIPS startup guard and self test, the Postgres pool (NOBYPASSRLS app
// role), the embedding provider, the authentication module, and the tenant scoped API. Cloud and
// provider selection are configuration (the portability mandate); no cloud SDK is imported here.
package main
import (
"context"
"crypto/fips140"
"crypto/pbkdf2"
"crypto/rand"
"crypto/sha256"
"encoding/json"
"errors"
"log"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/missionworkforce/missionmatch/internal/aioe"
"github.com/missionworkforce/missionmatch/internal/modules/ai"
aioemodule "github.com/missionworkforce/missionmatch/internal/modules/aioe"
"github.com/missionworkforce/missionmatch/internal/modules/alerts"
"github.com/missionworkforce/missionmatch/internal/modules/auth"
"github.com/missionworkforce/missionmatch/internal/modules/awards"
"github.com/missionworkforce/missionmatch/internal/modules/bidoutcomes"
"github.com/missionworkforce/missionmatch/internal/modules/careers"
"github.com/missionworkforce/missionmatch/internal/modules/collab"
"github.com/missionworkforce/missionmatch/internal/modules/commandcenter"
"github.com/missionworkforce/missionmatch/internal/modules/eeo"
"github.com/missionworkforce/missionmatch/internal/modules/governance"
"github.com/missionworkforce/missionmatch/internal/modules/matching"
"github.com/missionworkforce/missionmatch/internal/modules/missionmatch"
"github.com/missionworkforce/missionmatch/internal/modules/opportunities"
oppintel "github.com/missionworkforce/missionmatch/internal/modules/opportunity_intelligence"
"github.com/missionworkforce/missionmatch/internal/modules/org"
platformmod "github.com/missionworkforce/missionmatch/internal/modules/platform"
"github.com/missionworkforce/missionmatch/internal/modules/recruiting"
"github.com/missionworkforce/missionmatch/internal/modules/recruitmatch"
"github.com/missionworkforce/missionmatch/internal/modules/screenmatch"
"github.com/missionworkforce/missionmatch/internal/modules/slatematch"
"github.com/missionworkforce/missionmatch/internal/modules/sourcing"
"github.com/missionworkforce/missionmatch/internal/modules/staffing"
"github.com/missionworkforce/missionmatch/internal/modules/termalias"
"github.com/missionworkforce/missionmatch/internal/modules/wages"
"github.com/missionworkforce/missionmatch/internal/platform/audit"
"github.com/missionworkforce/missionmatch/internal/platform/config"
"github.com/missionworkforce/missionmatch/internal/platform/database"
"github.com/missionworkforce/missionmatch/internal/platform/directory"
"github.com/missionworkforce/missionmatch/internal/platform/extraction"
"github.com/missionworkforce/missionmatch/internal/platform/inference"
"github.com/missionworkforce/missionmatch/internal/platform/obs"
"github.com/missionworkforce/missionmatch/internal/platform/orgsettings"
"github.com/missionworkforce/missionmatch/internal/platform/ratelimit"
"github.com/missionworkforce/missionmatch/internal/platform/secrets"
"github.com/missionworkforce/missionmatch/internal/platform/security"
"github.com/missionworkforce/missionmatch/internal/platform/storage"
"github.com/missionworkforce/missionmatch/internal/platform/tenant"
"github.com/missionworkforce/missionmatch/internal/platform/tenantpool"
)
func main() {
cfg, err := config.Load()
if err != nil {
log.Fatalf("config: %v", err)
}
// Structured JSON logging for the request path (debug level in dev, info in production): the obs
// middleware logs every request through slog. The one time boot and shutdown messages below stay on
// the standard logger, since they are operator facing startup output, not the high volume hot path.
obs.Setup(!cfg.IsProduction())
// FIPS startup guard: in a FIPS deployment, refuse to start with crypto outside the validated module.
if cfg.RequireFIPS && !fips140.Enabled() {
log.Fatalf("FIPS required but OFF, build with GOFIPS140=v1.0.0 and run with GODEBUG=fips140=on")
}
// FIPS self test: actually derive a PBKDF2 key at the configured cost under the active module and
// fail closed if it errors, so a broken crypto path is caught at boot, not at first login.
if err := fipsSelfTest(); err != nil {
log.Fatalf("FIPS self test (PBKDF2 HMAC SHA 256) failed: %v", err)
}
log.Printf("missionMATCH starting: env=%s cloud=%s fips_enabled=%v module=%s",
cfg.Env, cfg.Cloud, fips140.Enabled(), fips140.Version())
ctx := context.Background()
// Two roles for what used to be one pool.
//
// controlDB is the CONTROL PLANE: identity, sessions, and the tenant directory. db is the DATA PLANE
// that tenant scoped business queries run through.
//
// In shared mode they are the same object and nothing about the existing behaviour changes. In
// per_tenant mode db becomes a routing DB that resolves each request to its own tenant database,
// while controlDB stays a direct pool, because login resolves a user by GLOBAL email before any
// organization is known and so has no tenant to route to yet. Handing the router to auth would make
// authentication depend on the very answer it exists to produce.
var (
controlDB *tenant.DB
db *tenant.DB
tenantMgr *tenantpool.Manager
)
if cfg.AppDatabaseURL != "" {
pool, err := database.NewPool(ctx, cfg.AppDatabaseURL)
if err != nil {
log.Fatalf("db pool: %v", err)
}
// Fail closed unless this pool is the NOBYPASSRLS app role, so a swapped or over privileged DSN
// can never silently bypass RLS.
if err := database.AssertRole(ctx, pool, cfg.AppDBRole); err != nil {
log.Fatalf("app pool role check: %v", err)
}
controlDB = tenant.New(pool)
defer controlDB.Close()
if cfg.Tenancy.Mode == config.TenancyPerTenant {
resolver, rerr := secrets.NewResolver(cfg.Tenancy.SecretsBackend, cfg.Tenancy.SecretTTL)
if rerr != nil {
log.Fatalf("tenant secret resolver: %v", rerr)
}
tenantMgr = tenantpool.New(directory.New(pool), resolver, tenantpool.Config{
MaxPools: cfg.Tenancy.MaxPools,
MaxConnsPerPool: cfg.Tenancy.MaxConnsPerPool,
IdleEvict: cfg.Tenancy.IdleEvict,
})
defer tenantMgr.Close()
db = tenant.NewRouting(tenantMgr)
log.Printf("tenancy=per_tenant secrets_backend=%s max_pools=%d max_conns_per_pool=%d idle_evict=%s",
cfg.Tenancy.SecretsBackend, cfg.Tenancy.MaxPools, cfg.Tenancy.MaxConnsPerPool, cfg.Tenancy.IdleEvict)
} else {
db = controlDB
log.Printf("tenancy=shared (one database, isolation by forced row level security)")
}
} else {
log.Printf("WARNING: MM_DATABASE_URL unset, DB backed routes return 503")
}
// The compliance pool authenticates as the compliance role and is the ONLY pool with eeo access.
// It drives adverse impact reporting; the app pool above can never read demographics.
//
// Under per tenant routing this MUST route as well, and for a sharper reason than the data plane.
// The eeo schema holds each tenant's demographic rows, so a single static compliance pool would go
// on reading whichever database it was pointed at while the data plane routed elsewhere, and adverse
// impact for tenant B would be computed from tenant A's demographics. That is both a wrong number
// and a cross tenant disclosure of the most sensitive data in the product.
//
// It gets its OWN registry rather than sharing the data plane's, so the app and compliance roles
// never share a connection: same directory rows, but each tenant credential reference is suffixed so
// a distinct compliance credential is resolved, and every pool asserts the compliance role.
var (
complianceDB *tenant.DB
complianceMgr *tenantpool.Manager
)
switch {
case cfg.Tenancy.Mode == config.TenancyPerTenant && controlDB != nil:
resolver, rerr := secrets.NewResolver(cfg.Tenancy.SecretsBackend, cfg.Tenancy.SecretTTL)
if rerr != nil {
log.Fatalf("compliance secret resolver: %v", rerr)
}
complianceMgr = tenantpool.New(directory.New(controlDB.Pool), resolver, tenantpool.Config{
MaxPools: cfg.Tenancy.MaxPools,
MaxConnsPerPool: cfg.Tenancy.MaxConnsPerPool,
IdleEvict: cfg.Tenancy.IdleEvict,
Role: "missionmatch_compliance",
SecretRefSuffix: "_compliance",
})
defer complianceMgr.Close()
complianceDB = tenant.NewRouting(complianceMgr)
if cfg.ComplianceDatabaseURL != "" {
log.Printf("NOTE: MM_COMPLIANCE_DATABASE_URL is ignored under per_tenant tenancy; the compliance pool routes per tenant")
}
log.Printf("compliance=per_tenant (eeo read through a routed compliance role pool)")
case cfg.ComplianceDatabaseURL != "":
pool, err := database.NewPool(ctx, cfg.ComplianceDatabaseURL)
if err != nil {
log.Fatalf("compliance db pool: %v", err)
}
// Fail closed unless this is the NOBYPASSRLS compliance role. A BYPASSRLS DSN here would leak
// every tenant's demographics cross org through the adverse impact join.
if err := database.AssertRole(ctx, pool, database.ComplianceRoleName()); err != nil {
log.Fatalf("compliance pool role check: %v", err)
}
complianceDB = tenant.New(pool)
defer complianceDB.Close()
default:
log.Printf("WARNING: MM_COMPLIANCE_DATABASE_URL unset, adverse impact reporting returns 503")
}
// The migrator pool is the BYPASSRLS write seam for the GLOBAL migrator owned tables (curation_proposals
// and the skill_relationships apply), used ONLY by the WS8 two step curation approve and verify actions,
// which are gated behind the compliance or operator role. The app pool above can never write these global
// tables (it has SELECT only), so the curation governance write needs this separate pool, the same two
// pool precedent cmd/tag-missions uses. It is NOT a request path tenant pool: it never reads tenant rows.
var migratorPool *pgxpool.Pool
if cfg.MigratorDatabaseURL != "" {
mp, err := pgxpool.New(ctx, cfg.MigratorDatabaseURL)
if err != nil {
log.Fatalf("migrator db pool: %v", err)
}
// Fail closed unless this is the BYPASSRLS migrator role, so a mis configured DSN cannot quietly run
// the global curation writes as the app role (or any under privileged role).
if err := database.AssertMigratorRole(ctx, mp); err != nil {
log.Fatalf("migrator pool role check: %v", err)
}
migratorPool = mp
defer migratorPool.Close()
} else {
log.Printf("WARNING: MM_MIGRATOR_DATABASE_URL unset, the curation approve and verify surface returns 503")
}
// The aioe pool authenticates as the dedicated updater role, the connection level isolation
// guarantee: missionmatch_aioe is granted the three aioe tables and NOTHING else, so the batch
// updater is physically unable to read a candidate row or an eeo table (the integration suite
// proves the denial). A missing DSN leaves the updater off; outcome capture keeps recording.
var aioeUpdater *aioe.Updater
if cfg.AioeDatabaseURL != "" {
ap, aerr := database.NewPool(ctx, cfg.AioeDatabaseURL)
if aerr != nil {
log.Fatalf("aioe db pool: %v", aerr)
}
if aerr := database.AssertRole(ctx, ap, database.AIOERoleName()); aerr != nil {
log.Fatalf("aioe pool role check: %v", aerr)
}
defer ap.Close()
aioeUpdater = aioe.NewUpdater(ap, aioe.DefaultParams(), nil).WithShadow(cfg.AioeShadowMode)
if cfg.AioeShadowMode {
log.Printf("aioe: SHADOW mode on, the updater proposes candidate profiles a human must promote")
}
} else {
log.Printf("WARNING: MM_AIOE_DATABASE_URL unset, the optimization engine updater is off (outcome capture still records)")
}
ext, err := extraction.New(cfg.Extraction)
if err != nil {
log.Fatalf("extraction: %v", err)
}
// The audit writer gets the CONTROL PLANE, not the routing db, and the split falls out of the two
// ways it is used rather than being a preference.
//
// Writer.System records pre auth and platform security events with a NULL organization: failed
// logins, lockouts, session events. By definition no organization is resolved yet, so there is
// nothing to route on, and handing it the router makes every such event fail with ErrNoTenant. Since
// the callers deliberately fail a request closed when a security event cannot be recorded, that
// turns an unroutable audit write into a failed login, which is exactly what it did.
//
// Writer.OnTx is unaffected: it writes on the caller's own transaction, so org scoped events keep
// landing in the tenant database beside the rows they describe. The result is the right shape for a
// compliance product: platform security events in one central immutable ledger, tenant activity in
// that tenant's own database.
auditW := audit.New(controlDB)
// Auth is deliberately given controlDB, never the routing db. Identity lives in the control plane:
// login looks a user up by global email before any organization is known, and the org scoped user
// management paths then run RunInTenantTx against that same control plane, where the users table
// carries organization_id and forced row level security scopes them correctly. In shared mode
// controlDB and db are the same pool, so this is a no op there.
// Per organization settings and sealed secrets, built BEFORE the auth service because invitation delivery
// depends on it.
//
// It takes the DATA plane db, not controlDB, and that distinction is load bearing under per tenant
// routing: org_settings and org_secrets are tenant tables that live in the tenant's own database, so a
// store built on the control plane pool would read one organization's mail configuration while every other
// query in the request read another's.
//
// The store is always constructed, because every non secret setting has to work on every node. Only
// STORING a credential needs the key, and that path reports itself unavailable rather than failing
// quietly. The log line says which, so an operator can tell from the boot output whether this node can
// hold a customer's mail password.
settingsStore := orgsettings.NewStore(db, cfg.Auth.OrgSecretKEK, cfg.Auth.OrgSecretKEKPrevious, cfg.Auth.OrgSecretKEKVersion)
if settingsStore.SealAvailable() {
log.Printf("api: organization secret storage ENABLED, key version %d", cfg.Auth.OrgSecretKEKVersion)
} else {
log.Printf("api: organization secret storage is DISABLED because MM_ORG_SECRET_KEK is unset, " +
"settings work but a customer mail password cannot be stored on this node")
}
authSvc, err := buildAuthService(cfg, controlDB, auditW, settingsStore)
if err != nil {
log.Fatalf("auth: %v", err)
}
csrf := security.NewCSRF(authKey(cfg.Auth.CSRFKey, 32, "MM_CSRF_KEY", cfg.IsProduction()))
authH := auth.NewHandler(authSvc, csrf, cfg.Auth.CookieSecure, cfg.AllowedOrigins, cfg.TrustedProxyHeader)
// The skill matcher reads the BACKFILLED skill embeddings (the HNSW indexed skills.embedding column)
// rather than embedding skill name strings at request time, so the vector layer is live in the scoring
// path and the fake default embedder is gone from it.
matcher := matching.NewChainedResolver(matching.NewDeterministicAliasResolver(db), matching.NewStoredSkillMatcher(db)).
WithAcronyms(matching.NewAcronymResolver(db))
// OBJECT STORAGE IS RESOLVED ONCE and shared. Opportunity documents and candidate documents are the same
// kind of thing living in the same place, so a second storage root would only mean two directories to
// back up and two to fill.
var objectStore storage.Storer
if root := strings.TrimSpace(os.Getenv("MM_STORAGE_ROOT")); root != "" {
st, serr := storage.NewLocal(root)
if serr != nil {
log.Fatalf("api: MM_STORAGE_ROOT is set to %q and is not usable: %v", root, serr)
}
objectStore = st
}
svc := matching.NewService(db, matcher, auditW).WithExtractor(ext).WithRecordScope(settingsStore)
// Candidate documents share the SAME object storage as opportunity documents, resolved from the one
// MM_STORAGE_ROOT setting. A second storage root would mean two places to back up and two to fill.
if objectStore != nil {
svc = svc.WithDocumentStore(objectStore)
log.Print("api: candidate documents ENABLED")
} else {
log.Print("api: candidate documents DISABLED, MM_STORAGE_ROOT is not set")
}
h := matching.NewHandler(svc)
screenH := screenmatch.NewHandler(h)
slateH := slatematch.NewHandler(h)
ccH := commandcenter.NewHandler(commandcenter.NewServiceWithAudit(db, auditW))
missionH := missionmatch.NewHandler(h, ccH)
recruitH := recruitmatch.NewHandler(h, recruitmatch.NewServiceWithAudit(db, auditW))
orgH := org.NewHandler(org.NewServiceWithAudit(db, auditW))
orgSettingsH := org.NewSettingsHandler(db, settingsStore, auditW)
collabH := collab.NewHandler(collab.NewService(db, settingsStore, auditW))
teamsH := org.NewTeamsHandler(db, auditW)
// The careers surface. It takes the CONTROL plane pool as well as the data plane, because the one thing a
// public request must do before anything else is turn a slug into an organization, and the directory is
// the only place that mapping authoritatively lives.
var careersH *careers.Handler
if controlDB != nil && controlDB.Pool != nil {
careersH = careers.NewHandler(careers.NewService(db, controlDB.Pool, objectStore, auditW).WithSettings(settingsStore))
}
recruitingH := recruiting.NewHandler(recruiting.NewService(db, auditW).WithMessaging(settingsStore))
oppH := opportunities.NewHandler(opportunities.NewService(db, auditW))
// Document intake, Phase 2. It ships DARK unless MM_STORAGE_ROOT names a place for the bytes: a document
// endpoint with nowhere to put a document would accept an upload and lose it, which is worse than a 404.
var docH *opportunities.DocumentHandler
if objectStore != nil {
docH = opportunities.NewDocumentHandler(
opportunities.NewDocumentService(db, objectStore, opportunities.NoopScanner{}, auditW))
log.Print("api: document intake ENABLED")
} else {
log.Print("api: document intake DISABLED, MM_STORAGE_ROOT is not set")
}
oppIntelH := oppintel.NewHandler(oppintel.NewService(db))
awardsH := awards.NewHandler(awards.NewService(db))
wagesH := wages.NewHandler(wages.NewService(db))
sourcingH := sourcing.NewHandler(sourcing.NewService(db, auditW))
// M7 bid outcomes ledger. The recording handler ships DORMANT: it is only constructed and mounted when
// MM_BID_OUTCOMES_ENABLED is on, so by default the POST and PATCH routes are never registered and a
// request to them is a plain 404. The ledger table and its advisory base rate read exist regardless; the
// flag only gates the write path, so real win and loss labels start accruing the day an operator turns it
// on while no learned model consumes them yet.
bidOutcomesH := bidoutcomes.NewHandler(bidoutcomes.NewService(db, auditW))
// The proactive alert engine UI surface, mounted LIVE (a live owner facing feature, not flag gated). The
// scheduled sweep (cmd/alert-sweep) writes the alerts; this handler lists them and flips their status.
alertsH := alerts.NewHandler(alerts.NewService(db, auditW))
// The Adaptive Intelligence Optimization Engine surface: outcome capture plus the admin status,
// history, and on demand run endpoints. The updater may be nil (run answers 503).
aioeH := aioemodule.NewHandler(aioemodule.NewService(db, aioeUpdater))
// The acronym and term expansion admin surface (list, tenant add, tenant remove).
termaliasH := termalias.NewHandler(termalias.NewService(db))
eeoH := eeo.NewHandler(eeo.NewService(complianceDB))
govH := governance.NewHandler(governance.NewService(db, auditW, cfg.DecisionRetentionDays))
// WS8 curation: the two step human in the loop apply surface on /compliance. It writes the GLOBAL curation
// tables through the migrator pool, never the app role, so a nil migrator pool makes the surface report
// unavailable (the approve and verify endpoints return 503) rather than ever falling back to the app role.
var curationApply *ai.CurationApplyService
if migratorPool != nil {
curationApply = ai.NewCurationApplyService(migratorPool, auditW, cfg.CurationEnforceSeparation)
}
curationH := ai.NewCurationHandler(curationApply)
platformH := platformmod.NewHandler(migratorPool, auditW)
// W3 staffing recommendations. It reuses the ONE engine path (the same skill matcher) and narrates only
// through the optional inference plane; a nil client means deterministic rationales, so the feature
// works even when the plane is down. Generating a run is a write capability; the deterministic engine
// stays the floor and a person decides every recommendation.
var inferClient *inference.Client
if cfg.Inference.URL != "" {
// The inference plane bearer credential, from the secret store (file first, env fallback).
// A missing token is tolerated ONLY because the gateway itself now refuses unauthenticated
// calls: a token less client simply gets 401s and the deterministic fallbacks engage.
secretStore, serr := secrets.Open(cfg.SecretsFile)
if serr != nil {
log.Fatalf("secret store: %v", serr)
}
mmToken, _ := secretStore.Get("MM_MEGAMIND_TOKEN")
if mmToken == "" {
log.Printf("WARNING: MM_MEGAMIND_TOKEN unset, inference plane calls will be rejected by the authenticated gateway")
}
ic, ierr := inference.New(inference.Config{
BaseURL: cfg.Inference.URL, EmbedModel: cfg.Inference.EmbedModel, RerankModel: cfg.Inference.RerankModel,
GenerateModel: cfg.Inference.GenerateModel, Dimension: cfg.Inference.Dimension,
Timeout: time.Duration(cfg.Inference.TimeoutSeconds) * time.Second, MaxRetries: cfg.Inference.MaxRetries,
BreakerTrips: cfg.Inference.BreakerTrips, BreakerCool: time.Duration(cfg.Inference.BreakerCoolSec) * time.Second,
TeachingEnabled: cfg.Inference.TeachingEnabled,
Token: mmToken,
})
if ierr != nil {
log.Fatalf("inference client: %v", ierr)
}
inferClient = ic
} else {
log.Printf("WARNING: MM_MEGAMIND_URL unset, staffing recommendation rationales are deterministic only")
}
staffingH := staffing.NewHandler(staffing.NewService(db, matcher, auditW, inferClient, staffing.Load()))
// The AI brain, mounted LIVE on the shared RLS database (per tenant pools are a sequenced follow on,
// out of scope here). It reuses the same inference client and audit writer and is memory augmented; a
// nil inference client makes every answer deterministic (the graceful no plane posture). The
// deterministic engine remains the floor: the brain narrates, it never scores.
brainH := ai.NewHandler(ai.NewOrchestrator(db, inferClient, auditW).WithMemory(ai.NewMemory(db, inferClient)))
// Fail the boot if any curated TAG intent is unsafe (a write, an unallowlisted table, or eeo), so an
// unsafe intent can never ship live, as the registry guarantee promises.
if err := ai.ValidateRegistry(); err != nil {
log.Fatalf("ai brain: unsafe TAG intent in the registry: %v", err)
}
// The enforced re embed gate. With a configured plane, /ai/query stays dark until a boot self check
// confirms the stored vectors were produced by the configured embed model (the all-minilm to bge
// corruption mode mis ranks every retrieval with no error). A proven mismatch keeps the brain route
// disabled with a typed 503 while the rest of the API serves normally; a boot time plane outage lets
// the gate self heal on the first query after the plane recovers. This is never a fatal boot error.
if inferClient != nil {
gate := ai.NewVectorGate(func(ctx context.Context) error {
return ai.VerifyVectorModel(ctx, db, inferClient, cfg.Inference.Dimension)
})
brainH = brainH.WithVectorGate(gate)
bootCtx, cancelBoot := context.WithTimeout(context.Background(), 30*time.Second)
switch err := gate.Check(bootCtx); {
case err == nil:
log.Printf("brain vector model check passed: stored vectors match %s", cfg.Inference.EmbedModel)
case errors.Is(err, ai.ErrVectorModelMismatch):
log.Printf("ERROR: brain vector model check FAILED, /ai/query is disabled until a re embed: %v", err)
default:
log.Printf("WARNING: brain vector model not yet verified at boot, /ai/query will retry on first use: %v", err)
}
cancelBoot()
}
// Resolve the tenant middleware. In a release build this is always the session resolver; the dev
// X-Org-ID path only exists in a dev tagged build. Assert that positively in production.
sessionMW, resolverKind := chooseResolver(cfg, authH)
if cfg.IsProduction() && resolverKind != "session" {
log.Fatalf("production must resolve the tenant from a validated session, got resolver %q", resolverKind)
}
log.Printf("tenant resolver: %s", resolverKind)
if !cfg.Auth.MFARequired {
log.Printf("WARNING: MM_MFA_REQUIRED is false, login is password only with no TOTP step (pilot only, this weakens the auth posture)")
}
// The operator relaxation says so at every boot, and loudly. Amendment 6 decision 1 makes a second
// factor mandatory for the platform plane, so a deployment that has stood it down should never be able
// to forget that it did.
if !cfg.Auth.OperatorMFARequired {
log.Printf("WARNING: MM_OPERATOR_MFA_REQUIRED is false, the operator platform seat signs in with a password alone, which stands down amendment 6 decision 1 (set at Joseph's explicit direction, 28 July 2026)")
}
if cfg.BidOutcomesEnabled {
log.Printf("MM_BID_OUTCOMES_ENABLED is true, the M7 bid outcomes recording routes are mounted")
} else {
log.Printf("MM_BID_OUTCOMES_ENABLED is false, the M7 bid outcomes ledger ships dormant (recording routes not mounted)")
}
csrfMW := csrf.Protect(authH.CSRFCookieName(), auth.CSRFHeader, authH.SessionCookieName(), cfg.AllowedOrigins)
// Metrics registry, fed the database pool gauges so /metrics reports connection saturation.
metrics := obs.NewRegistry()
metrics.SetPoolStats(func() map[string]float64 {
out := map[string]float64{}
if db != nil && db.Pool != nil {
s := db.Pool.Stat()
out["app_acquired"] = float64(s.AcquiredConns())
out["app_idle"] = float64(s.IdleConns())
out["app_total"] = float64(s.TotalConns())
out["app_max"] = float64(s.MaxConns())
}
if complianceDB != nil && complianceDB.Pool != nil {
out["compliance_total"] = float64(complianceDB.Pool.Stat().TotalConns())
}
return out
})
// Rate limiters: a generous per IP cap on the whole API, and a strict one on the public auth routes
// (login, CSRF, recovery) to blunt credential stuffing. Account lockout in the auth service is the
// deeper control; this is the transport layer defense in depth. Sweepers bound memory.
stopSweep := make(chan struct{})
defer close(stopSweep)
apiLimiter := ratelimit.New(50, 100)
// Sized for strangers, not for a signed in floor: a person submits an application once, so two a second
// with a burst of twenty is generous for a real careers page and hostile to a script.
careersLimiter := ratelimit.New(2, 20)
apiLimiter.StartSweeper(stopSweep, time.Minute, 10*time.Minute)
authLimiter := ratelimit.New(1, 10)
authLimiter.StartSweeper(stopSweep, time.Minute, 10*time.Minute)
r := chi.NewRouter()
r.Use(middleware.RequestID)
// NOTE: middleware.RealIP is deliberately NOT used; it trusts spoofable client headers (deprecated
// in chi for that reason). The auth handler and the rate limiter derive the client IP from the TCP
// peer, or from MM_TRUSTED_PROXY_HEADER only when the app is behind a trusted reverse proxy.
r.Use(middleware.Recoverer)
r.Use(metrics.Middleware) // access log + metrics, outside the timeout so timed out requests still record
r.Use(middleware.Timeout(60 * time.Second))
// Liveness: the process is up. Cheap, never touches the database (a probe must not be coupled to the DB).
r.Get("/healthz", func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("ok"))
})
// Readiness: the process can serve, the database pools answer. A failing pool returns 503 so a load
// balancer stops routing to a node that cannot reach Postgres (the old /healthz reported healthy even
// with a dead DB).
r.Get("/readyz", func(w http.ResponseWriter, req *http.Request) {
ctx, cancel := context.WithTimeout(req.Context(), 2*time.Second)
defer cancel()
// Under per tenant routing db.Pool and complianceDB.Pool are nil by design, so the previous
// `!= nil` guards skipped BOTH pings and fell through to 200. Readiness reported ready with every
// database unreachable, which is the exact failure this endpoint was added to catch, reintroduced
// for the newer mode. The control plane pool is what readiness can honestly assert here: tenant
// pools are opened lazily per request, so probing them from here would either be a partial answer
// or a fan out that makes readiness cost more than the request it gates.
probe := db
if probe == nil || probe.Pool == nil {
probe = controlDB
}
if probe == nil || probe.Pool == nil {
http.Error(w, "no database configured", http.StatusServiceUnavailable)
return
}
if err := probe.Pool.Ping(ctx); err != nil {
http.Error(w, "app database not ready", http.StatusServiceUnavailable)
return
}
if complianceDB != nil && complianceDB.Pool != nil {
if err := complianceDB.Pool.Ping(ctx); err != nil {
http.Error(w, "compliance database not ready", http.StatusServiceUnavailable)
return
}
}
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("ready"))
})
r.Get("/healthz/fips", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{
"fips_enabled": fips140.Enabled(),
"fips_enforced": fips140.Enforced(),
"module": fips140.Version(),
"pbkdf2_self_test": true, // a failure would have aborted startup
})
})
// Metrics are unauthenticated operational data carrying no PII; production firewalls this to the
// scraping network. It sits outside /api so the API rate limiter never throttles a scrape.
r.Handle("/metrics", metrics.Handler())
// The one way public knowledge feed: GET /knowledge/manifest and GET /knowledge/cards.
//
// missionMATCH PUBLISHES what it knows publicly; anything that wants to learn from it pulls on its own
// schedule. It is deliberately NOT an organ of any federation and is not registered in an orchestrator
// registry, so nothing routes tasks into this service and no other system depends on it being up. The
// manifest states that as data, not only in prose.
//
// Outside /api because the public nginx vhost proxies only /api/ and /healthz, so the feed is reachable
// by a harvester across the tailnet and not from the internet, and a bulk pull does not spend the API
// rate limit that real user traffic depends on. See internal/modules/ai/feed.go.
ai.NewFeedHandler().Routes(r)
r.Route("/api", func(r chi.Router) {
r.Use(apiLimiter.Middleware(cfg.TrustedProxyHeader))
// Public federation read: the World to missionMATCH edge. Rate limited like the rest of /api, but NOT
// behind the session resolver or CSRF, because it takes no cookie and can only ever return the static
// public knowledge base (no tenant, no database, no demographic data). GET or POST /api/mm-recall.
brainH.PublicRoutes(r)
// THE CAREERS SURFACE IS THE ONLY UNAUTHENTICATED PATH IN THIS PRODUCT THAT TOUCHES TENANT DATA, so it
// gets its OWN limiter rather than sharing the general API one. The general limiter is sized for a
// signed in floor of recruiters; this is sized for strangers, and an application is a thing a person
// submits once, not fifty times a minute.
//
// No CSRF, deliberately and correctly: CSRF protects a request carrying ambient authority and these
// carry none, there being no session to ride. What bounds them is that they can only read a PUBLISHED
// posting and only write into job_applications, a staging table nothing downstream trusts.
if careersH != nil {
r.Group(func(r chi.Router) {
r.Use(careersLimiter.Middleware(cfg.TrustedProxyHeader))
careersH.PublicRoutes(r)
})
}
// Public pre tenant auth: runs before any org is known. Behind the strict auth rate limiter, the
// CSRF Origin allowlist, and the signed double submit, but NOT the session resolver (these operate
// on a pre MFA session or no session and read the cookie themselves).
r.Group(func(r chi.Router) {
r.Use(authLimiter.Middleware(cfg.TrustedProxyHeader))
r.Use(csrfMW)
authH.PublicRoutes(r)
})
// Tenant scoped: the session resolver feeds app.current_org_id, then CSRF, then per route RBAC.
r.Group(func(r chi.Router) {
r.Use(sessionMW)
r.Use(csrfMW)
// Flat alias surface: the pre split paths (POST /match, /slates/..., /candidates, ...) stay
// working so existing clients and the screenmatch.thatwebhostingguy.com redirect never break.
// The pre split alias paths (POST /match, /slates/..., /candidates, ...) carry the SAME tool
// entitlement as their canonical namespaces.
//
// They previously did not, and that made the entitlement layer cosmetic rather than enforced:
// h.Routes mounts the identical handler functions that screenH, slateH, and missionH mount
// behind RequireTool, so a user refused at GET /api/slatematch/slates/{id} could fetch the same
// slate, its decision trace, and its candidate CSV export from GET /api/slates/{id} with no gate
// at all. The comment that used to sit here argued these stayed capability gated, which was
// true and beside the point: capability and entitlement are deliberately orthogonal, so one
// cannot stand in for the other. The bookmark the gate is meant to defeat was the flat one.
//
// Gated per sub group rather than as a blob, so entitlement stays exact: a slateMATCH only user
// keeps the flat slate paths and does not silently acquire the screenMATCH ones.
r.Group(func(r chi.Router) {
r.Use(security.RequireTool(security.ToolScreenMatch))
h.ScreenRoutes(r)
})
r.Group(func(r chi.Router) {
r.Use(security.RequireTool(security.ToolSlateMatch))
h.SlateRoutes(r)
})
r.Group(func(r chi.Router) {
r.Use(security.RequireTool(security.ToolMissionMatch))
h.IntelRoutes(r)
h.MissionRoutes(r)
})
// ReadRoutes are the shared candidate and requirement pickers every tool needs, so they are
// gated on holding ANY product rather than a specific one. Gating them to one tool would break
// the other three; leaving them open would expose the candidate list to an account entitled to
// nothing.
r.Group(func(r chi.Router) {
r.Use(security.RequireAnyTool(security.AllTools...))
h.ReadRoutes(r)
})
// Canonical tool namespaces over the same shared core: screenMATCH, slateMATCH, missionMATCH.
// Each tool namespace carries its own entitlement gate. This is the enforcement that matters:
// filtering the navigation decides what is OFFERED, and a bookmark or a read of the bundle reaches
// these handlers regardless. Mounting the gate on the route GROUP means every endpoint the tool
// owns is covered, including ones added later, without each handler remembering to check.
//
// The flat alias surface above is deliberately NOT gated: those are the pre split paths that
// existing clients and the screenmatch redirect still use, and they remain capability gated as
// before. Tool entitlement applies to the canonical namespaces.
r.Route("/screenmatch", func(r chi.Router) {
r.Use(security.RequireTool(security.ToolScreenMatch))
screenH.Routes(r)
})
r.Route("/slatematch", func(r chi.Router) {
r.Use(security.RequireTool(security.ToolSlateMatch))
slateH.Routes(r)
})
r.Route("/missionmatch", func(r chi.Router) {
r.Use(security.RequireTool(security.ToolMissionMatch))
missionH.Routes(r)
})
// recruitMATCH owns two namespaces: the tool surface and the recruiting workflow beneath it.
r.Route("/recruitmatch", func(r chi.Router) {
r.Use(security.RequireTool(security.ToolRecruitMatch))
recruitH.Routes(r)
})
r.Route("/recruiting", func(r chi.Router) {
r.Use(security.RequireTool(security.ToolRecruitMatch))
recruitingH.Routes(r)
})
orgH.Routes(r)
// Mounted OUTSIDE any RequireTool group, deliberately. How an organization is configured, and what
// a given person is allowed to see, are not features of a purchased product: a customer with only
// screenMATCH still has an owner who sets rate visibility and still has recruiters who need the
// policy answers to render a page.
orgSettingsH.Routes(r)
// Also OUTSIDE any RequireTool group, and for the same reason: whether a colleague is already
// working a record is not a feature of a purchased product, it is how a floor avoids calling the
// same person twice.
collabH.Routes(r)
// Correspondence and candidate documents sit OUTSIDE the tool gate for the same reason settings
// do: writing to a candidate and holding their resume are not features of a purchased product,
// they are the minimum a recruiting record needs to be usable.
recruitingH.MessagingRoutes(r)
recruitingH.MailSyncRoutes(r)
h.CandidateDocumentRoutes(r)
h.SearchAndListRoutes(r)
h.MergeRoutes(r)
// Teams give record scope its meaning, so they sit beside settings and outside any tool gate.
teamsH.Routes(r)
if careersH != nil {
careersH.Routes(r)
}
ccH.Routes(r)
oppH.Routes(r)
// The tenant tracked opportunity capture surface ships under the slateMATCH tool entitlement (the
// intake and pursuit workflow lives with the proposal slate), gated per route on opportunity.manage.
// RequireTool answers 404 for an unentitled tool, never 403.
r.Group(func(r chi.Router) {
r.Use(security.RequireTool(security.ToolSlateMatch))
oppH.CaptureRoutes(r)
// Teaming partners and key personnel commitments ride the same tool entitlement and the same
// opportunity.manage capability as the rest of capture, because deciding who you bid WITH is
// the same act as deciding what you bid ON.
oppH.TeamingRoutes(r)
// Document intake rides the same tool entitlement as capture, because attaching a
// solicitation package IS the capture workflow. Nil when storage is unconfigured, and a nil
// handler mounts nothing, so the routes are absent rather than broken.
if docH != nil {
docH.Routes(r)
}
})
oppIntelH.Routes(r)
awardsH.Routes(r)
wagesH.Routes(r)
sourcingH.Routes(r)
// M7 bid outcomes recording, flag gated so it ships DORMANT. When MM_BID_OUTCOMES_ENABLED is off
// the POST and PATCH routes are simply not registered (a 404), matching how other deferred features
// gate; when on, the ledger starts accruing real win and loss labels.
if cfg.BidOutcomesEnabled {
bidOutcomesH.Routes(r)
}
// The proactive alert engine: GET /alerts and PATCH /alerts/{id}, always mounted.
alertsH.Routes(r)
// AIOE: POST /outcomes (the one click feedback loop) and the admin /aioe surface.
aioeH.Routes(r)
termaliasH.Routes(r)
eeoH.Routes(r)
// THE PLATFORM PLANE, amendment 6 section 2 item 2 and Track U item 4. Every surface that acts
// across tenants rather than inside one mounts here and answers 404 to everyone who is not an
// operator, tenant superusers included.
//
// Curation is the only such surface that EXISTS today, and it is the one that matters most: it
// writes the GLOBAL skill ontology every tenant is scored against, so a client's own top seat
// approving an edge would change what a competitor's candidates score. It was already narrowed to
// the operator capability, but RequireCapability answers 403, which confirms the control plane is
// there. This group makes the refusal a 404 instead.
//
// The other platform surfaces the amendment names are Phase 5A work that is not built: there is no
// cross tenant Organizations route, no cross tenant Users route, and no platform Metrics API. When
// they land they mount INSIDE this group and inherit the gate rather than each remembering it.
// The federation reads (/api/mm-recall and the /knowledge feed) are deliberately NOT here: they
// are unauthenticated by design, carry only published public knowledge cards, touch no tenant and
// no database, and sit outside the session plane entirely, so gating them on a session role would
// break their stated contract without protecting anything. /metrics is likewise unauthenticated
// operational data outside /api, firewalled to the scraping network.
r.Group(func(r chi.Router) {
r.Use(security.RequirePlatform())
curationH.Routes(r)
// The cross tenant Organizations route the comment above anticipated. It mounts HERE so it
// inherits the operator gate rather than remembering it, and it reaches provision_org through
// the migrator pool because the app role deliberately cannot execute that function.
platformH.Routes(r)
})
govH.Routes(r)
staffingH.Routes(r)
brainH.Routes(r)
// The capture readiness READ, so a bid decision screen can show the number it is deciding
// against instead of recording a snapshot of nothing.
brainH.CaptureRoutes(r)
authH.TenantRoutes(r)
})
})
srv := &http.Server{
Addr: cfg.HTTPAddr,
Handler: r,
ReadHeaderTimeout: 10 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 60 * time.Second,
IdleTimeout: 120 * time.Second,
}
// Graceful shutdown: on SIGINT or SIGTERM, stop accepting and let in flight requests (and their
// tenant transactions) drain before exit, so a redeploy never severs a write mid commit.
shutdownCtx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
go func() {
log.Printf("listening on %s", cfg.HTTPAddr)
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("server: %v", err)
}
}()
<-shutdownCtx.Done()
log.Printf("shutdown signal received, draining in flight requests")
drainCtx, cancel := context.WithTimeout(context.Background(), 25*time.Second)
defer cancel()
if err := srv.Shutdown(drainCtx); err != nil {
log.Printf("graceful shutdown error: %v", err)
}
log.Printf("stopped")
}
// fipsSelfTest derives one PBKDF2 key at the production iteration count under the active crypto module.
func fipsSelfTest() error {
_, err := pbkdf2.Key(sha256.New, "missionmatch fips self test", []byte("self test salt"), security.PBKDF2Iterations, 32)
return err
}
// buildAuthService assembles the auth service from config. In production every key is present
// (config.Validate enforces it). In development a missing key falls back to an ephemeral random key
// with a loud warning, so the server still starts for local work.
func buildAuthService(cfg config.Config, db *tenant.DB, auditW *audit.Writer, settingsStore *orgsettings.Store) (*auth.Service, error) {
prod := cfg.IsProduction()
pepperKeys := map[int][]byte{
cfg.Auth.PepperVersion: authKey(cfg.Auth.Pepper, 32, "MM_CREDENTIAL_PEPPER", prod),
}
if len(cfg.Auth.PepperPrevious) > 0 {
pepperKeys[cfg.Auth.PepperVersion-1] = cfg.Auth.PepperPrevious
}
pepper, err := security.NewPepper(cfg.Auth.PepperVersion, pepperKeys)
if err != nil {
return nil, err
}
hasher, err := security.NewHasher(pepper, security.PBKDF2Iterations)
if err != nil {
return nil, err
}
tagger, err := security.NewTagger(authKey(cfg.Auth.RecoveryLookupKey, 32, "MM_RECOVERY_LOOKUP_KEY", prod))
if err != nil {
return nil, err
}
// The KEK map carries the active version plus an optional prior version so a rotated KEK still
// decrypts TOTP secrets sealed under the old one, mirroring the pepper rotation above.
totpKEKs := map[int][]byte{
cfg.Auth.TOTPKEKVersion: authKey(cfg.Auth.TOTPKEK, 32, "MM_TOTP_KEK", prod),
}
if len(cfg.Auth.TOTPKEKPrevious) > 0 {
totpKEKs[cfg.Auth.TOTPKEKVersion-1] = cfg.Auth.TOTPKEKPrevious
}
return auth.NewService(auth.Deps{
DB: db,
Audit: auditW,
Hasher: hasher,
Tagger: tagger,
TOTP: security.TOTP{},
TOTPKEKs: totpKEKs,
TOTPKEKActive: cfg.Auth.TOTPKEKVersion,
TOTPIssuer: cfg.Auth.TOTPIssuer,
IdleSeconds: cfg.Auth.SessionIdleSeconds,
AbsoluteSeconds: cfg.Auth.SessionAbsoluteSeconds,
PendingSeconds: cfg.Auth.PendingAbsoluteSeconds,
InviteSeconds: cfg.Auth.InviteExpirySeconds,
CookieSecure: cfg.Auth.CookieSecure,
MFADisabled: !cfg.Auth.MFARequired,
OperatorMFADisabled: !cfg.Auth.OperatorMFARequired,
// Mail delivery for invitations. Both are optional: with no store, or with no organization that has
// configured mail, Invite returns the one time link exactly as it did before.
Settings: settingsStore,
AppBaseURL: cfg.AppBaseURL,
})
}
// authKey returns the configured key, or an ephemeral random key in development when it is unset.
func authKey(set []byte, size int, name string, prod bool) []byte {
if len(set) > 0 {
return set
}
if prod {
// config.Validate already fatals before here; this is a defensive backstop.
log.Fatalf("%s required in production", name)
}
b := make([]byte, size)
_, _ = rand.Read(b)
log.Printf("WARNING: %s unset, using an ephemeral dev key (sessions and credentials will not survive a restart; set %s for a stable dev database)", name, name)
return b
}