A local, multi-user web dashboard that ingests the
FedRAMP Machine-Readable
(FRMR.documentation.json) source of truth and lets your team track
implementation status against every FedRAMP 20x requirement and Key
Security Indicator (KSI).
It is not a fork of the FedRAMP docs — it sits next to a clone of the upstream repo and re-ingests on demand, preserving the status, owner, notes, and evidence you've entered.
- Dashboard — % done, status counts, "next 10 to tackle" (MUSTs first)
- Gap analysis — every not-started item grouped by Process / KSI Domain
- Requirements browser — filter by process, applicability (20x / Rev5 / both), actor label, status, owner, free-text
- KSI browser — filter by domain, status, free-text
- Item detail — full statement (with FRD term tooltips), RFC 2119 keyword, following-information list, examples, varies-by-level handling, plus your editable status / owner / notes / evidence / last-reviewed fields
- NIST 800-53 crosswalk — for each control referenced by a KSI, the indicators that satisfy it and their current status — useful when mapping 20x against an existing Rev5 / NIST baseline
- Definitions (FRD) browser
- CSV / JSON export — your full tracker state for spreadsheets or external GRC ingest
- Multi-user with sessions, per-item audit log, role-based admin gating of new sign-ups
- Node 24+
- Hono + better-sqlite3 (server)
- React 18 + Vite + TanStack Query + React Router (client)
- TypeScript end-to-end, no Bun required
- SQLite file at
data/tracker.db(gitignored)
- Node 24 or newer
- A local clone of
FedRAMP/docs— by default expected at../docs/relative to this tracker. Override viaFRMR_PATH.
# from /Users/kenith.philip/FedRAMP 20x
git clone https://github.com/FedRAMP/docs.git # if you haven't already
cd trackernpm installnpm run ingestThis reads ../docs/FRMR.documentation.json, creates data/tracker.db if
needed, and (re)populates the FRMR-derived tables. Your item_state,
audit_log, users, and sessions tables are not touched, so re-ingest
when upstream publishes a new FRMR version and your team's progress is
preserved (state is keyed by stable FRMR IDs).
To point at a different FRMR file:
FRMR_PATH=/path/to/FRMR.documentation.json npm run ingestTwo processes, hot-reload on both sides:
npm run devThis starts:
- API at
http://localhost:4000 - Vite dev server (with API proxy) at
http://localhost:5173
Open http://localhost:5173. The first time you visit, the app detects no
users exist and prompts you to create the first admin account. After
that, only admins can add additional users (from the API; a future UI
can wrap this if you want).
npm run build # bundles client/ into client/dist/
npm run start # serves API + bundled client on PORT (default 4000)Visit http://localhost:4000.
- Find a starting point — Dashboard or Gap analysis page. The
next_uplist on the dashboard surfaces MUST-keyword 20x requirements that haven't been started yet. - Open an item — click its ID to land on the detail page. The
statement is annotated with FRD term tooltips. Fill in:
- Status —
not_started/in_progress/met/not_applicable/blocked - Owner — pick a user (for accountability), and/or a free-text owner (for teams)
- Evidence URL — link to the artifact that proves implementation (runbook, policy doc, security-tools dashboard, …)
- Last reviewed — when an item was last validated against current state
- Notes — implementation decisions, blockers, links to design docs
- Status —
- Trace coverage — the NIST crosswalk page tells you which legacy controls each KSI maps to. Useful for telling auditors "we already have evidence for ac-2.2 via KSI-IAM-AAM" — and for finding controls you aren't covering via 20x and need to address separately.
- Report out — Export page produces CSV (good for spreadsheets) or JSON (good for piping into other GRC tooling).
cd ../docs && git pull && cd -
npm run ingestYour tracker state survives. New requirements or KSIs appear as
not_started. If upstream removes an item, the row in requirements /
indicators is removed and any state you had for that ID becomes
orphaned (still in item_state — surface via SQL if you need to audit).
Today, additional users are added via API (admin session required):
curl -X POST -H 'content-type: application/json' \
-b cookies.txt \
-d '{"email":"alice@example.com","name":"Alice","password":"strongpw1"}' \
http://localhost:4000/api/auth/signup(Get an admin session cookie via /api/auth/login first, or use the
browser session: open DevTools → Application → Cookies → copy fr20x_sid.)
data/tracker.db is the entire state. For a hot, scriptable backup use
server/backup.ts (online .backup() → gzip, WAL checkpointed first):
npm run backup # writes backups/tracker-<UTC>.db.gzRestore stops at nothing-destructive-by-default: it validates the SQLite magic
header before overwriting, writes atomically (temp + rename), refuses symlink
targets, and clears stale -wal/-shm sidecars. Stop the server first, then
restore from a *.db.gz.
| Variable | Default | Purpose |
|---|---|---|
PORT |
4000 |
HTTP port. |
DB_PATH |
data/tracker.db |
SQLite path. Open failures produce an actionable error (missing dir / not writable). |
TRACKER_DB_BUSY_TIMEOUT_MS |
5000 |
SQLite busy_timeout — concurrent writers retry internally before SQLITE_BUSY. |
TRACKER_MAX_ATTACHMENT_MB |
25 |
Per-file attachment cap (validated at startup). |
TRACKER_ATTACHMENT_MIME_ALLOWLIST |
common doc/image types | Comma-separated upload MIME allowlist. |
TRACKER_ATTACHMENTS_DIR |
data/attachments |
Content-addressed blob store root. |
RL_LOGIN_PER_MIN / RL_LOGIN_PER_HOUR |
5 / 30 |
Login rate limits (per client IP; falls back to the TCP peer address with no proxy). |
RL_TOKEN_CREATE_PER_HOUR |
10 |
API-token creation rate limit. |
RL_API_TOKEN_PER_MIN |
60 |
Per-API-token request rate limit. |
NODE_ENV |
— | Set to production to mark cookies Secure. |
tracker/
server/
index.ts # Hono app entry
db.ts # SQLite open + schema init
schema.sql # DDL
auth.ts # scrypt password hashing + session helpers + middleware
ingest.ts # FRMR JSON → SQLite (idempotent)
routes/
auth.ts # /api/auth/*
items.ts # /api/processes, /requirements, /indicators, /items/:type/:id (PATCH), /definitions, /users, /meta
dashboard.ts # /api/dashboard, /api/crosswalk
export.ts # /api/export?format=csv|json
client/
index.html
vite.config.ts
src/
main.tsx, App.tsx, styles.css
lib/api.ts, lib/auth.tsx, lib/formatting.tsx
pages/Dashboard.tsx, GapAnalysis.tsx, Requirements.tsx, Indicators.tsx,
ItemDetail.tsx, Crosswalk.tsx, Definitions.tsx, Export.tsx, Login.tsx
data/ # tracker.db lives here (gitignored)
.env.example
package.json, tsconfig.json
- Passwords are stored with
scrypt(N=16384, r=8, p=1)and a per-user salt. - Session tokens are 256-bit random, hashed before storage; cookies are
HttpOnly+SameSite=Strict+Securein production. - CSRF:
SameSite=Strictblocks cross-origin browser-initiated requests. If you put this behind a tunnel or expose it on the network, add a reverse-proxy with TLS and consider explicit CSRF tokens. - Every state mutation is recorded in
audit_logwith the acting user, field, old value, new value, and timestamp.
The tracker has grown well past the original v0.1 scope. Now included:
- Evidence file uploads — per-item attachments stored in a content-addressed
blob store (
TRACKER_ATTACHMENTS_DIR), with a MIME allowlist and size cap. - TOTP 2FA (RFC 6238) and granular RBAC (roles + per-domain assignments) with an admin UI.
- Audit-log search UI with filters + CSV export.
- Online backup/restore (
npm run backup/restore). - Collector-runs view surfacing impact level + the NIST 800-53 benchmark headline.
- Risk-acceptance workflow (LOOP-B.B3) — signed, audited deviation / risk-adjustment
decisions (NIST CA-5 / RA-7 / FedRAMP Deviation Request). Every acceptance is signed
with a resident Ed25519 key (
server/risk-acceptance-sign.ts, key registry in thesigning_keystable); Authorizing-Official approval writes a second signature; an hourly enforcer expires acceptances past theirexpiration_date. The cloud-evidence POA&M emitter pulls approved, unexpired acceptances (verifying each signature) and flips matching risks to OSCALdeviation-approved. Three separation-of-duties roles back it (see below).
viewer < contributor < ksi-owner < auditor < admin, plus three
LOOP-B.B3 risk-acceptance roles:
iso(Information System Owner) — creates and revokes risk acceptances.ao(Authorizing Official) — approves risk acceptances (and may revoke). Anisocannot self-approve; approval authority is deliberately separate.assessor(3PAO) — read-only access to risk acceptances.
admin retains all permissions. Assign roles in the Users & roles admin UI.
- Multi-tenant / per-org partitioning (one tracker, one team)
- PDF reports — use the CSV/JSON export and your reporting tool of choice
- Self-service signup beyond the first admin — additional users are admin-created