Files
Chris TonjesandClaude Opus 5.5 e3c4cb0cdc
ci / test (push) Has been cancelled
Phase 0 milestone 0.9: docs + acceptance
- README rewritten: Docker and native quickstarts, try-it flow, gate
  commands, promote-a-stub steps.
- CONTRIBUTING rules 9-13 (pii_read on PII reads, __audit_redact__ for
  encrypted columns, retention policy per table, models.py registry,
  vendored-code change discipline); PR checklist updated.
- docs/nist-mapping.md: per-control file:line evidence and remaining gap.
- NIST_800_53_CONTROL_MAPPING.md v0.3: Phase 0 notes on 28 rows
  (12 implemented, 12 partial, 4 progress notes on hybrid/tailored rows)
  plus a status-shift table.
- PHASE0_PLAN: 0.9 complete; Definition of Done 12/14 checked with how
  each was verified. Open: first green Gitea CI run, Docker/Postgres boot.
- POA&M and status handoff updated; status TL;DR no longer claims an
  unverified Docker boot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:37:40 -04:00

123 lines
5.9 KiB
Markdown

# YPMS — Youth Program Management System
DC Department of Employment Services / Office of Youth Programs — in-house build replacing seven legacy tools (MBSYEP intake, TMS timekeeping, PF3 admin, manual payroll scripts, MicroStrategy, and two supporting apps) with a single Python / FastAPI / Postgres system on Azure Government.
**Status:** Phase 0 (foundations) — framework, auth, RBAC, audit, Registrant vertical slice, 501 stubs for every other entity, retention scaffold, CI. See [`PHASE0_STATUS.md`](PHASE0_STATUS.md).
**System owner + ISSO + AO:** Chris Tonjes, DOES CIO.
**Full context:** [`OVERVIEW.md`](OVERVIEW.md), [`ARCHITECTURE.md`](ARCHITECTURE.md), [`PHASE0_PLAN.md`](PHASE0_PLAN.md).
## Quickstart — Docker (canonical)
Requires Docker Desktop (or equivalent). Ports 8000 (app), 5433 (Postgres), 8081 (Adminer) must be free.
```bash
cp .env.example .env
docker compose up --build
```
Compose runs `alembic upgrade head`, seeds roles + permissions + the bootstrap admin (`chris` / `change-me-immediately`, or `SEED_ADMIN_PASSWORD`), then starts the app.
- App: http://localhost:8000 — OpenAPI at http://localhost:8000/docs
- Adminer: http://localhost:8081 — System PostgreSQL, server `db`, user/password/db `ypms`
- Stop with `Ctrl+C`; wipe the local DB with `docker compose down -v`.
## Quickstart — native, no Docker
Uses SQLite in place of Postgres. Fine for poking at the API; Postgres-specific behaviour needs the Docker path.
```bash
python3.12 -m venv .venv && source .venv/bin/activate
pip install -e '.[dev]'
export DATABASE_URL=sqlite:///./ypms-dev.db
export JWT_SECRET=dev-jwt-secret-change-me-32-bytes-min # >= 32 bytes or the app won't start
export COLUMN_ENCRYPTION_KEY=dev-col-enc-key-change-me-32bytes-______
export SEED_ADMIN_PASSWORD=pick-a-local-password
alembic upgrade head
python -m ypms.seed.run
uvicorn ypms.main:app --reload
```
## Try it
```bash
curl http://localhost:8000/healthz # {"status":"ok"}
curl http://localhost:8000/readyz # {"status":"ok"} once the DB is reachable
TOKEN=$(curl -s -X POST http://localhost:8000/auth/login \
-d 'username=chris&password=change-me-immediately' | python3 -c 'import json,sys;print(json.load(sys.stdin)["access_token"])')
curl -s -X POST http://localhost:8000/registrants \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"first_name":"Test","last_name":"Youth","dob":"2010-05-01","ssn":"123-45-6789"}'
# → ssn_masked "XXX-XX-6789"; the SSN itself is never returned
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8000/guardians -H "Authorization: Bearer $TOKEN"
# → 501 (stub: RBAC checked and audited, no logic yet)
```
Change the bootstrap password right away: `POST /auth/change-password`.
The retention job is report-only in Phase 0:
```bash
python -m ypms.framework.retention # logs "0 records to purge" and writes an audit row
```
## Tests and gates
```bash
pytest # SQLite in-memory, ~10s
pytest --cov=src/ypms --cov-report=term-missing --cov-fail-under=80
ruff check src tests
mypy src
bandit -q -r src -c pyproject.toml
pip-audit --strict . # runtime dependencies
```
CI runs all of these plus gitleaks and a CycloneDX SBOM on Gitea Actions — [`.gitea/workflows/ci.yml`](.gitea/workflows/ci.yml).
## Repo shape
Full tree in [`ARCHITECTURE.md`](ARCHITECTURE.md) §5.1. High points:
- `src/ypms/main.py` — app factory; refuses to start if any table lacks a retention policy; opaque 500 handler
- `src/ypms/settings.py` — pydantic-settings config (secret-length floors enforced)
- `src/ypms/framework/` — vendored from OWC, plus YPMS additions: `pii_audit.py`, `stub_router.py`, `retention.py`
- `src/ypms/security/` — RBAC, `column_crypto.py` (AES-256-GCM app-layer encryption)
- `src/ypms/auth/` — login, JWT (PyJWT), refresh, change-password, login rate limit
- `src/ypms/youth/` — Registrant, the real vertical-slice entity
- `src/ypms/stubs.py`, `src/ypms/stub_models.py` — the 26 entities still at 501
- `src/ypms/retention_classes.py` — PIA §9 retention policy catalog
- `src/ypms/models.py` — the one list of model modules
- `src/ypms/seed/` — roles, permissions matrix, bootstrap admin
- `migrations/` — Alembic; merged revisions pinned in `IMMUTABLE.sha256`
- `docs/coverage/` — per-entity requirement traces; `docs/nist-mapping.md` — control evidence index
## Adding an entity (promoting a stub to real)
1. Move the class from `src/ypms/stub_models.py` into `src/ypms/<domain>/models.py`; keep `__tablename__` and `retention_policy`. Add the module to `src/ypms/models.py`.
2. Add columns in a **new** migration; append its hash to `migrations/IMMUTABLE.sha256`.
3. Encrypted columns: write through `security.column_crypto`, list them in `__audit_redact__`.
4. Schemas in `<domain>/schemas.py`; the read schema masks anything sensitive.
5. Router: `framework.router.make_router` for plain CRUD, or a hand-built router on `CRUDService` (see `youth/router.py`) when you need PII-read audit or row scoping. Register it in `main.py` and remove the entity from `src/ypms/stubs.py`.
6. Check the permissions matrix rows for the entity — they're policy; review them as such.
7. Tests, then a coverage doc at `docs/coverage/<entity>.md` (see `registrant.md`).
## Contributing
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the hard rules.
## Compliance
- [`SYSTEM_SECURITY_PLAN.md`](SYSTEM_SECURITY_PLAN.md) — NIST 800-53 Moderate SSP
- [`NIST_800_53_CONTROL_MAPPING.md`](NIST_800_53_CONTROL_MAPPING.md) v0.3 — full control map with Phase 0 evidence
- [`docs/nist-mapping.md`](docs/nist-mapping.md) — Phase 0 control evidence index (file:line)
- [`PRIVACY_IMPACT_ASSESSMENT.md`](PRIVACY_IMPACT_ASSESSMENT.md) — PIA covering minors, SSN, banking, FERPA, CFSA
- [`POAM.md`](POAM.md) — plan of action and milestones
## License / usage
Internal DOES system. Not for external distribution.