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:
+10
-7
@@ -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 0001–0012
|
||||
### 6.1 Migrations 0001–0014
|
||||
|
||||
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.
|
||||
|
||||
@@ -33,10 +33,10 @@ Every requirement (§3, §4) has an ID; every ID is traceable to a sub-project (
|
||||
|
||||
A self-hosted EDI claims-management suite for a single billing office. Parses 837P professional claims and 835 ERA remittances (X12 005010X222A1 and 005010X221A1), with a local-only FastAPI backend and a React UI for browsing, filtering, and inspecting the parsed data.
|
||||
|
||||
- **Local-only on purpose:** binds `127.0.0.1`, no auth, no internet exposure, single operator, single machine, one trading partner (Colorado Medicaid, currently).
|
||||
- **Local-only on purpose:** binds `127.0.0.1`, requires login (auth boundary is HTTP; bcrypt + HttpOnly session cookie), no internet exposure, single operator, single machine, one trading partner (Colorado Medicaid, currently).
|
||||
- **Single-process:** the backend is one Python process; the frontend is one Vite-served React app; there is no message broker, no separate worker, no queue.
|
||||
- **Single source of truth:** SQLite at `~/.local/share/cyclone/cyclone.db` (overridable via `CYCLONE_DB_URL`); optionally encrypted at rest via SQLCipher (SP12).
|
||||
- **Threat model:** a process on the same host. No second party to authenticate; no second host to harden against.
|
||||
- **Threat model:** a process on the same host, plus a deliberate HTTP-layer login (no remote unauthenticated read or write). File-system threats (stolen/imaged drive) are still the primary concern; SQLCipher at rest + the macOS Keychain handle those. The SP23 product fork changes the threat model to "remote operator on LAN."
|
||||
|
||||
The Cyclone codebase has honored this contract end-to-end across 22 shipped sub-projects. Every design spec re-asserts the local-only / single-operator / single-payer / no-auth posture and lists (often at length) what is intentionally out of scope.
|
||||
|
||||
@@ -151,7 +151,7 @@ Each requirement is `FR-NN`, traceable to one or more sub-projects. Verification
|
||||
|
||||
| ID | NFR | Source / SP |
|
||||
|---|---|---|
|
||||
| NFR-1 | **Local-only bind.** Backend binds `127.0.0.1:8000` (overridable via `CYCLONE_PORT`); CORS allowlist is exact (`http://localhost:5173`); no auth, no API key. | SP1 |
|
||||
| NFR-1 | **Local-only bind + auth.** Backend binds `127.0.0.1:8000` (overridable via `CYCLONE_PORT`); CORS allowlist is exact (`http://localhost:5173`); login required (bcrypt + HttpOnly session cookie; first admin bootstrapped from env vars `CYCLONE_ADMIN_USERNAME` + `CYCLONE_ADMIN_PASSWORD`); dev/test escape hatch via `CYCLONE_AUTH_DISABLED=1` (logs WARNING at boot). | SP1 + auth (2026-06-23 merge) + SP24 (doc reconciliation) |
|
||||
| NFR-2 | **Determinism.** Parser / serializer round-trip is guaranteed on 113 real prodfiles (`docs/prodfiles/claims/*.x12`); canonical fields, not byte-identity (called out in the SP8 spec). | SP8 |
|
||||
| NFR-3 | **Audit completeness.** Every reconciliation anomaly, every state transition, every 999/277CA reject writes an `ActivityEvent` (in `activity_events` table, un-chained; powers the `/activity` feed and inbox state transitions). | SP2 + SP10 |
|
||||
| NFR-4 | **Tamper-evidence.** A separate `audit_log` table (SP11) carries SHA-256 hash-chained rows for security-sensitive events (login attempts, rejections, key rotations, backup lifecycle, rejected requests). `GET /api/admin/audit-log/verify` detects any break. **The `activity_events` (NFR-3) and `audit_log` (NFR-4) tables are distinct** — different semantics, different audiences. New code that needs an audit row must decide which one to write to. | SP11 |
|
||||
@@ -406,6 +406,7 @@ Each row's "Spec" / "Plan" point to the design spec and implementation plan on d
|
||||
| SP20 | NPI checksum + Tax ID format validation | [spec](superpowers/specs/2026-06-21-cyclone-npi-validation-design.md) | (no plan file on disk; feat(sp20) commits on `main`) | Met |
|
||||
| SP21 | CycloneStore split (in-flight) + Universal drilldown (shipped) | [split spec](superpowers/specs/2026-06-21-cyclone-store-split-design.md), [drilldown spec](superpowers/specs/2026-06-21-cyclone-universal-drilldown-design.md) | [store-split plan](superpowers/plans/2026-06-21-cyclone-store-split.md), [drilldown plan](superpowers/plans/2026-06-21-cyclone-universal-drilldown.md) | Drilldown **Met**; split **In-flight** (`refactor/store-split` branch, not yet on `main`) |
|
||||
| SP22 | Parse-decide workflow + Pipeline agent | [parse-decide spec](superpowers/specs/2026-06-21-cyclone-parse-decide-workflow-design.md), [pipeline spec](superpowers/specs/2026-06-21-cyclone-pipeline-agent-design.md) | [parse-decide plan](superpowers/plans/2026-06-21-cyclone-parse-decide-workflow.md), [pipeline plan](superpowers/plans/2026-06-21-cyclone-pipeline-agent.md) | Met (parse-decide on `main`; pipeline agent lives in `cyclone-pipeline/`) |
|
||||
| SP24 | Auth posture alignment (docs-only reconciliation with the auth work in `main`) | [spec](superpowers/specs/2026-06-23-cyclone-auth-posture-alignment-design.md) | [plan](superpowers/plans/2026-06-23-cyclone-auth-posture-alignment.md) | Met (no code paths change; only docs + `__main__.py` WARNING + 3 skills addenda) |
|
||||
|
||||
### 6.2 Backlog (specs awaiting plans, or vice versa)
|
||||
|
||||
@@ -625,7 +626,7 @@ Pulled forward from [`docs/reviews/2026-06-20-cyclone-completeness-review.md`](r
|
||||
|
||||
| ID | Risk / gap | Source | Status |
|
||||
|---|---|---|---|
|
||||
| R-1 | No app-layer auth (anyone on the host can read PHI). | Completeness review §3.1.4 + §3.2.25 | **Open** — addressed only if/when SP23 ships. |
|
||||
| R-1 | No app-layer auth (anyone on the host can read PHI). | Completeness review §3.1.4 + §3.2.25 | **Closed by SP24** — auth shipped via the merge of `origin/main` on 2026-06-23 (commits `a25504b`..`39ae988`, `cyclone.auth.*` + `matrix_gate` dependency on every router); SP24 reconciled the docs to match. SP23 still covers the LAN-bind / Docker / RBAC product fork. |
|
||||
| R-2 | SQLite plaintext if SQLCipher not enabled. | Completeness review §3.1.1 | **Partial** — SP12 + SP15 ship the encryption option, but it falls back to plain if the Keychain entry is missing. |
|
||||
| R-3 | No Dockerfile / docker-compose.yml. | Completeness review §3.2.29 | **Open** — SP23 spec covers it but is awaiting user decision. |
|
||||
| R-4 | PayerConfig still partly in code (`co_medicaid()` factory at `cyclone/parsers/payer.py:57-58`). | Completeness review §3.1.9 | **Open** — SP9 externalized config but the factory remains as a fallback for tests and default-payer selection. |
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Cyclone Auth Posture Alignment — Design
|
||||
|
||||
**Date:** 2026-06-23
|
||||
**Status:** Draft, awaiting user sign-off
|
||||
**Branch:** `main` (auth code already landed; this SP aligns docs + decisions)
|
||||
**Status:** Approved (in implementation on `sp24-doc-posture-alignment`, 2026-06-23)
|
||||
**Branch:** `sp24-doc-posture-alignment` (off `css-reduction` — `docs/REQUIREMENTS.md` and `docs/ARCHITECTURE.md` were added on `css-reduction` and do not yet exist on `main`)
|
||||
**Depends on:** The auth work that landed in main via the merge of `origin/main` on 2026-06-23 (commits `a25504b`..`39ae988`, 14 commits, the `feat(auth):`/`fix(auth):`/etc. series on `auth-rebased`).
|
||||
**Replaces:** The "no auth, no LAN-bind, single operator" claim in `CLAUDE.md`, `docs/REQUIREMENTS.md`, and `docs/ARCHITECTURE.md` — the codebase no longer matches the doc and the divergence needs to be resolved one way or the other.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user