Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / docs/ARCHITECTURE.txt |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | docs/ARCHITECTURE.txt |
| Original SHA-256 | 71765110e4603b77e946cc5c6bd260b5d03cbb648d7a3859e31f21b17e09e535 |
| Git blob SHA | b4f436ec95566302e5ceeb019d7d5494aeb0b677 |
| Reading-copy SHA-256 | 71765110e4603b77e946cc5c6bd260b5d03cbb648d7a3859e31f21b17e09e535 |
Recovered source transcript
Download complete source text ↓1,876 lines · 107.7 KB
================================================================================
missionMATCH PLATFORM
Architecture, Data Flow, Product Breakdown, AI Layer, Operations, Incidents
================================================================================
THIS IS A LIVING DOCUMENT. Canonical at docs/ARCHITECTURE.txt in the repository
and NOWHERE ELSE. Any copy outside the repo is stale by definition.
Revision 29 July 2026, revision 4, at commit 870d5ad.
Supersedes revision 3 (9a73b61), revision 2 (b1a298d), revision 1 (b8c7b07), the
28 July early edition (e9ff911), and the 21 July edition (9e77f65).
Trigger for this revision: a phase close and two production deploys. Phase 2 scope
completion added table extraction, split and merge, draft generation, and the three
missing intake screens; migrations 0081 through 0084 shipped to production; and
Gates A, B, C and D were all re run, Gate C for the first time against these screens.
SECRETS NEVER APPEAR IN THIS DOCUMENT, regardless of anyone's direction,
including the owner's. A previous copy of this file carried an API key at its
tail; it was removed before this file was first committed, and git history was
checked and confirmed never to have contained it.
WHEN THIS DOCUMENT IS REVISED, exactly four moments and no others:
every phase close, as a Gate D line item
every production deploy
every incident closed
every recorded decision
HOW IT IS REVISED. Volatile facts are RE MEASURED, never edited by hand: schema
versions queried per database, binary hashes, account and data counts,
environment flags, task and track statuses, the risk register, and the current
state section. The header takes a new date, commit, and supersedes line. A claim
that cannot be re measured is labeled INFERRED in the sentence that makes it.
Corrections are withdrawn BY NAME, never silently edited. Nothing aspirational
ever enters: a planned thing belongs in the directive and the ledger, not here.
GOVERNING DOCUMENTS, read in this order:
CLAUDE.md
docs/CLAUDE_CODE_BUILD_DIRECTIVE.md the work order
docs/DIRECTIVE_AMENDMENTS_CONSOLIDATED.md amendments 1 through 5
docs/DIRECTIVE_AMENDMENT_6.md identity, roles, the operator plane
docs/BUILD_CONTINUATION_PROTOCOL.md the five gate phase close
docs/BUILD_STATE.md the append only ledger
docs/RUNBOOK_RESTORE.md how to actually restore
this file what the system IS, measured
Everything here was measured. Where a number came from a command, that is said.
Where something is inferred rather than observed, the sentence says so. Where an
earlier claim was wrong, it is withdrawn by name rather than quietly edited,
because a document that hides its corrections cannot be trusted about anything.
CONTENTS
0 What this is
1 Physical topology
2 Control plane and data plane
3 Environments
4 Request data flow, the map
5 Authorization model
6 Authentication and session security
7 THE FOUR PRODUCTS, IN DEPTH
8 The matching engine
9 The opportunity pipeline
10 THE AI LAYER, UNDER SCRUTINY
10A THE LEARNING BOUNDARY, three doors and no fourth
11 Federation relationship to PANTHEON
12 File architecture
13 How it is built
14 Deployment and its real failure modes
15 Observability and what runs on a timer
16 THE 28 JULY 2026 INCIDENT RECORD
17 Current state, honestly
18 Risk register
19 The design decisions that explain the rest
20 Glossary
SCALE, re measured 28 July 2026 at commit 9a73b61:
327 Go source files 68,304 lines
228 Go test files 37,903 lines (36 percent of the Go is tests)
87 TypeScript and TSX files 13,217 lines
80 SQL migrations, head 0081, plus a segregated EEO set of 2
36 command line binaries
24 domain modules, 31 cross cutting platform packages
22 capabilities, 8 roles, 4 products
86 tables in the tenant database, 50 with FORCED row level security
372 commits on feature/opportunity-to-outcome
scoring engine diff against the e74fd2e baseline: EMPTY, verified
(anchor corrected 29 July 2026, finding D8; 9e77f65 was the 24 June
project baseline and diffs 1476 lines, so it could never be empty)
MEASURED STATE AT THIS REVISION, every number re measured rather than carried forward:
scale 339 Go source files, 236 test files, 94 frontend files, 83 migrations at
head 0084, 408 commits. The frozen scoring engine diff against e74fd2e is
EMPTY, and the anchor and path are both asserted to exist before that
result is trusted, because this check has failed open twice.
schema, queried per database, never summarized
control plane 100.77.114.40:5433/missionmatch v84
tenant 100.94.8.94:5432/missionmatch v84
staging 100.94.8.94:5432/missionmatch_staging v84
ALL THREE LEVEL for the first time since the build began. 0081 through
0084 are additive and carry no data, so they ship to production while the
routes stay dark, which keeps the Gate 6 freeze intact.
CORRECTION TO THE CONTROL PLANE ADDRESS, and it cost time to rediscover.
Earlier revisions wrote "megamind-agi:5433". megamind-agi has TWO tailnet
identities: the Windows host at 100.69.120.61, and the WSL2 instance at
100.77.114.40 running its own tailscaled. Postgres binds INSIDE WSL2, so
it answers on .40 and a connection to .61:5433 times out with no useful
error. There is no sshd inside WSL2 either, so the only route in is ssh
to the Windows host and then wsl with a STAGED script, never inline
quoting.
STAGING IS ONE AHEAD deliberately: migration 0081, provenance at origin,
is staging only pending an approved production deploy under the Track
guardrails. Named here so it reads as intended rather than as drift.
binaries
both production API nodes 8f24414531e80a9a (identical)
staging API 98edd2b8094721f0
live bundle /assets/index-BE5iWWvj.js
environment flags, production
MM_ENV=development MM_TENANCY_MODE=per_tenant
MM_MFA_REQUIRED=false MM_OPERATOR_MFA_REQUIRED=false
accounts, six, from the authoritative control plane
joseph.w.anady@gmail.com operator active last login 28 Jul 11:51
patriciacox6@aol.com superuser active last login 28 Jul 08:04
tallen282005@gmail.com superuser active last login 27 Jul 14:42
batwood@igotozarks.com recruiter active last login 27 Jul 15:17
trutle973@hotmail.com recruiter active last login 27 Jul 16:51
trackh.probe@missionmatch.test viewer DISABLED last login 28 Jul 16:36
The probe was created for one production positive control and retired.
Every last login value above is real and current, which was NOT true this
morning: see incident 7.
data, tenant database
candidates 11, requirements 6, opportunity_records 6676,
audit_logs 56470, clients 0, requisitions 0, applications 0
track and phase status
PHASE 2 current. Phases 3, 4, 5, 5A, 6 not started.
TRACK U complete, all six items, all four gate items proven.
TRACK H items 1, 3, 4, 5 complete. Items 2 and 6 are the owner's.
PHASE 2 Gate A is OPEN pending exactly ONE blocker, a real solicitation
package. Gate E is pending the real document trial plus the owner's
approval. Gates B, C, and D PASS. The complete bundle is filed at
docs/evidence/phase2/ with a README that states what it does NOT claim
next to what it does. See docs/PHASE2_DOCUMENT_TRIAL.md.
Tasks T1 and T2 complete.
--------------------------------------------------------------------------------
0. WHAT THIS IS
--------------------------------------------------------------------------------
A Cleared Workforce Intelligence Platform for United States government
contractors, cleared staffing firms, proposal teams, and capture teams. It is
NOT a generic applicant tracking system. Everything serves one loop:
Requirement -> Candidate Intelligence -> Match Scoring ->
Ranked Candidate Slate -> Proposal Readiness
The current build program extends the front of that loop:
Opportunity -> Source Documents -> Scope Extraction -> Human Review ->
Approved Requirements -> Staffing Slates -> Proposal Workspace ->
Submission -> Outcome
Legally the product is an Automated Employment Decision Tool. That single fact
drives more architecture than any technical consideration: NYC Local Law 144,
Title VII, Colorado and California fair employment law, the EU AI Act, and the
vendor exposure recognised in Mobley v. Workday. Every design choice that looks
paranoid below traces back to it.
WHO IS ON IT TODAY. One production organization, Acme GovCon, five accounts:
one operator (Joseph Anady), two tenant superusers (Patricia Cox, Tallen), two
recruiters (Brian Atwood, one other). FG Talent Partners is the live design
partner and the first client site.
--------------------------------------------------------------------------------
1. PHYSICAL TOPOLOGY
--------------------------------------------------------------------------------
internet
|
Cloudflare
|
+------------------------v--------------------------+
| BUBBLES Debian 13, i5-8350U, 15GB |
| 100.90.97.104 |
| |
| nginx TLS, fail2ban, ~60 other vhosts |
| | |
| +-- upstream mm_api (least_conn, passive |
| | health checks, keepalive 32) |
| | +-- 127.0.0.1:8094 Bubbles |
| | +-- 100.77.114.40:8094 megamind-agi |
| | |
| +-- limit_req zone=mm_auth 1r/s, answers 429 |
| +-- missionmatch.thatwebhostingguy.com |
| | root /var/www/sites/missionmatch |
| +-- missionmatch-staging.thatwebhostingguy.com |
| | root /var/www/sites/missionmatch-staging |
| | /api/ proxies to 127.0.0.1:8097 |
| +-- fg-talent.com |
| root /var/www/sites/fgtp |
| |
| mm-api /home/user/missionmatch/mm-api |
| mm-api-staging /opt/missionmatch-staging/mm-api |
| mmorch :9505 mmdb :9506 mmk :9500 mmreason |
+---------------------------------------------------+
| |
| tailnet | tailnet
v v
+------------------------+ +---------------------------------+
| M4 Mac mini | | MEGAMIND-AGI |
| 100.94.8.94 | | Windows 11 + WSL2 Ubuntu 26.04 |
| 10 core, 16GB | | 100.77.114.40 |
| | | i7-8700K 12t, 26GB allotted |
| PostgreSQL 16.14 :5432 | | |
| missionmatch | | PostgreSQL 18 |
| TENANT DATA PLANE | | :5432 PANTHEON cluster |
| org 11111111... | | :5433 missionmatch cluster |
| 86 tables, v84 | | missionmatch |
| missionmatch_staging | | CONTROL PLANE, v84 |
| full stack, v84 | | missionmatch_staging |
| | | EMPTY SHELL, unused |
| megamind-gateway :8090 | | |
| bearer authenticated | | mm-api :8094 second node |
| Ollama :11434 | | /opt/missionmatch/mm-api |
| bge-small-en-v1.5 | | |
| llama3.1:8b | | PANTHEON organs: mm-oracle 9402,|
+------------------------+ | mm-loom 9410, mm-serve, mm-eye, |
| mm-sem, mm-world, mm-game, |
+------------------------+ | mm-narrate, mm-shard, |
| VALKYRIE M1, 8GB | | mm-genserve |
| 100.89.0.6 codex 8790 | +---------------------------------+
+------------------------+
WHY THIS SHAPE
The API tier is genuinely liquid. mm-api holds no per process state: sessions
live in the control plane database, CSRF is a signed double submit. Any request
may be served by either node, and a session minted on one is valid on the other.
Passive health checks take a dead node out and put it back with no operator
action. Verified by stopping a node mid traffic: 14 of 14 requests succeeded.
The DATABASE tier is deliberately NOT liquid, and this is the single most
important architectural decision in the system. Recruiting correctness rests on
FOR UPDATE row locks that serialise seat caps, stage transitions, and ownership
claims. Multi master would break every one of them silently. So state is one
primary per tenant.
What IS distributed is TENANTS. tenant_directory records a host and port PER
TENANT, so different tenants live on different machines while each still has
exactly one primary and every lock keeps working unchanged.
THE LIABILITY OF THIS SHAPE, stated plainly. Three of the four machines are
consumer hardware in one physical location, two of them are a laptop and a
desktop that can be closed or unplugged, and there is no off machine backup of
either database. The design is cloud portable by construction and is running on
a home network by circumstance. Those are different facts and only the first is
an architectural claim.
TOPOLOGY RULE, owner decision 29 July 2026, and it is enforced rather than
aspirational:
BUBBLES CARRIES EXACTLY TWO THINGS
hosted website content, the 138 nginx vhosts
deployed production artifacts, the API node binary and the job binaries
systemd actually runs, plus two rollback points
EVERYTHING ELSE LIVES ON MEGAMIND-AGI
build workspace /home/user/missionmatch/workspace
document intake /home/user/missionmatch/inbox
archived build artifacts /home/user/missionmatch/archive
Bubbles is the revenue web host. Build residue there competes with customer
vhosts on a disk already at 82 percent, and a scratch file left beside a deployed
binary is something a later session can mistake for deployed state. Audited and
enforced on 29 July: 463 MB of stale rollback binaries, 59 MB of gate captures,
and twelve staged scripts were archived to megamind-agi by checksum and only then
removed, with every serving surface verified untouched afterwards.
--------------------------------------------------------------------------------
2. CONTROL PLANE AND DATA PLANE
--------------------------------------------------------------------------------
CONTROL PLANE
megamind-agi 100.77.114.40 PORT 5433, database missionmatch
organizations, users, auth_credentials, mfa_totp, mfa_recovery_codes,
auth_sessions, user_invites, login_attempts, user_tool_access,
tenant_directory. Schema v84.
This is what MM_DATABASE_URL points at.
ANSWER EVERY IDENTITY QUESTION HERE.
TENANT DATA PLANE
M4 100.94.8.94 PORT 5432, database missionmatch. Schema v84.
Everything else. 86 tables, 50 with forced RLS.
Reached ONLY by routing through tenant_directory. Named in no env file
except as the value of MM_TENANT_DSN_TENANT_ACME.
ITS COPY OF users IS NON AUTHORITATIVE, and is currently stale and
incomplete: it was missing two of the five real accounts and had a
stale status on a third.
STAGING
M4 100.94.8.94 PORT 5432, database missionmatch_staging. Schema v84.
2766 organizations, 8872 candidates, its own API, vhost, and isolated
role. Runs shared tenancy, not per_tenant.
NOT USED
megamind-agi 5433 database missionmatch_staging is an EMPTY SHELL.
Named in an early amendment. Must never be used.
WHY SPLIT. Login resolves a user by GLOBAL email BEFORE any organization is
known. Handing the router to auth would make authentication depend on the very
answer it exists to produce. So auth is wired to a direct pool and never to the
router (cmd/api/main.go line 257), while business queries go through the router.
Global email uniqueness does NOT break under per tenant databases, because
identity is authoritative in ONE database. The unique index there is genuinely
global. Copies of users in tenant databases exist only so business tables can
foreign key to a user, and are synced, not authoritative.
TWO MODES, selected by MM_TENANCY_MODE. shared: one database, isolation by
forced RLS, the default, what staging runs. per_tenant: each organization's
business data in its own database, resolved per request, RLS still forced
INSIDE each tenant database so the mechanisms stack. Production runs per_tenant,
confirmed from the boot log: secrets_backend=env max_pools=32
max_conns_per_pool=4 idle_evict=15m0s.
PER ENVIRONMENT ROLE NAMES, from incident D6, 29 July 2026.
ROLES ARE CLUSTER SCOPED. DATABASES ARE NOT. The production tenant database and
missionmatch_staging live in ONE M4 cluster and therefore share ONE role
namespace, so for a long time they shared missionmatch_migrator. A rehearsal
aimed at staging changed the password of a role production depended on, and one
of the two API nodes was down for one minute fifty nine seconds as a result.
nginx failed over and no client saw a 5xx, which is the redundancy working rather
than an argument that the mistake was small.
missionmatch_migrator PRODUCTION TENANT only
missionmatch_staging_migrator STAGING only, CONNECT on the production
database REVOKED, proven by refusal
The containment is proven with a positive control rather than asserted: the
staging role reads 2,784 staging organizations, and the same credential against
the production database is refused with permission denied.
THE RULE THAT GOES WITH IT: on a shared cluster, roles, role passwords,
extensions, tablespaces, and cluster settings are PRODUCTION CHANGES by
definition, whichever database the statement names. Rehearsals of cluster scoped
mutations do not exist.
Every credential and every consuming location is inventoried in
docs/CREDENTIAL_CONSUMERS.md, references only. Rotations enumerate from it,
because the rotation that caused D6 was enumerated from memory and missed two.
--------------------------------------------------------------------------------
3. ENVIRONMENTS
--------------------------------------------------------------------------------
DEVELOPMENT STAGING PRODUCTION
API local go run Bubbles :8097 Bubbles :8094 and
megamind-agi :8094
control DB local or staging M4 missionmatch_ megamind-agi:5433
staging missionmatch
data plane same as control same (shared) M4:5432 missionmatch
tenancy shared shared per_tenant
DB role missionmatch_app missionmatch_ missionmatch_app
staging_app
MM_ENV development development development
MFA off off off
operator MFA on (the default) off off
binary HEAD HEAD baseline + track only
THE STAGING ISOLATION STORY, because it failed twice before it passed.
Round one: staging and production ran as the SAME role on the SAME cluster, so
staging credentials could connect to production and successfully write a row.
The zero row reads people took for isolation were RLS with no org GUC set, not a
privilege denial. PostgreSQL grants CONNECT to PUBLIC by default, which is what
made every role in the cluster able to reach production. Fixed by creating
missionmatch_staging_app with grants mirrored inside missionmatch_staging only,
granting CONNECT on production explicitly to the four legitimate roles, and
revoking the PUBLIC blanket.
Round two: that fix mirrored TABLE grants and not FUNCTION grants, so the new
role held EXECUTE on ZERO of the nineteen SECURITY DEFINER auth functions.
Nothing could log in on staging at all and invite acceptance returned 500. Found
28 July, nineteen grants mirrored in. IF A ROLE IS EVER CLONED AGAIN, CLONE
FUNCTIONS AS WELL AS TABLES.
--------------------------------------------------------------------------------
4. REQUEST DATA FLOW, THE MAP
--------------------------------------------------------------------------------
browser
|
| https, __Host- session cookie, X-CSRF-Token header
v
nginx on Bubbles
| TLS terminate, limit_req on /api/auth/, proxy_next_upstream
| (never non_idempotent: a GET may retry on the other node, a POST never
| does, because a retried write could apply a placement twice)
v
chi router (cmd/api/main.go)
|
+-- obs middleware structured slog, request id, metrics
+-- ratelimit token bucket, PER PROCESS
+-- CSRF Protect signed double submit, bound to the session hash
+-- SessionMiddleware __Host- cookie -> auth.Validate
| |
| +-> CONTROL PLANE: session row, state=active, mfa_satisfied
| +-> loads role AND tool entitlement onto the principal
| +-> EffectiveTools widens the top seats BY RULE, once, here
| +-> tenant.WithOrg(ctx, org) + tenant.WithUser(ctx, principal)
|
+-- RequireCapability what you may DO (22 capabilities) 403
+-- RequireTool what you may SEE (4 products) 404
+-- RequirePlatform the platform plane (operator only) 404
|
v
module handler (internal/modules/<name>/handler.go)
| thin: decode, call the service, map the error
v
service (service.go)
|
+-- db.RunInTenantTx(ctx, fn)
| |
| +-- per_tenant: router resolves org -> tenant_directory ->
| | secrets resolver (MM_TENANT_DSN_<REF>) -> tenantpool
| | (bounded LRU, singleflight, ping before cache, refcount pin,
| | typed errors that NEVER fall back to another tenant)
| |
| +-- shared: the one pool
| |
| +-- BEGIN
| +-- SELECT set_config('app.current_org_id', $1, true)
| | transaction local, so it cannot leak across the pool
| +-- fn(ctx, tx) <-- ALL repository work happens here
| +-- COMMIT (or ROLLBACK on any error)
|
v
PostgreSQL
forced row level security, policy = org matches the GUC,
no GUC set yields ZERO rows (default deny, fail safe)
TWO STACKED SAFEGUARDS:
Go side no validated org in context, RunInTenantTx refuses before the pool
is touched at all.
DB side an unset GUC makes current_setting(...,true) NULL, so the policy
matches nothing.
PROVEN, not asserted, on 28 July using the production DSN the running process
holds: connect as missionmatch_app, confirm rolbypassrls=false, read candidates
with NO org GUC and get 0 rows, read the same table inside a transaction with
the GUC set and get 11. That is the application's real path.
THE TRAP THAT CATCHES EVERYONE. A query with an explicit
"WHERE organization_id = $1" predicate LOOKS scoped and is not. Forced RLS
filters the row out before the predicate is evaluated, so a statement on a bare
pool connection with no GUC matches ZERO rows and returns "not found" for a row
that plainly exists. PATCH /me/profile answered 404 to the signed in user on her
own account for exactly this reason. Any read or write of a tenant table must be
inside RunInTenantTx, without exception.
THE RATE LIMITER IS PER PROCESS, so a client's effective allowance is the
configured rate times the node count. That is why the login throttle lives in
nginx, where it is globally correct regardless of how many nodes exist, and the
in process limiter is a second layer only.
--------------------------------------------------------------------------------
5. AUTHORIZATION MODEL
--------------------------------------------------------------------------------
THREE INDEPENDENT AXES. Collapsing any two would be wrong.
ROLE = what you may DO
viewer(0) member(1) recruiter(2) manager(3) admin(4) owner(5)
superuser(6) operator(7)
OPERATOR is the PLATFORM PLANE, above and outside the tenant ladder. It
holds every platform capability and every tenant capability in every
tenant. It exists because superuser previously meant two things at once,
the platform seat and the client's top seat, which is why a client was
once shown platform shaped controls that refused her.
SUPERUSER is now the TENANT top seat: everything BY RULE WITHIN ITS
TENANT, explicitly not the platform capabilities.
Grant rule: strictly BELOW your own, with one exception. Superuser may
grant superuser, because a tier that cannot reproduce can only shrink and
an organization losing its last holder would be locked out of its own
access control. Nobody may change their OWN role, ever.
PLATFORM CAPABILITIES are the ones that act across every tenant at once.
Today exactly {curation.curate}, which writes the GLOBAL skill ontology
every tenant is scored against. A client's top seat approving an ontology
edge would change what a competitor's candidates score.
TOOL = what you may SEE
missionmatch, screenmatch, slatematch, recruitmatch.
Per user, explicit, FAILS CLOSED for every role below superuser. Two
bounds on granting: outrank the target, AND confer only tools you hold.
Revocation needs only the first, because taking access away is safe.
BY RULE for the top seats, because relying on provisioning to keep an
invariant true is not the same as the invariant being true: the design
partner's top seat held superuser with ZERO grants and saw nothing.
Enforced on each tool's route GROUP. Answers 404, never 403.
PLANE = whether the surface is tenant scoped at all
RequirePlatform, operator only, 404. A platform surface must be
indistinguishable from a path that was never built.
NO SILENT GREY. A disabled entitlement control must STATE ITS REASON, and the
two reasons are distinct because they have different fixes: the organization is
not entitled to that product, or you do not personally hold that tool.
OPEN DESIGN POINT: no organization level product entitlement table exists yet,
so every tenant is treated as entitled to all four.
security.OrgEntitledTools(orgID) is the single place that narrows when the
Phase 5A operator console lands. It takes orgID today, unused, so adding the
lookup is a body change and not a signature change through every caller.
--------------------------------------------------------------------------------
6. AUTHENTICATION AND SESSION SECURITY
--------------------------------------------------------------------------------
PASSWORD KDF PBKDF2 HMAC SHA 256 (SP 800-132). NEVER argon2 or bcrypt, which
sit outside the validated boundary. 600,000 iterations. Input
capped at 64 characters to stay inside the CAVP tested length
bound. Exceeding the tested iteration envelope is a STRONGER
choice, not a deviation; state it that way to an assessor.
PEPPER Versioned, HMAC SHA 256 under a server held key, applied BEFORE
PBKDF2 so it stays in boundary. Per credential pepper_version
so it can rotate without a flag day.
MFA TOTP RFC 6238, HMAC SHA 256 end to end, 32 byte secret, 6
digits, 30 seconds, strictly monotonic replay guard so a used
code and every earlier in window step cannot be replayed.
Implemented on crypto/hmac rather than a library, so every
primitive is provably inside the validated module. Secrets
AES 256 GCM at rest with a key_version. 10 recovery codes.
SESSIONS Opaque, server side, 32 random bytes, stored ONLY as a SHA 256
hash. __Host- cookie, HttpOnly, Secure, SameSite Strict.
Instantly revocable. Two stage: auth_pending then active.
15 minutes idle, 12 hours absolute, computed by the DATABASE
clock, not the application's.
CSRF Signed double submit BOUND to the session hash. That binding is
what stops an attacker being handed a token that works against
a victim's session.
LOCKOUT Keyed on EMAIL alone, never (email, ip). Scoped to the pair it
was free to bypass by rotating source addresses, which defeated
the per ip rate limiter with the same move, so two controls
shared one exit. The audit records the source spread.
IDENTITY One human, one account, one org. Global unique index on
lower(email). Login resolves by global email BEFORE the org GUC
is set, through a SECURITY DEFINER cross org lookup.
PROVISIONING No public signup. First org and owner by the migrator role
through a migrator only SECURITY DEFINER function. Everyone
else by authority bounded invite. Delivery is MANUAL at pilot:
the endpoint returns the one time link and a human delivers it.
DEFERRED OIDC Login.gov, Entra Gov, SAML, WebAuthn, PIV CAC, and public
forgot password are sub phase 2.9. The five federal auth
libraries are deliberately not in go.mod yet.
CURRENT DEVIATIONS, both deliberate and both recorded:
MM_MFA_REQUIRED is FALSE everywhere, so login is password only. Config
REFUSES this when MM_ENV is production.
MM_OPERATOR_MFA_REQUIRED is FALSE in production only, set 28 July 2026 at
the owner's explicit direction. Default is true, staging proves the rule,
config refuses it in a production MM_ENV, and the boot log states it at
every start. Re arming is one environment variable per node.
--------------------------------------------------------------------------------
7. THE FOUR PRODUCTS, IN DEPTH
--------------------------------------------------------------------------------
All four are ONE binary, ONE database, ONE candidate spine. A product is a
TOOL ENTITLEMENT plus a route group plus a set of screens. There is no
duplicated candidate model, no second scoring engine, and no per product
database. That is the whole reason a person can sit on a proposal slate and in
a recruiting pipeline at once and resolve to one candidate id.
................................................................................
7.1 missionMATCH "who can support this mission today"
................................................................................
WHO IT IS FOR: capture leads and program managers who need to know whether the
bench can cover a mission right now.
SCREENS (frontend/src/tools/missionmatch/)
CommandCenter.tsx mission readiness aggregation, the executive view
Candidates.tsx the candidate roster and profile
Ask.tsx the natural language interface to the AI layer
WhoCanSupport.tsx the named question the product is built around
API SURFACE
GET /command-center readiness tiles
GET /command-center/federal-signal external signal overlay
POST /command-center/upcoming/{id}/adopt adopt a forecast record
GET /candidates/intelligence enriched candidate view
GET /requirements/{id}/support who can support this
POST /ai/query the Ask path
WHAT IT ACTUALLY COMPUTES. The command center derives readiness from live
counts, not from a stored score: available candidates by clearance tier against
open requirement demand, producing a readiness band and a list of named gaps
("NEED 10 Intelligence Analysts, 3 Data Scientists, 2 Cyber Architects"). The
demo seed is deliberately shaped so that Win Impact reads HIGH on the Candidates
page while the Command Center still names the open gaps, so the two screens tell
one connected story rather than contradicting each other.
STATE: works. Renders with zero 5xx. Data is thin: 11 candidates, 6
requirements in production.
................................................................................
7.2 screenMATCH "is this candidate qualified for this requirement"
................................................................................
WHO IT IS FOR: recruiters and proposal staff qualifying ONE person against ONE
requirement, and needing to defend the answer.
SCREENS (frontend/src/tools/screenmatch/)
ScreeningQueue.tsx the work queue of pending evaluations
CandidateEvaluation.tsx the full explainable result for one pairing
ScreenCompare.tsx two candidates side by side
AliasSettings.tsx skill alias and term normalization controls
API SURFACE
POST /match the flat legacy alias
POST /screen the canonical evaluation
GET /requirements/{id}/candidates ranked for one requirement
GET /candidates/{id}/opportunities the reverse direction
GET /candidates/{id}/clearance-provenance where a clearance claim came
POST /mission-check/screen the browser parse path
GET /mission-check/opportunities
WHAT MAKES IT DEFENSIBLE. Every evaluation returns the per component breakdown,
the band, which caps were applied and why, the engine version, the timestamp,
and which candidate skill matched which required skill with the similarity that
matched them. That last item is the one an auditor asks for and the one most
products cannot produce.
CLEARANCE PROVENANCE is its own endpoint on purpose: a clearance value is a self
reported indicator, and the product must be able to show WHERE the claim came
from rather than presenting it as fact.
STATE: works. The most complete product in the platform after the engine.
................................................................................
7.3 slateMATCH "who belongs on the proposal"
................................................................................
WHO IT IS FOR: proposal and capture teams building a named staffing slate for a
bid. THIS IS THE COMMERCIAL DIFFERENTIATOR: requirement to slate.
SCREENS (frontend/src/tools/slatematch/)
Slate.tsx the ranked slate with per member decisions
Capture.tsx opportunity intake (Phase 1, DARK in production)
OpportunityDetail.tsx opportunity intelligence and edit (DARK)
API SURFACE
POST /slates/generate build a ranked slate
GET /requirements/{id}/slates slates for a requirement
GET /slates/{id} one slate
POST /slates/{id}/members/{candidateID}/decision approve or reject
GET /slates/{id}/members/{candidateID}/trace the decision trace
GET /slates/{id}/proposal-readiness readiness for the bid
GET /slates/{id}/export csvsafe export
DARK IN PRODUCTION until Gate 6, mounted only at repository HEAD:
POST /opportunities/tracked, PATCH .../{id}, GET .../scope,
POST .../scope/items, POST /requirement-drafts/{id}/approve and /reject
DECISION VOCABULARY: slate_members.decision is CHECK constrained to
pending, approved, rejected. Human in the loop is a database constraint, not a
convention. Nothing auto rejects.
EXPORT goes through internal/platform/csvsafe, which escapes formula injection,
because a slate export is opened in Excel by someone who did not create it.
STATE: the slate path works. The opportunity capture front end is BUILT and
verified on staging but deliberately DARK in production, because the Gate 6
freeze says no build feature route is reachable in production until the whole
program passes. Proven dark after every deploy.
................................................................................
7.4 recruitMATCH "work the desk"
................................................................................
WHO IT IS FOR: the recruiting desk. Clients, job orders, candidate pipeline,
interviews, offers, placements. This is the operational heart and it is the
LEAST exercised product in the platform.
SCREENS (frontend/src/tools/recruitmatch/), 11 of them
RecruiterDashboard.tsx the desk overview
Clients.tsx / ClientDetail.tsx
Requisitions.tsx / RequisitionDetail.tsx
Pipeline.tsx the stage board for one requisition
Assignment.tsx one application in depth
Tasks.tsx ActivityFeed.tsx Analytics.tsx Reports.tsx
API SURFACE, the largest in the platform, 40 routes
clients GET/POST /clients, GET/PATCH /clients/{id},
POST /clients/{id}/contacts, PATCH /contacts/{id},
POST /clients/{id}/claim | /release | /reassign
requisitions GET/POST /requisitions, GET/PATCH /requisitions/{id},
POST /requisitions/{id}/status | /claim | /release,
GET /requisitions/{id}/pipeline,
POST /requisitions/{id}/applications
applications GET /applications, GET /applications/{id},
POST /applications/{id}/stage | /interviews | /offers |
/placement, GET /applications/{id}/assignment
offers POST /offers/{id}/status
placements GET /placements, POST /placements/{id}/end
work GET/POST /activities, PATCH /activities/{id}/note,
GET/POST /tasks, PATCH /tasks/{id}, GET /tasks/queue
reporting GET /analytics, GET /reports, GET /dispositions
THE STATE MACHINES, read from the live database constraints, not from docs:
applications.stage
sourced -> screened -> submitted -> interviewing -> offered -> placed
with rejected and withdrawn reachable from any stage
requisitions.status
draft, open, on_hold, filled, closed, cancelled
offers.status
extended, accepted, declined, rescinded, expired
placements.status
pending_start, active, completed, terminated, fell_off
THE LOCKS THAT MAKE IT CORRECT. recruiting/ownership.go holds three FOR UPDATE
locks (claim, release, reassign), requisition_service.go two, and
placement_service.go one on the seat cap. These are the reason the database tier
is not allowed to be multi master. Proven by racing them: 16 concurrent stage
transitions elected one winner, 8 racing claims produced one owner, 8 placements
into one seat produced one placement.
STATE, AND THIS IS THE HONEST PART: recruitMATCH has ZERO PRODUCTION DATA.
0 clients, 0 requisitions, 0 applications, 0 placements. The schema, the
services, the endpoints, the locks, the screens, and the analytics are all
built. Nothing has ever been created, because the New Client form is too thin to
record a real client. Expanding it is Phase 5 item 1.
Consequences today:
The dashboard "NOT TRACKED YET" panel is STALE HARDCODED COPY at
internal/modules/recruitmatch/derive.go:73 and is now wrong about
interviews, offers, and placements.
Revenue, bill rates, and candidate source are genuinely not tracked.
Time to fill has a migration but no service.
Dashboard stat cards are not clickable through to their filtered lists.
................................................................................
7.5 THE SHARED SPINE
................................................................................
ONE candidates table. ONE application record linking candidate to requisition.
The frozen engine is reached from BOTH directions, a job order ranking
candidates and a candidate ranking open job orders, through the same requisition
to requirement bridge. screenMATCH is the deep evaluation of one pairing.
slateMATCH is the proposal branch off the same spine.
Shared read routes are gated on holding ANY tool rather than a specific one,
because gating the candidate and requirement pickers to one product would break
the other three, while leaving them open would expose the candidate list to an
account entitled to nothing.
THE FLAT ALIAS SURFACE. Pre split paths (POST /match, /slates/..., /candidates)
still exist so older clients and the screenmatch redirect keep working. They
carry the SAME tool entitlement as their canonical namespaces. They previously
did not, which made the entitlement layer cosmetic: a user refused at
/slatematch/slates/{id} could fetch the same slate, its decision trace, and its
CSV export from /slates/{id} with no gate at all. The bookmark the gate exists
to defeat was the flat one.
--------------------------------------------------------------------------------
8. THE MATCHING ENGINE (the regulated core)
--------------------------------------------------------------------------------
Match Score = Clearance x0.25 + Skills x0.25 + Certifications x0.15
+ CustomerExperience x0.15 + Experience x0.10
+ LocationAvailability x0.10
Bands: Green (Proposal Ready) 90+, Yellow (Potential) 75 to 89.99,
Red (Recruiting Required) below 75.
Clearance: exact OR higher scores 100 percent; lower, expired, or unknown
scores 0. The hierarchy is encoded, so TS/SCI satisfies Secret and Secret does
NOT satisfy TS/SCI.
Skills: 100 percent of required met scores full, 80 to 99 partial, below 80
reduced. Exact match first, then semantic similarity through embeddings, and
the system always records WHICH candidate skill matched WHICH required skill
and the similarity, because an AEDT decision must be explainable at that grain.
TWO DETERMINISTIC BAND CAPS, applied after the band is derived. Neither ever
changes the Total; each can only LOWER a band:
1. A clearance gap caps at Red.
2. An unmet mandatory skill caps a Green at Yellow.
ALL scoring uses shopspring/decimal, never float64. The engine is a pure Go
package with no DB and no I/O, skill_matcher injected. That purity is not
aesthetic: it is what makes the score bit stable and reproducible, which is
what an AEDT compliance artifact requires.
THE ENGINE IS FROZEN. internal/modules/matching/engine is untouchable under
the current directive and its diff against the starting commit is checked and
must stay empty. Verified empty at this revision.
EEO data lives in a physically and privilege segregated schema the application
and scoring roles cannot read at all, with its own migration set and version
table so sqlc never models it. Demographics flow ONE WAY, into adverse impact
reporting run by a separate compliance role, never into a score. The matching
and search code must never reference the EEO tables.
THE COMPLIANCE ARTIFACT CHAIN, which is the actual product for a regulated
buyer: per component breakdown, human readable rationale, gap summary, engine
version, weights, caps applied, and a recordable decision trace. Humans
approve or reject every slate member and nothing is ever auto rejected.
Adverse impact reporting produces selection rates and four fifths ratios so a
customer can run their own LL144 or OFCCP audit. The platform SUPPLIES the
data for that audit; it does not constitute one, and vendor tooling does not
transfer the employer's liability.
--------------------------------------------------------------------------------
9. THE OPPORTUNITY PIPELINE
--------------------------------------------------------------------------------
An opportunity may enter through the SAM.gov API, manual entry, uploaded
solicitation documents, a customer or partner scope, a spreadsheet import, or a
future agency forecast feed. Every path converges into one workflow, and the
SAFETY BOUNDARY is that nothing a machine derived becomes visible to the scoring
engine without a named human approving it.
TABLES, all with organization_id, forced RLS, composite indexes:
ingest_manifest shared reference data, no RLS. EVERY synced row
and every knowledge card traces to a row here.
tracked_opportunities 31 columns. Unique partial index on
(org, public_opportunity_id) so a tenant cannot
track one notice twice.
opportunity_versions IMMUTABLE. A trigger allows only the is_current
flag to change and blocks DELETE outright.
opportunity_documents sha256 duplicate detection, extraction_status
state machine, malware scan hook between upload
and pending.
opportunity_document_chunks page, section, content hash, method, confidence
opportunity_scope_items ck_scope_items_cited REFUSES uncited machine
produced scope. A manual item may be uncited.
requirement_drafts ck_drafts_approved_has_requirement,
ck_drafts_decided_has_reviewer (so no automated
path can approve), ck_drafts_skill_flags_align
THE GATE RULES LIVE IN THE SCHEMA, not only in code. A future service bug
cannot write uncited scope or an unattributed approval, because the database
refuses it. The constraint names are listed so that claim is checkable.
PROMOTION is one audited transaction taking FOR UPDATE on the draft row,
recording the named approver, writing the requirement, and flipping the draft
together. Raced: 8 concurrent promotions produced exactly 1 requirement.
SEMANTIC RETRIEVAL IS LIVE. opportunity_records carries vector(384) with a
partial HNSW cosine index and a generated search_tsv with a gin index. 6,676
records embedded with qllama/bge-small-en-v1.5. "cybersecurity engineering
support for the Air Force" returns PACAF Cyber Systems Operations Support and
Cybersecurity Support Services at 0.78 cosine, ranked by meaning.
AMENDMENT HANDLING. sam-sync compares the source payload hash per tracking
tenant and on a change writes an immutable version, retires its predecessor in
the same transaction, cites the manifest, and raises an alert. Tenant authored
fields survive a source refresh BYTE FOR BYTE, proven by md5 fingerprint.
THE EXTERNAL BOUNDARY, redesigned 28 July 2026 after its fixtures found a hole.
PROVENANCE IS BOUND AT ORIGIN, migration 0081. Six tables carry a provenance
column that a DEFAULT supplies, a CHECK pins to one value, and a BEFORE INSERT
OR UPDATE trigger overwrites on insert and refuses to change on update. A row
in the public SAM cache is BORN public_cache; a tenant upload is BORN
tenant_upload; a candidate is BORN tenant_record. The application cannot choose
it, so fabricating provenance requires shipping a migration, which is a named
reviewable location rather than a constant at a call site.
WHY, and it is worth keeping. The gate was originally content based, and the
Gate 2 fixtures proved that cannot hold: a candidate row with the uuid, email
and phone stripped is still one named cleared human with a clearance, a
location and an availability, and it crossed; a customer's own performance work
statement contains no personal data at all and crossed too. The first is
private because of WHAT IT IS, the second because of WHERE IT CAME FROM, and no
pattern match on the bytes recovers either fact once a caller has flattened it
into a string. A second version took provenance as a CALL TIME argument, which
is a control one mistake defeats. Only the stored value survives contact.
The gate now takes the SET of provenance values read from every row a payload
was composed from. ONE TENANT ELEMENT POISONS THE WHOLE PAYLOAD, because a
public notice with one paragraph of a customer's PWS folded in is a customer's
PWS. AN EMPTY SET IS REFUSED, because silence is not permission.
A BACKSTOP REMAINS, and its limit is asserted by name rather than assumed away:
a caller that MISDECLARES a stripped candidate row is not caught by content
scanning, and a test says so explicitly so nobody mistakes the backstop for a
guarantee.
Only four egress classes may leave the machine:
PUBLIC_KNOWLEDGE, PUBLIC_DATA_SUMMARY, ONTOLOGY_SUGGEST, REQUIREMENT_NORMALIZE.
NEVER external, enforced by the gate and by tests: candidate rows, resume
text, names, emails, phone numbers, tenant identifiers, anything from the eeo
schema, RouteRAG and RouteGraph over tenant data, scoring inputs, scoring
outputs, slate contents. The gate fails closed to the local plane. Every
external call audits backend, class, byte count, and content hash, NEVER
content.
CURRENT DATA: 6,676 opportunity_records, 1 tracked_opportunity, 5 manifest
rows.
--------------------------------------------------------------------------------
10. THE AI LAYER, UNDER SCRUTINY
--------------------------------------------------------------------------------
This section is deliberately harder on the AI layer than a sales document would
be, because the failure mode of an AI feature in a regulated product is not a
bad answer, it is a confident bad answer that someone acts on.
THE ARCHITECTURE. Routed, not monolithic. One router picks the cheapest
sufficient strategy, defined in orchestrator.go:
RouteRefuse unsafe input (EEO, injection)
RouteTAG the database must compute (count, sum, aggregate)
RouteRAG retrieve the tenant's candidates and narrate
RouteDirect answer from stable rules in context (CAG), no retrieval
RouteGraph walk a cited graph evidence path
RoutePlan bounded plan and execute over closed intents
THE REAL SHAPE OF THE CODE, by size, which tells you where the weight is:
graph_route.go 835 planner.go 841 tag.go 798
mag.go 642 curation.go 655 orchestrator 524
graph_market.go 488 teaching.go 446 graph_teaming 403
plus graph_lifecycle, graph_agency, graph_loop, graph_skill,
graph_clearance, graph_bench, capture_readiness, knowledge, memory,
safety, suppression, vectorguard, egress, feed
WHAT IS GENUINELY STRONG
RAG, the hybrid retrieval. pgvector cosine kNN fused with tsvector rank by
Reciprocal Rank Fusion (k=60), IN SQL. This is the best piece in the layer.
It now spans TWO corpora, the tenant's candidates and the public opportunity
catalog, and the detector yields to any bench noun so a question touching both
planes keeps its candidate corpus rather than being hijacked by the market
one. Fusion in SQL rather than in Go means one query plan, one round trip, and
no reranking layer to drift.
THE CLOSED INTENT REGISTRY. 80 curated, parameterised, read only intents in
tag.go. The model selects an intent NAME. It never authors SQL, never authors
parameters, and never sees a query planner. THIS IS A SECURITY PROPERTY, NOT
A MISSING FEATURE, and it is the single most important decision in the layer.
Every NL to SQL system is one prompt injection away from an arbitrary read;
this one is not, because there is no code path from model output to SQL text.
THE VERIFIER. A faithfulness gate: model prose ships ONLY if the verifier
confirms it against the retrieved evidence, otherwise the user gets the
deterministic rendering of the same evidence. It FAILS CLOSED on a parse
error. This is what makes the layer safe to put in front of a recruiter: the
worst case is a plainer answer, not a wrong one.
THE POLICY STACK. EEO prohibition on the question itself, read only SQL
guard, table allowlist, stacked statement rejection, k anonymity suppression.
A federation audit widened the protected noun matching beyond bare terms to
close proxies, which is the part most implementations miss.
WHAT IS REAL BUT SHALLOWER THAN IT SOUNDS
GraphRAG. Recursive CTE traversal, depth capped, cycle guarded, across 20
graph files covering agency, bench, clearance, lifecycle, loop, market, route,
skill, and teaming. It is real graph traversal over real relational data with
real citations. It is NOT a graph database, there is no learned embedding of
the graph, and the traversals are hand written per domain. That is a
reasonable engineering choice and it does mean "GraphRAG" here means
"parameterised recursive SQL with provenance", which is less than the term
implies elsewhere.
PAG, the planner. K=3 self consistency, DAG execution, real data dependency
chaining between intents. 841 lines. The plan space is bounded by the same
closed intent registry, so it cannot plan its way to something the registry
does not already permit. That bound is what makes it safe and also what caps
how clever it can be.
KAG and OAG. 30 cited GovCon knowledge cards plus O*NET, NICE, PSC, and CALC
ontologies. Cited is the operative word. But 30 cards is a small corpus and
curation is a manual, operator only, two step approve then verify process. It
is trustworthy and it is not large.
WHAT IS SCAFFOLDED, OFF, OR HONESTLY ABSENT
The teaching signal channel. missionMATCH emits contents free craft signals.
PANTHEON does not learn from them: the gateway VALIDATES a signal and then
DISCARDS it. The working federation path is the knowledge feed harvest, not
the teaching channel. Anyone reading "teaching loop" should read "built,
validated, and currently a no op at the consumer".
Federated differential privacy. Scaffolded and OFF. The Gaussian sampler
returns ErrNotImplemented. It is a placeholder with a good shape, not a
capability.
The external OpenAI backend. Built, boot guarded (enabled with no secret
exits 1, proven against the live gateway), and currently pointed at the LOCAL
model. REQUIREMENT_NORMALIZE has therefore never executed against OpenAI end
to end. Enabling it is a config change protected by the boot guard, not new
code, but the end to end path is UNPROVEN.
DELIBERATELY ABSENT, and these are decisions, not gaps:
Self-RAG needs a fine tuned critic; incompatible with a stateless
commodity plane and the no Python rule.
Web retrieval must not happen with CUI.
NL to SQL replaced by the closed registry, on purpose.
Multimodal, vision no use case in a text domain.
THE SCRUTINY THAT MATTERS MOST
1. The layer's safety rests on the verifier and the closed registry, and both
are code paths, not model behaviour. That is the right place to put trust.
2. The EEO prohibition operates on the QUESTION. A question that never names a
protected attribute but correlates with one is not caught by it. The real
defence there is that demographics are in a schema the query role cannot
read at all, which is architectural rather than heuristic. That is the
stronger guarantee and it should be the one cited.
3. Nothing in this layer can write. Every intent is read only and the guard
enforces it. The AI cannot change a score, a stage, or an entitlement.
4. The inference plane is a SEPARATE stateless Go binary on another machine,
bearer authenticated. The FIPS API binary never hosts a model; it speaks
HTTP. So a model process crash cannot take the API down and a model cannot
be reached without the token.
5. UNPROVEN AT SCALE. Every claim above is proven by tests and by small live
checks. No part of this layer has served production traffic at volume,
because production has five users and 11 candidates.
--------------------------------------------------------------------------------
10A. THE LEARNING BOUNDARY, THREE DOORS AND NO FOURTH
--------------------------------------------------------------------------------
A standing rule about what INGESTION is allowed to change, recorded 28 July 2026
as the corpus began to grow at scale.
INGESTION MAY OPEN EXACTLY THREE DOORS:
1. THE CORPUS new documents, chunks, notices, attachments
2. THE EMBEDDINGS new vectors over that corpus
3. ONTOLOGY SUGGESTIONS candidate skills and adjacencies PROPOSED for curation
THERE IS NO FOURTH DOOR. Ingestion never alters scoring weights, never alters the
band thresholds, never alters the clearance hierarchy, and never promotes an
ontology suggestion into the live ontology by itself.
WHY IT IS WRITTEN DOWN. The scoring engine is an AEDT under NYC LL144 and OFCCP.
Its weights are frozen, diffed, and exhaustively tested precisely so a customer
can point at one number and say what produced it. A pipeline that learned from the
market and adjusted those weights would produce a scoring function nobody could
reproduce, explain, or audit, and it would do it silently, one federal document at
a time. The failure would not look like a bug. It would look like the numbers
slowly changing.
DOOR 3 IS THE ONE THAT LOOKS SAFEST AND IS NOT. An ontology that grows itself
changes which candidate skills match which required skills, which changes the
Skills component, which changes the score. So a SUGGESTION is data, a PROMOTION is
an operator action by a named human, and the gap between them is load bearing.
cmd/promote-ontology exists for exactly that gap.
HOW IT IS ENFORCED TODAY, honestly: by the frozen engine diff against e74fd2e
asserted EMPTY at every drift check, by ontology promotion being a separate
operator binary, and by nothing else. It is a rule with a DETECTOR, not a rule
with a LOCK, and it is named that way so nobody mistakes one for the other.
--------------------------------------------------------------------------------
11. FEDERATION RELATIONSHIP TO PANTHEON
--------------------------------------------------------------------------------
ONE WAY, by design. missionMATCH --publishes--> PANTHEON
missionMATCH is a standalone product, NOT an organ of the federation. It is not
in ORACLE's registry, nothing routes tasks into it, and it has no knowledge that
any consumer exists.
GET /knowledge/manifest declares federation_member:false, routable:false,
as DATA not prose
GET /knowledge/cards the whole curated public set in one pull
Both sit OUTSIDE /api, because the public vhost proxies only /api/ and /healthz.
They are reachable across the tailnet, not from the internet, and a bulk pull
does not spend the API rate limit real traffic depends on.
PANTHEON harvests hourly through curator-db.sh, content addressed and
idempotent, alongside codex-lore and seferim-events.
WHY PUBLISH RATHER THAN JOIN: a registered organ is DELEGATED to, and the
orchestrator needs it reachable every time. A harvested feed is ABSORBED, and
the consumer knows the material afterwards with missionMATCH offline or gone.
Learning is what survives the source going away.
--------------------------------------------------------------------------------
12. FILE ARCHITECTURE
--------------------------------------------------------------------------------
missionmatch/
|
+-- cmd/ 36 binaries
| +-- api/ THE SERVER. Config, FIPS guard, pools,
| | modules, routes. ~720 lines, all wiring.
| +-- megamind-gateway/ stateless inference proxy (runs on M4)
| +-- provision-org/ create an org and its first owner
| +-- provision-tenant/ create a NEW empty tenant database
| +-- register-tenant/ adopt an EXISTING database into the directory
| +-- migrate-all-tenants/ fan out migrations across control plane and
| | every active tenant (see Section 14)
| +-- sync-tenant-identity/ copy org and users into a tenant DB
| +-- sam-sync, opp-sync, awards-sync, rates-sync, oews-national-sync,
| | ontology-sync, missionclass-sync, icims-sync external ingestion
| +-- opportunity-scope-normalize model normalization into cited drafts
| +-- aioe-run, aioe-sim, aioe-backfill adaptive weight optimization
| +-- train-model, eval-model, eval learned model lifecycle
| +-- teach-aggregate contents free craft signals
| +-- fairness-snapshot adverse impact monitoring
| +-- embed-backfill, verify-vector-model, verify-resolver
| +-- alert-sweep, staffing-proactive-sync, demo-seed, brain-probe,
| import-candidates, import-bench, promote-ontology, tag-missions
|
+-- internal/platform/ 31 cross cutting packages
| +-- config/ ALL configuration and validation. Cloud choice
| | is CONFIG, never code.
| +-- database/ pool construction, pgvector codecs per
| | connection, role assertion failing BOOT closed
| +-- tenant/ THE CORRECTNESS BOUNDARY. RunInTenantTx, the
| | org GUC, the Router interface, Principal
| +-- tenantpool/ bounded per tenant registry: LRU evict,
| | singleflight, ping before cache, refcount pin,
| | typed errors that never fall back
| +-- directory/ tenant_directory access
| +-- identity/ seed org and users into a tenant DB
| +-- secrets/ Store (named) and Resolver (per tenant refs,
| | TTL cached, env backend MM_TENANT_DSN_<REF>)
| +-- security/ rbac, capabilities, tools, CSRF, password
| | hashing, TOTP, session tokens, RequirePlatform
| +-- audit/ append only trail, System() and OnTx()
| +-- inference/ typed client, circuit breaker, bounded retries
| +-- scrub/ egress payload classes, enforced twice
| +-- csvsafe/ formula injection escape
| +-- federated/ differential privacy scaffolding (OFF)
| +-- embeddings/, magstore/, learn/, eval/, aioe/, docextract/, extraction/
| +-- samgov/, samawards/, bls/, marketintel/, forecast/, connector/
| +-- ratelimit/, obs/, storage/, ontology/
|
+-- internal/modules/ 24 domain modules
| | each: handler.go, service.go, dto.go, repo.go
| +-- auth/ login, MFA, sessions, invites, users, roles,
| | tool entitlement, self service profile
| +-- matching/ THE ENGINE plus screen and slate cores
| | +-- engine/ pure deterministic scoring, decimal, FROZEN
| +-- screenmatch/ slatematch/ missionmatch/ recruitmatch/
| +-- recruiting/ THE DESK, 40 routes, the state machines
| +-- opportunities/ public cache PLUS the tenant capture layer
| +-- commandcenter/ mission readiness aggregation
| +-- ai/ orchestrator, retrieval, planner, 20 graph
| | files, memory, knowledge, safety, suppression,
| | vectorguard, curation, teaching, egress, feed
| +-- eeo/ adverse impact, SEGREGATED
| +-- governance/ retention, AEDT inventory, model registry
| +-- aioe/ adaptive weights
| +-- opportunity_intelligence/, awards/, wages/, sourcing/, staffing/,
| alerts/, bidoutcomes/, ontology/, termalias/, org/, federalsignal/
|
+-- db/
| +-- migrations/ 79 files, head 0080. The ONLY way schema
| | changes. 0020 is absent from the sequence;
| | numbers are never reused.
| +-- migrations_eeo/ SEPARATE set, separate version table, so sqlc
| | never models the eeo schema
| +-- bootstrap/ cluster roles
| +-- queries/ sqlc source SQL
|
+-- frontend/src/ 87 files, 13,217 lines
| +-- App.tsx routes + ToolGuard entitlement redirect
| +-- main.tsx ErrorBoundary > QueryClient > Router
| +-- components/ AppShell (tool switcher, nav, signed in
| | identity), ui, ErrorBoundary, ExecutiveCards
| +-- lib/ api, auth, AuthContext, can (capabilities,
| | tools, the two disabled reasons), homeTool,
| | queries, types
| +-- screens/ Login, AcceptInvite, Admin, Settings,
| | Compliance, Requirements, TimeZonePanel,
| | NotFound
| +-- tools/ 22 views across the four products (Section 7)
|
+-- docs/ CLAUDE_CODE_BUILD_DIRECTIVE (the work order),
| DIRECTIVE_AMENDMENTS_CONSOLIDATED,
| DIRECTIVE_AMENDMENT_6, BUILD_STATE (the append
| only ledger), AI_RESEARCH, STACK_DECISIONS,
| MODEL_CARD, runbooks
+-- ops/m4/ gateway lifecycle
+-- scripts/deploy.sh encodes three SILENT failure modes
--------------------------------------------------------------------------------
13. HOW IT IS BUILT
--------------------------------------------------------------------------------
NON NEGOTIABLE RULES (CLAUDE.md):
1. No Python anywhere. Go backend, TypeScript frontend, bash scripts. HTML
written by hand, never generated by a program.
2. No dashes in written output. Hyphens only inside URLs, HTTP header names,
CLI flags, and code identifiers.
3. Every API key stays on the server. Root owned 0600, never in the repo,
never in the browser bundle.
4. Clearance is "clearance intelligence", never "verified".
THE BUILD PROGRAM. Work runs under docs/CLAUDE_CODE_BUILD_DIRECTIVE.md, phase
locked: work only on the lowest incomplete phase, never start phase N+1 until
every item in the phase N gate is verified and recorded in docs/BUILD_STATE.md.
That ledger takes only VERIFIED items; aspirations never enter. Phase 0 and
Phase 1 complete with gates passed, Phase 2 current, Phases 3 through 6 not
started. TRACK U (identity and access) ran in parallel and is complete. Tasks T1
and T2 are standalone client site work outside the phase gates.
FROZEN AND UNTOUCHABLE: internal/modules/matching/engine, db/migrations_eeo,
every existing migration file, generated sqlc output, append only audit
semantics.
ENGINEERING INVARIANTS:
organization_id, forced RLS, and composite indexes leading with
organization_id, on every tenant table.
ON DELETE SET NULL on a composite FK MUST use the column list form. The bare
form tries to null organization_id, which is NOT NULL, making the
referenced row undeletable. Migration 0053 repaired 24 of these.
audit_logs is append only. UPDATE and DELETE revoked, triggers enforce it.
A down migration may drop what its up added but may NOT delete rows the
application authored. Several downs REFUSE with a count.
Widen a CHECK from the CURRENT constraint, never from an old migration's
vocabulary, or a later migration's additions are silently dropped. Caught
twice.
Role enum values are EXTENDED, never renamed, never removed.
VERIFICATION METHOD:
Reproduce the defect against a live database FIRST, fix, verify against the
same database, verify end to end through the live AUTHENTICATED API, then
look at the rendered screen.
Concurrency proven by RACING it: 16 concurrent stage transitions elected one
winner; 8 racing claims one owner; 8 placements into one seat one placement;
8 concurrent promotions of one draft one requirement.
Artifacts are GREPPED, not trusted. A bundle is checked for the strings it
must and must not contain, because a source tree that looks right can still
build the wrong thing. This caught a Phase 1 navigation link riding into a
production bundle on 28 July.
A NEGATIVE RESULT IS ONLY EVIDENCE WHEN THE INSTRUMENT IS KNOWN TO WORK.
Added 28 July after being burned twice in one day (Section 16). A check that
can pass by doing nothing needs a positive control.
THE MERGE GATE: make build-test-gates runs gofmt, go vet, the full unit suite,
the security gates, and a clean frontend production build. Green at this
revision.
--------------------------------------------------------------------------------
14. DEPLOYMENT AND ITS REAL FAILURE MODES
--------------------------------------------------------------------------------
Every item below is something that has actually gone wrong.
BUILD CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOFIPS140=v1.0.0
go build -trimpath
API DEPLOY Stage to /tmp, COMPARE SHA256, stop, swap, start, poll healthz.
BOTH nodes must get the same binary or the failover serves two
different answers. On 28 July the second node briefly ran a
binary with no operator role while the first had it, which
would have given the operator zero capabilities on one node and
everything on the other. The correct response is to mark the
stale node DOWN in the nginx upstream and reload BEFORE
shipping anything further.
SECOND NODE megamind-agi has no direct SSH and refuses publickey. Use
PASSWORD authentication to the WINDOWS host
josep@100.69.120.61, scp there, then run a STAGED BASH SCRIPT
through wsl. Multi line quoting through wsl breaks; stage a
file rather than inlining. WSL sudo is NOPASSWD.
MIGRATIONS Use cmd/migrate-all-tenants, NOT the goose CLI (which pulls
heavy unrelated drivers absent from go.sum). It EMBEDS
migrations with go:embed, so an edited .sql does NOTHING until
the migrator binary is REBUILT.
CRITICAL DEFECT, found AND FIXED 28 July 2026, recorded because
the failure mode is worth knowing. The tool built each tenant
DSN with swapDB(controlDSN, dbName), swapping only the DATABASE
NAME and IGNORING the db_host and db_port that tenant_directory
exists to carry. A tenant on another host was never reached: it
connected to a database of the same name on the CONTROL PLANE
host, migrated it a second time, and printed APPLIED for both.
Every run reported success while the production control plane
fell TWELVE migrations behind the tenant database.
The fix builds the target from the directory row, host and port
and name together. MM_TENANT_MIGRATOR_DSN_<REF> overrides for a
tenant whose credential differs, folded by the same rule
internal/platform/secrets uses. A row with no host is an ERROR,
never a guess, and a target that cannot be addressed is
reported FAILED and carried into the exit code rather than
dropped from the summary. Five tests pin it, including one
using an identical database name on both hosts, which is the
shape that made the original bug invisible.
THE GENERAL LESSON: a fan out tool that cannot reach a target
must FAIL, never silently succeed against something else.
Verify the version in EVERY database regardless of the summary.
FRONTEND npm run build, ship dist/assets, then rewrite ONLY the two
asset hashes in the live index.html. Overwriting it destroys
injected SEO lines. Verify the line count is unchanged.
ENV FILES sudo tee TRUNCATES. Stage to /tmp then cp, and assert the
result is non empty and not smaller before replacing.
POST DEPLOY Re prove the build feature routes DARK and run a zero 5xx
sweep, sampled enough times that both pool members answer.
AND INCLUDE AN AUTHENTICATED LEG: a sweep of unauthenticated
paths cannot see a defect behind the session gate, which is
exactly what hid a 500 on 28 July.
PRODUCTION BINARIES ARE NOT REPOSITORY HEAD. HEAD carries Phase 1 and 2 build
features that must stay dark until Gate 6. The production binary is built in a
worktree at the pre build baseline e74fd2e with only the approved track's
files overlaid and cmd/api/main.go hand resolved. Any deploy before Gate 6
must be built the same way and the artifacts grepped to prove it.
--------------------------------------------------------------------------------
15. OBSERVABILITY
--------------------------------------------------------------------------------
LOGGING Structured slog, JSON, request id, method, route, status, latency,
bytes on every request. Debug in development, info in production.
PII is never logged.
METRICS /metrics outside /api so a scrape never spends the API rate limit.
Feeds database pool gauges so connection saturation is visible.
Unauthenticated by design, firewalled to the scraping network.
HEALTH /healthz per node. nginx passive health checks handle failover.
AUDIT audit_logs is the operational record as well as the compliance one:
logins, failures, lockouts with source spread, permission changes,
identity changes, exports, every material write through OnTx.
ALERTS The alerts module plus alert-sweep.
SCHEDULED mm-sam-sync.timer runs daily SAM.gov ingestion on Bubbles.
WHAT RUNS ON A TIMER, all installed 28 July 2026 and each proven by an
induced failure where one was possible:
02:45 megamind-agi mm-cp-backup control plane dump, streamed through
gpg into ssh, pushed to Bubbles
03:05 Bubbles mm-db-backup tenant and staging dumps, ingest of
the pushed artifact only after it is
proven to decrypt, manifest, prune to
14 daily and 8 weekly
every 5 min Bubbles mm-watch public site, BOTH API nodes named
individually so a single node failure
is not masked by the pool, and 5xx
since the last run. Alerts on a
TRANSITION, once when a problem starts
and once when it clears.
hourly megamind-agi mm-identity-sync control plane to every active tenant,
reconciled after each run rather than
trusting the exit code
Delivery is mm-alert.sh through Gmail SMTP, reusing the app password the
form handlers already use. The systemd OnFailure notifier routes through it.
A TIMER THAT IS NOT ON THAT LIST AND FAILS EVERY DAY, found 28 July 2026 at
the Phase 2 drift check and recorded as finding D1:
05:16 Bubbles mm-fairness-snapshot FAILING SINCE INSTALLATION
It exits 1 with "set MM_COMPLIANCE_DATABASE_URL (the compliance role DSN, the
only path to the eeo schema)". The unit reads /etc/missionmatch/api.env and
that file does not carry the variable. Its sibling on the control plane node,
/etc/missionmatch/db.env, does, and that DSN authenticates, so the repair is
configuration and not code.
WHY THIS ONE MATTERS MORE THAN A FAILED TIMER USUALLY DOES. The adverse impact
snapshot is the AEDT compliance artifact this product exists to provide: the
selection rates and four fifths ratios a customer needs for an LL144 or OFCCP
audit. The claim that missionMATCH supplies it is currently true of the code
and FALSE of the running system.
ALERTING COVERAGE IS PARTIAL, which is why the above survived, recorded as
finding D2. Exactly TWO units on Bubbles carry OnFailure, bubbles-cert-check
and mm-db-backup, both wired during Track H item 3. That work fixed the
alerting PATH and proved it by breaking things; it did not sweep every
existing unit onto that path. A failing unit outside those two is silent. This
is the same shape as the decorative alerting already found once: the mechanism
works, the coverage does not, and partial coverage is harder to notice than a
mechanism that is plainly broken.
Both are production changes and both are QUEUED rather than fixed, because the
Phase 2 lock holds. One deploy closes both.
REMAINING GAPS: no metrics scraper and no dashboard. That is a deliberate
ordering, not an oversight: a phone that buzzes was worth more than a graph
nobody is watching at three in the morning.
--------------------------------------------------------------------------------
16. THE 28 JULY 2026 INCIDENT RECORD
--------------------------------------------------------------------------------
A full account, because the corrections are more instructive than the features.
INCIDENT 1: THE STAGING ROLE COULD NOT AUTHENTICATE AT ALL
FOUND Provisioning a Track U repro account. Invite acceptance returned 500.
CAUSE The isolated role missionmatch_staging_app was created with TABLE
grants mirrored and FUNCTION grants not. It held EXECUTE on ZERO of
the nineteen SECURITY DEFINER auth functions.
IMPACT No login, no invite acceptance, no session validation on staging
from the moment the role was introduced. It also means an earlier
ledger claim that Track U items were "proven on staging" could not
have been an end to end check, because no authenticated request
could complete. That claim was withdrawn.
FIX The nineteen missing grants mirrored into missionmatch_staging only,
generated by comparing the two roles rather than hand listed.
VERIFIED 0 of 188 functions still missing. The staging role is STILL refused
on missionmatch, tenant_alpha, and tenant_beta. Production grants
unchanged at 151 of 152. Production site 200.
INCIDENT 2: THE BY RULE TOOL FIX NEVER REACHED THE BROWSER
CAUSE SessionInfo re read the RAW rows of user_tool_access instead of the
principal's already widened set.
IMPACT A tenant superuser with no grant rows was widened for every server
side check and still received tools:[], which hides the entire
navigation and locks every entitlement checkbox. The reported
symptom surviving its own fix, one layer up.
FIX SessionInfo returns the principal's tools.
INCIDENT 3: PATCH /me/profile ANSWERED 404 ON THE CALLER'S OWN ACCOUNT
CAUSE writeProfile, getProfile, and roleOf ran on a bare pool connection
with no app.current_org_id. users carries forced RLS, so every
statement matched zero rows.
LESSON The explicit "WHERE organization_id = $1" predicate LOOKS like it
does the scoping and does not. RLS filters first.
FIX All three run inside RunInTenantTx. The same class of bug was
introduced in AcceptInvite the same day and fixed the same way.
INCIDENT 4: A PHASE 1 NAVIGATION LINK NEARLY SHIPPED TO PRODUCTION
CAUSE Copying HEAD's AppShell.tsx wholesale into the production build
dragged a /slate/capture link with it.
CAUGHT BY Grepping the BUILT BUNDLE, not the source tree.
FIX That file rebuilt from the baseline with only the Track U change.
INCIDENT 5: THE PRODUCTION TOPOLOGY WAS RECORDED WRONG, AND IT BROKE PRODUCTION
CAUSE The ledger described production as missionmatch on M4 5432. That is
the TENANT data plane. The CONTROL PLANE is missionmatch on
megamind-agi 5433, and it was at v68, twelve migrations behind.
IMPACT The Track U binary selects title and phone from users; those columns
were added by 0079, applied to the tenant database. On the control
plane they did not exist, so GET /api/auth/session WOULD have
returned 500 to the first person to sign in.
SCOPE Stated precisely: the journal shows ZERO 5xx and ZERO ERROR lines
across the entire window between deploy and fix. Nobody logged in
while it was broken. The defect was real and had no victim.
WHY IT WAS MISSED The post deploy zero 5xx sweep used only unauthenticated
paths, which answer 403 before the failing handler runs. A sweep
with no session cannot see a defect behind the session gate.
FIX Control plane snapshotted and migrated v68 to v80.
VERIFIED title and phone present, remap audit trail written on the
authoritative database with all three superuser grants named, the
promoted account's sessions revoked, login returns 401 for a wrong
password rather than 500, zero 5xx across 21 requests.
ROOT CAUSE The migrate-all-tenants swapDB defect (Section 14). FIXED, with
five tests, and proven against the real two host topology: the fixed
tool now targets the tenant's own host and, when credentials did not
match, FAILED LOUDLY instead of quietly succeeding elsewhere.
DRIFT NOW CLOSED. Control plane v80, tenant v80, staging v80, each verified
by querying that database directly rather than trusting the summary.
INCIDENT 6: TWO OF MY OWN FINDINGS WERE WRONG
6a I reported Patricia Cox could not sign in, with no password credential and
no live invite, and built a Resend invitation button on that basis. I had
read the TENANT copy of users, which is non authoritative and was stale.
On the control plane she is ACTIVE with a password and four tool grants;
she has simply never logged in. Production has FIVE accounts, not three.
The button is still correct work (amendment 6 lists resend invitations and
nothing called the endpoint), but the urgency was based on a bad read.
6b I reported MM_TENANT_DSN_TENANT_ACME missing from production and put it in
the risk register as a probable breakage. It is defined on line 23 of
api.env and present in the running process. The claim came from a grep run
through two layers of shell quoting that silently matched nothing.
WITHDRAWN, and the data plane is now PROVEN working instead: connecting
with the exact DSN the running process holds reaches the tenant database
as missionmatch_app, that role is NOBYPASSRLS, a read of candidates with
no org GUC returns 0 rows, and the same read with the GUC set returns 11.
INCIDENT 7: THE LOGIN INSTRUMENT LIED FOR WEEKS, and a design partner caught it
REPORTED She said she had logged in and the control plane showed "never
signed in".
MEASURED She was right, and it was worse than one account. FOUR of five
accounts held real ACTIVE sessions while the screen said none had
ever signed in, and the fifth was STALE BY A WEEK.
CAUSE internal/modules/auth/service.go read
_, _ = db.Exec(ctx, `SELECT auth_touch_last_login(, )`, ...)
The $1 and $2 PLACEHOLDERS WERE MISSING FROM THE SOURCE, almost
certainly stripped by an edit made through a shell that expanded the
dollar signs away. Postgres rejected it as a syntax error on EVERY
sign in, and both return values were discarded, so it failed
silently. This is the no scripts rule demonstrated in production.
RULED OUT by direct test, not by reading: the function works when called as a
superuser, the app role holds EXECUTE, and RLS is not the cause
because the function is SECURITY DEFINER owned by a BYPASSRLS role.
ONE OF MY OWN TESTS WAS INVALID and is recorded as such: subselects
run as the app role returned NULL under RLS, so the function's NULL
guard returned early and it looked like a failure that was not one.
FIX Placeholders restored, and the failure is now LOGGED at ERROR
including the zero rows case, which Postgres does not treat as an
error and which is exactly what a silent no op looks like. It still
does not fail the login, because recording a sign in is bookkeeping
and denying entry over bookkeeping would turn a reporting defect into
an outage. Swallowed deliberately is different from swallowed