feat(sp24): align CLAUDE.md + REQUIREMENTS.md + ARCHITECTURE.md with auth work + AUTH_DISABLED WARNING

Replaces every stale 'no auth' / 'no authentication' / 'no second party to
authenticate' claim in the three top-level docs with the v1 posture: the
auth boundary is HTTP (bcrypt + HttpOnly session cookie; first admin
bootstrapped from CYCLONE_ADMIN_USERNAME + CYCLONE_ADMIN_PASSWORD env
vars); the file-system posture (SQLCipher at rest, macOS Keychain) is
unchanged; the threat model is still a stolen/imaged drive.

Closes requirements §11 R-1 (was Open; now Closed by SP24 — auth shipped
via the origin/main merge on 2026-06-23, SP24 reconciled the docs).

Also:
- backend/src/cyclone/__main__.py — emit WARNING when AUTH_DISABLED is
  True at boot so a misconfigured production deploy fails loudly
- §6.1 migrations table in ARCHITECTURE.md — list 0013 (auth_users_and_
  sessions) and 0014 (audit_log_user_id) with the renumbering note
- §4.2 module map — add the cyclone.auth.* package
- Three skills addenda: cyclone-tests (AUTH_DISABLED conftest bypass is
  mandatory context), cyclone-api-router (every router needs
  Depends(matrix_gate)), cyclone-spec (spec template threat-model is now
  auth-aware)

This also tracks CLAUDE.md in git for the first time (was previously
untracked; the SP24 doc updates are in scope for the increment).

Spec: docs/superpowers/specs/2026-06-23-cyclone-auth-posture-alignment-design.md
Plan: docs/superpowers/plans/2026-06-23-cyclone-auth-posture-alignment.md
This commit is contained in:
Tyler
2026-06-23 14:47:16 -06:00
parent d294b8bbed
commit 00f11f84b6
8 changed files with 233 additions and 17 deletions
+10 -7
View File
@@ -13,7 +13,7 @@
## 1. System overview
Cyclone is a self-hosted X12 EDI claims-management suite for a single billing office. The first deployment target is Colorado Medicaid (one operator, one trading partner). The system is local-only on purpose: the backend binds to `127.0.0.1` and has no authentication because the threat model is a stolen or imaged drive, not a remote attacker.
Cyclone is a self-hosted X12 EDI claims-management suite for a single billing office. The first deployment target is Colorado Medicaid (one operator, one trading partner). The system is local-only on purpose: the backend binds to `127.0.0.1` and requires login (bcrypt + HttpOnly session cookie). The threat model is still a stolen or imaged drive, not a remote attacker — SQLCipher at rest and the macOS Keychain handle that. The auth boundary is the HTTP layer; the file-system posture is unchanged. The SP23 product fork changes the threat model to "remote operator on LAN."
Cyclone processes three X12 transaction pairs end-to-end:
@@ -199,8 +199,9 @@ backend/src/cyclone/
| Module | Responsibility |
|---|---|
| `__main__` | Dispatch `serve` vs CLI; honor `CYCLONE_PORT` / `CYCLONE_RELOAD` |
| `__main__` | Dispatch `serve` vs CLI; honor `CYCLONE_PORT` / `CYCLONE_RELOAD`; emit `AUTH_DISABLED` WARNING if `cyclone.auth.deps.AUTH_DISABLED` is True at boot |
| `api` | FastAPI app, lifespan, route includes, 999/270/271 endpoints, exception → HTTP |
| `auth.*` | HTTP-layer login (bcrypt + HttpOnly session cookie). Submodules: `deps` (the `matrix_gate` FastAPI dependency, `AUTH_DISABLED` flag), `routes` (`/api/auth/login`, `/api/auth/logout`, `/api/auth/me`), `users`, `sessions`, `permissions` (role matrix), `bootstrap` (env-var first-admin), `cli` (`cyclone admin create-user` / `rotate-password`) |
| `api_helpers` | Error → status code mapping, common response shapes |
| `audit_log` | Append + verify a SHA-256 hash-chained log (SP11) |
| `backup` | Pure crypto + file I/O for SP17 backup format |
@@ -338,9 +339,9 @@ The client uses `fetch` + `ReadableStream` to consume the body and dispatches ea
## 6. Data model
### 6.1 Migrations 00010012
### 6.1 Migrations 00010014
The schema evolves in 12 ordered SQL files under `backend/src/cyclone/migrations/`. Each file starts with `-- version: N` and is applied in order by `db_migrate.py`. There is no migration framework — `db_migrate.py` is a 30-line file that checks the `schema_version` row and applies everything newer.
The schema evolves in ordered SQL files under `backend/src/cyclone/migrations/`. Each file starts with `-- version: N` and is applied in order by `db_migrate.py`. There is no migration framework — `db_migrate.py` is a 30-line file that checks the `schema_version` row and applies everything newer. The first 12 ship on `main`; 13 + 14 are the auth migrations that landed in `main` on 2026-06-23.
| # | File | What it adds |
|---|---|---|
@@ -356,8 +357,10 @@ The schema evolves in 12 ordered SQL files under `backend/src/cyclone/migrations
| 10 | `0010_payer_rejected_acknowledged.sql` | `claim.payer_rejected_acknowledged_*` columns (SP14 hand-off) |
| 11 | `0011_processed_inbound_files.sql` | `processed_inbound_files` idempotency table (SP16) |
| 12 | `0012_backups.sql` | `db_backups` table (SP17) |
| 13 | `0013_auth_users_and_sessions.sql` | `users` + `sessions` tables, `users.password_hash` (bcrypt) — landed 2026-06-23 |
| 14 | `0014_audit_log_user_id.sql` | `audit_log.user_id` FK to `users.id` (forward reference to 0013) — landed 2026-06-23 |
A 13th (`0013_claims_unique_constraint.sql`) and 14th (`0014_409_ux.sql`) are in-flight on the `claims-unique-fix` worktree but not yet on `main`.
The SP22 parse-then-decide migrations (drop claims UNIQUE constraint + relax PK to `(batch_id, id)`) are renumbered to 0015 + 0016 on the `claims-unique-fix` worktree so the auth migrations stay at the front of the chain; they'll keep those numbers when SP22 lands.
### 6.2 ERD (high level)
@@ -614,7 +617,7 @@ Every API error is a JSON object with `{"error": "<code>", "message": "<human>",
| `ValidationError` (R-codes) | 422 | `validation_failed` (issues in `detail`) |
| `FileNotFoundError` | 404 | `not_found` |
| `ValueError` | 400 | `bad_request` |
| `PermissionError` | 403 | `forbidden` (reserved — no auth today) |
| `PermissionError` | 403 | `forbidden` (raised by `matrix_gate` when the role is below the endpoint's required role) |
| (anything else) | 500 | `internal` (with a request id for log correlation) |
The 409-vs-422-vs-400 split is intentional: 409 for state-machine violations (the operator's input is well-formed but conflicts with current state), 422 for parse / validation, 400 for bad input shape.
@@ -700,7 +703,7 @@ For a single operator on a single machine, the dev deployment IS the production
The following are explicitly NOT built in v1, by design:
- **Multi-user / multi-host** — no auth, no LAN-bind, no Docker, no reverse proxy. The operator is one person, the host is one machine. (SP23 fork if this changes.)
- **Multi-user / multi-host** — login is required (single-user model today; SP23 adds RBAC). LAN-bind, Docker, reverse proxy still out of scope. (SP23 covers the LAN-bind / Docker / RBAC product fork.)
- **Other trading partners** — only Colorado Medicaid via Gainwell is configured. The `PayerConfig` mechanism (SP9) supports adding more; the spec/plan/CLI work for that is on the backlog.
- **837I / 837D / 834 / 820 / 275 / 278 / 276-277** — only 837P and 835 are first-class. Other transaction types are listed in [REQUIREMENTS.md §6.2](REQUIREMENTS.md) as backlog items.
- **NPPES real-time lookup** — only local NPI checksum validation (SP20). The operator who wants real registry lookup has to wire it in later.