# missionMATCH / slateMATCH / screenMATCH — Project Context for Claude Code
## What this is
A **Cleared Workforce Intelligence Platform** for US government contractors, cleared staffing
firms, proposal teams, and capture teams. NOT a generic ATS. The whole product serves the loop:
**Requirement → Candidate Intelligence → Match Scoring → Ranked Candidate Slate → Proposal Readiness.**
slateMATCH (requirement-to-slate) is the core differentiator.
## Hard rules (tooling and writing style, non negotiable)
1. No Python anywhere. Backend is Go, frontend is TypeScript and Node, scripts are bash. Never add a Python script, tool, or dependency. HTML is written by hand, never generated by a program.
2. No dashes of any kind in written output: code comments, log strings, UI copy, docs, and commit messages. Use commas, periods, or rewrite the sentence. Hyphens that are required syntax are allowed inside URLs, HTTP header names, CLI flags, and code identifiers (package names, file names, npm script keys).
3. Every API key stays on the server only. All keys load from `/etc/missionmatch/sam-sync.env` on Bubbles, a root owned 0600 file that is never web served and never in the repo. Placeholder names live in `.env.example` and the real env file is covered by `.gitignore`. Never print a key, never commit a key, never expose a key to the browser bundle.
4. Clearance is a self reported indicator, shown in the UI as "clearance intelligence," never as "verified." The real check is the hiring client FSO through DISS at onboarding. State this once where clearance appears.
## Demo data (sales demo seed)
The candidate pool in the demo org is SYNTHETIC generated data, not real people, and is labeled "Demo data" in the UI everywhere candidates appear (the entire pool, not only the External sourced leads). Clearance values are seeded self reported indicators on generated profiles. A live demo must never imply these are real cleared people; the real check is the customer FSO through DISS at onboarding, and once Galapagos loads their own bench the seeded fillers fall away. The deliberate seed shaping is documented and reproducible in `db/seed/demo_gapfillers.sql`: nine rare skill holders are set available and TS/SCI active so the three hardest staffing gaps have legitimate fillers. That lets the Win Impact feature show HIGH on the Candidates page while the Command Center still names the open gaps (NEED 10 Intelligence Analysts, 3 Data Scientists, 2 Cyber Architects), so the two screens tell one connected story. It moves the Command Center to roughly readiness 86 Watch instead of 59.75 At Risk.
## Authentication and Accounts (Phase 2 decisions, locked)
The auth module lives in `internal/modules/auth` with the pure crypto and RBAC primitives in
`internal/platform/security`. It produces a verified (org_id, user_id, role) from a first party
opaque server side session and feeds `app.current_org_id` only from that validated state, never a
client header. The tenancy foundation (RunInTenantTx, WithOrg, the RLS contract) is reused, not
rebuilt. These choices are settled and must not drift:
1. **Password KDF is PBKDF2 with HMAC SHA 256 (SP 800-132), never argon2 or bcrypt.** Those KDFs sit
outside the FIPS validated boundary; PBKDF2 is inside CMVP module #5247 under GOFIPS140=v1.0.0.
Iteration count is 600000 (OWASP 2024 floor). SP 800-132 directs maximizing iterations within
tolerable latency, so this deliberately exceeds the CAVP tested envelope (iteration count 1 to
10000, password length 8 to 64). That is a stronger choice, not a deviation: state it that way to a
FedRAMP assessor. Password input is capped at 64 characters to stay inside the tested length bound.
2. **A versioned credential pepper** (HMAC SHA 256 of the password under a server held key, applied
before PBKDF2, stays in boundary) is stored as a per credential `pepper_version` so the pepper can
rotate without a flag day. TOTP secrets are AES 256 GCM encrypted at rest with a `key_version` so
the KEK can rotate too.
3. **MFA is mandatory TOTP** (RFC 6238, pinned to HMAC SHA 256 end to end, 32 byte secret, strictly
monotonic replay guard). TOTP is implemented in house on the standard library (crypto/hmac) rather
than a third party library, so every primitive stays provably inside the validated module and
there is no external crypto to re verify under fips140=on. No code path yields an active session
with MFA unsatisfied.
4. **Sessions are opaque and server side** (32 random bytes, stored only as a SHA 256 hash), in a
`__Host-` cookie (HttpOnly, Secure, SameSite Strict), instantly revocable, two stage (pre MFA then
active). Timeouts are fixed NIST values, 15 minutes idle and 12 hours absolute, computed and
compared by the database clock, not per org configurable in this phase.
5. **Identity is one human, one account, one org.** Email is globally unique (a functional unique
index on lower(email)); login resolves the user by global email before the org GUC is set, through
a SECURITY DEFINER cross org lookup. Multi org access for one human is a future membership table
refactor, not a Phase 2 capability.
6. **Org creation is provisioning plus invites only, no public self serve signup.** The first org and
owner are created by the migrator role `provision-org` CLI calling a migrator only SECURITY DEFINER
function; everyone else joins by an authority bounded invite. Invite and recovery link delivery is
manual at pilot (the endpoint returns the one time link, the owner delivers it); there is no email
transport in this phase.
7. **Federated SSO** (OIDC Login.gov, Entra Gov, SAML, WebAuthn and PIV CAC) and public forgot
password are deferred to sub phase 2.9. The seam is `internal/platform/idp`, which does not exist
yet. The five federal auth libraries are not added to go.mod in this phase.
**DO NOT put SSO in `internal/platform/identity`.** That name is already taken by a working
package that does something unrelated: it seeds an organization and its users from the control
plane into a tenant database so the tenant's business foreign keys (`requirements.created_by`,
`slate_members.decided_by` and the rest) resolve against local rows. This file claimed identity
was the SSO seam until 3 August 2026; it never was, and the claim was copied onward into the
owner's manual before anyone checked it.
8. **The dev X-Org-ID header resolver is removed** via a build tag split so it cannot compile into a
production binary, and is additionally hard rejected by config validation in production.
## Products
- **missionMATCH**: workforce intelligence ("who can support this mission today?")
- **slateMATCH**: proposal staffing ("who belongs on the proposal candidate slate?")
- **screenMATCH**: single-candidate qualification ("is this candidate qualified for this requirement?") = `POST /match`
## Language & stack (Go — chosen for cloud portability + the cleanest federal FIPS story)
- **Backend: Go 1.26+** — compiled, memory-safe, single static binary, native FIPS 140-3.
- **HTTP**: `chi` router (idiomatic, std-lib-compatible `net/http`). Thin handlers; logic in services.
- **DB**: PostgreSQL 16 + **pgvector** (HNSW). Access via **`pgx/v5`** (`pgxpool`); typed SQL via **`sqlc`** (compile-checked, no heavy ORM). `pgvector-go` for vector types/ops.
- **Migrations**: a single tool (pinned in STACK_DECISIONS.md — `goose` or `golang-migrate`); migrations are the ONLY way to change schema.
- **Multi-tenancy**: Postgres Row-Level Security (pool model) — see rules below.
- **Skills**: ontology (taxonomy + embeddings + adjacency) in Postgres/pgvector.
- **Search**: hybrid `tsvector` full-text + `pgvector` HNSW fused with **Reciprocal Rank Fusion (RRF)**, in SQL.
- **Storage**: S3-compatible behind a Go interface (MinIO local; S3/GovCloud or Azure Blob in cloud).
- **Parsing/embeddings**: behind a **provider interface** — managed FedRAMP endpoint (AWS Bedrock OR Azure OpenAI) OR self-hosted vLLM; cleared resumes are CUI, so production self-hosts. The core Go app never depends on a native ML stack — it calls the provider over HTTP.
- **Frontend**: Vite + React 18 + TypeScript + TanStack Query + React Router + Tailwind + shadcn/ui + Recharts.
## CLOUD-PORTABILITY MANDATE (non-negotiable — the founder's explicit requirement)
The platform MUST run on **both AWS GovCloud and Azure Government** with **no single-vendor lock-in.**
- Everything cloud-specific lives behind an interface: object storage, the AI/embedding provider, secrets, identity. Choosing a cloud is **configuration**, not code.
- DB is portable managed Postgres+pgvector on either cloud (AWS RDS for PostgreSQL ↔ Azure Database for PostgreSQL Flexible Server).
- Deploy as **containers + Terraform IaC** targeting both clouds; SBOM-at-build; FIPS-validated crypto.
- Do NOT adopt an Azure-only or AWS-only primitive in the core path. If a cloud-specific feature is used, it is isolated behind a port/adapter with the other cloud's adapter present.
## "Don't blindly build" rule (founder directive)
Do NOT rely on stale patterns for Go specifics. Where the correct approach is non-obvious or
version-sensitive — **the FIPS 140-3 module, pgx RLS-with-a-pool, pgvector HNSW DDL/queries,
sqlc + pgvector type overrides, WebAuthn/OIDC/SAML/PIV-CAC libraries, both-cloud managed-Postgres
support** — follow **docs/STACK_DECISIONS.md** (research-backed, source-cited, with verified minimal
patterns). If something there is missing or uncertain, research it with live sources before coding it.
## Non-negotiable engineering rules
1. **Multi-tenancy**: every tenant-scoped table has `organization_id`. Enforce with Postgres RLS
(`USING` + `WITH CHECK`) and `ALTER TABLE ... FORCE ROW LEVEL SECURITY`. The app sets
`SET LOCAL app.current_org_id` **per request transaction on the same pooled connection**
(the pgx-pool gotcha — see STACK_DECISIONS.md) from the validated session. Never trust a
client-supplied `organization_id`. No context set ⇒ **zero rows** (fail safe). The DB role the
app uses is non-superuser, `NOBYPASSRLS`; a separate migrator role has `BYPASSRLS`.
2. Composite indexes lead with `organization_id` on every tenant table.
3. **Audit logging**: a separate append-only `audit_logs` table OUTSIDE RLS. Log logins, record
create/update/delete, exports, permission changes — with actor/entity/old/new/ip/ua. Never UPDATE/DELETE it in app code.
4. **PII/CUI**: clearance data and cleared resumes are sensitive. Never log PII in app logs.
Resume/RFP parsing goes through the provider interface (managed for synthetic demo, self-hosted vLLM for production CUI).
5. **Security baseline (NIST 800-171 Rev 2 aligned)**: RBAC, MFA, AES-256 at rest, TLS 1.2+,
session/idle timeout, password policy, export logging, consent tracking. Build with the native
Go FIPS 140-3 module enabled (see STACK_DECISIONS.md) so crypto is validated from day one.
6. **API-first**. Endpoints include `GET /candidates`, `GET /requirements`, `POST /match`,
`POST /slates/generate`, `GET /clearance-status`, `GET /proposal-readiness`.
## Responsible AI rules (this is an Automated Employment Decision Tool)
1. Every match score is **explainable**: per-component breakdown + human-readable rationale + gap summary + a recordable decision trace.
2. **Human-in-the-loop**: humans approve/reject every slate member. Never auto-reject.
3. **Demographic/EEO data** lives in a separate module, is privacy-protected, and is **NEVER** an
input to matching (no protected attributes or close proxies as features). The matching/search code must never reference the EEO table.
4. Provide **adverse-impact reporting** (selection rates + four-fifths/80% ratios) so customers can run NYC LL144 / OFCCP audits.
5. Maintain a model card / scoring-methodology doc; align to NIST AI RMF and ISO/IEC 42001.
The platform PROVIDES the data for customers' bias audits; it does not itself constitute one, and vendor tooling does not transfer the employer's liability.
## Matching engine (fixed weights — do NOT change without explicit instruction)
Match Score = Clearance*0.25 + Skills*0.25 + Certifications*0.15 + CustomerExperience*0.15
+ Experience*0.10 + LocationAvailability*0.10 (total 100)
- **Clearance**: exact OR higher than required = 100%; lower / expired / unknown = 0%
(TS/SCI satisfies Secret; Secret does NOT satisfy TS/SCI — encode the hierarchy).
- **Skills**: 100% of required met = full; 80–99% = partial; below 80% = reduced. Skill matching
uses **exact match first, then semantic similarity** (embeddings) for synonyms/adjacent skills;
always record which candidate skill matched which required skill and the similarity.
- **Bands**: Green (Proposal Ready) 90+, Yellow (Potential) 75–89, Red (Recruiting Required) <75.
- The engine is a **pure, deterministic Go package** — no DB, no I/O, `skill_matcher` injected.
**All scoring math uses `shopspring/decimal`, never `float64`** (bit-stable determinism for the
AEDT/compliance asset). Exhaustively unit-tested with golden + property tests; lint with
`go-exhaustive`/`nilaway`/`errcheck`. LLM ranking is a FUTURE layer ON TOP, never a replacement.
## Conventions
- Package layout: `internal/platform/` for cross-cutting (config, db, tenant, security, audit,
storage, embeddings); `internal/modules/` per domain with `handler.go`, `service.go`,
`repo.go` (sqlc-backed), `dto.go`. Wire HTTP routes in one place. `cmd/api/main.go` is the entrypoint.
- Errors are values, wrapped with `%w`; no panics in request paths. Context-aware. `gofmt`/`go vet`/
`golangci-lint` clean. Every feature ships with tests; the pure logic must pass without a DB.
- Schema changes via migrations only. SQL is authored in `.sql` and compiled by `sqlc`.
## Brand (frontend)
Deep Navy #0F2744 (primary), Steel Blue #2E5EAA (secondary), Mission Gold #D4A017 (accent),
Light Gray #F4F6F8 (neutral). App shell: left sidebar nav, top stat cards, dashboard charts.