diff --git a/README.md b/README.md index 79ec516..ad19ceb 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,17 @@ 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. +and 005010X221A1), with a FastAPI backend and a React UI for browsing, +filtering, and inspecting the parsed data. -Local-only on purpose: binds to `127.0.0.1`, no auth, no internet exposure. -Built for one operator, one machine, one trading partner (Colorado -Medicaid, currently). +LAN-only by design: always binds to `0.0.0.0:8000` and requires login +(bcrypt + HttpOnly session cookie; first admin bootstrapped from +`CYCLONE_ADMIN_USERNAME` + `CYCLONE_ADMIN_PASSWORD`). Reachability is +controlled by the host firewall / compose port publishing, not by the +bind address. Built for one operator, one machine, one trading partner +(Colorado Medicaid, currently). Do not expose the published ports to +the public internet. See [SP23](docs/superpowers/specs/2026-06-22-cyclone-ubuntu-docker-deployment-design.md) +for the LAN-bind / Docker / RBAC product fork that shipped 2026-06-23. ## Install @@ -184,13 +189,15 @@ happening — clients flip to `stalled` after 30s of total silence ### Endpoints -| Method | Path | Subscribes to | Default sort | -| ------ | -------------------------- | ------------------- | ------------------- | -| GET | `/api/claims/stream` | `claim_written` | `-submission_date` | -| GET | `/api/remittances/stream` | `remittance_written`| `-received_date` | -| GET | `/api/activity/stream` | `activity_recorded` | `-timestamp` (limit 50) | +| Method | Path | Subscribes to | Default sort | +| ------ | -------------------------- | -------------------- | ------------------- | +| GET | `/api/claims/stream` | `claim_written` | `-submission_date` | +| GET | `/api/remittances/stream` | `remittance_written` | `-received_date` | +| GET | `/api/activity/stream` | `activity_recorded` | `-timestamp` (limit 50) | +| GET | `/api/acks/stream` | `ack_received` | `-received_date` | +| GET | `/api/ta1-acks/stream` | `ta1_ack_received` | `-received_date` | -All three accept the same query params as their non-streaming +All five accept the same query params as their non-streaming counterparts (`status`, `payer`, `date_from`, …) so a frontend can swap a one-shot fetch for a tail with no URL surgery. Responses are `Content-Type: application/x-ndjson`. @@ -743,12 +750,11 @@ backup API). . ├── backend/ │ ├── src/cyclone/ -│ │ ├── api.py # FastAPI app, GET + parse routes, /api/{resource}/stream +│ │ ├── api.py # FastAPI app shell (~377 LOC post-SP36; routes live in api_routers/) │ │ ├── api_helpers.py # NDJSON / content-negotiation / live-tail helpers │ │ ├── pubsub.py # in-process EventBus (drop-oldest, per-kind fan-out) -│ │ ├── store.py # CycloneStore, mappers, publish-on-write │ │ ├── db.py # SQLAlchemy engine, session factory, ORM models -│ │ ├── db_migrate.py # PRAGMA user_version migration runner +│ │ ├── db_migrate.py # PRAGMA user_version migration runner (23 migrations) │ │ ├── db_crypto.py # optional SQLCipher encryption at rest (SP12) │ │ ├── audit_log.py # tamper-evident hash-chained audit_log (SP11) │ │ ├── inbox_lanes.py # rejected / payer_rejected / candidates / unmatched / done_today @@ -756,37 +762,45 @@ backup API). │ │ ├── inbox_state_277ca.py # 277CA STC A4/A6/A7 → payer_rejected stamp (SP10) │ │ ├── providers.py # multi-NPI provider lookups (SP9) │ │ ├── payers.py # payer / payer_config lookups (SP9) -│ │ ├── secrets.py # macOS Keychain-backed secret fetcher -│ │ ├── reconcile.py # pure-function 835→claim match + line-level match -│ │ ├── __main__.py # `python -m cyclone serve` -│ │ ├── cli.py # click CLI -│ │ └── parsers/ # X12 tokenizer, models, validator, writers, 277CA, 999, TA1, 270, 271 -│ └── tests/ -│ ├── fixtures/ # co_medicaid_*.txt, minimal_*.txt -│ ├── test_api.py # parse-837/835 round-trip -│ ├── test_api_gets.py # 6 GET endpoints -│ ├── test_store.py # store + mappers + iterators -│ ├── test_api_streaming.py -│ ├── test_api_stream_live.py # 3 live-tail endpoints + disconnect cleanup -│ ├── test_pubsub.py # EventBus + subscribe/unsubscribe -│ ├── test_api_parse_persists.py -│ ├── test_db.py / test_db_crypto.py / test_db_migrate.py -│ ├── test_audit_log.py -│ ├── test_inbox_lanes.py / test_inbox_state.py -│ ├── test_apply_277ca_rejections.py -│ ├── test_sftp_stub.py / test_sftp_paramiko.py -│ └── test_providers_seed.py / test_payer_config_loading.py -├── src/ # React + Vite + TypeScript UI +│ │ ├── secrets.py # macOS Keychain-backed secret fetcher (4-tier: _FILE > env > Keychain > None) +│ │ ├── reconcile.py # pure-function 835→claim match + line-level match (SP31 pcn-exact) +│ │ ├── scheduler.py # inbound MFT polling scheduler (SP16, SP27 per-op timeout) +│ │ ├── edifabric.py # Edifabric /v2/x12/{read,validate} HTTP client (SP40) +│ │ ├── backup.py / backup_service.py / backup_scheduler.py # SP17 encrypted backups +│ │ ├── __main__.py # `python -m cyclone serve` (auth bootstrap then dispatch) +│ │ ├── cli.py # click CLI (15+ subcommands; see cyclone-cli skill) +│ │ ├── api_routers/ # SP36 — 22 FastAPI routers + _shared.py helpers +│ │ ├── auth/ # SP24 — bcrypt + sessions + matrix_gate + admin users + rate_limit +│ │ ├── clearhouse/ # SftpClient + InboundFile + SftpStat +│ │ ├── edi/ # filename regex/builder helpers +│ │ ├── handlers/ # SP27 — per-file-type inbound parse handlers +│ │ ├── parsers/ # X12 tokenizer, models, validator, writers, 277CA, 999, TA1, 270, 271 +│ │ ├── rebill/ # SP41 — in-window rebill pipeline (13 modules) +│ │ ├── reissue/ # SP24 — offline 837P reissue workflow +│ │ ├── store/ # SP21 — split CycloneStore facade (16 modules) +│ │ └── submission/ # SP37 — canonical submit_file helper + recover + bulk_ingest +│ └── tests/ # 178 pytest files (post-SP41) +│ ├── fixtures/ # 22 entries (837P / 835 / 999 / TA1 / 277CA samples + visits CSV) +│ ├── test_api_*.py # FastAPI integration via TestClient +│ ├── test_*.py # pure-unit +│ └── ... +├── src/ # React + Vite + TypeScript UI (12 pages, 84 test files) │ ├── components/ │ │ ├── ui/ # Skeleton, EmptyState, ErrorState, FilterChips, Pagination, … +│ │ ├── ClaimDrawer/ # 12 sibling tests +│ │ ├── RemitDrawer/ # 8 sibling tests +│ │ ├── ProviderDrawer/ # right-anchored side panel +│ │ ├── AckDrawer/ # right-anchored side panel (SP28 ack-claim auto-link) +│ │ ├── inbox/ # 5-lane triage surface (SP14) +│ │ ├── drill/ # DrillStackProvider + DrillableCell + PeekModal │ │ └── TailStatusPill.tsx # live-tail status badge + reconnect button -│ ├── pages/ # Claims, Remittances, Providers, Acks, Activity, Upload, Inbox, … +│ ├── pages/ # Dashboard, Claims, Remittances, Providers, ActivityLog, +│ │ # Upload, Reconciliation, Inbox, Acks, Batches, BatchDiff, Login │ ├── hooks/ # useBatches, useClaims, useRemittances, useProviders, useActivity, useParse -│ │ # + useTailStream, useMergedTail (live tail) -│ ├── lib/ # api.ts, format.ts, utils.ts -│ │ # + tail-stream.ts (NDJSON parser) -│ ├── store/ # zustand sample-data + parsed-batches store -│ │ # + tail-store.ts (FIFO-capped live tail slices) +│ │ # + useTailStream, useMergedTail (live tail triplet) +│ ├── auth/ # AuthProvider, RoleGate, api client (SP24) +│ ├── lib/ # api.ts, format.ts, utils.ts, tail-stream.ts (NDJSON parser), inbox-api.ts +│ ├── store/ # zustand useAppStore (sample data) + useTailStore (FIFO-capped live tail slices) │ └── types/ # shared TS types ├── config/ │ └── payers.yaml # YAML-driven payer + clearhouse config (SP9) @@ -807,11 +821,15 @@ backup API). > 3. The per-SP spec under [`docs/superpowers/specs/`](docs/superpowers/specs/) for whatever you're touching. > 4. The per-SP plan under [`docs/superpowers/plans/`](docs/superpowers/plans/) if you're implementing. -Sub-projects 2 through 19 are **shipped**. See the [completeness +Sub-projects 2 through 41 are **shipped** (last merged: SP41 in-window +rebill pipeline, `merge: SP41 inwindow-rebill-pipeline into main` at +`309e6c0`). See the [completeness review](docs/reviews/2026-06-20-cyclone-completeness-review.md) for the honest gap analysis against the industry definition of a HIPAA -clearinghouse — the short version is that the local-only, -single-operator, single-payer design contract is honored, and the +clearinghouse — the short version is that the LAN-only, +single-operator, single-payer design contract is honored (auth per +SP23, encryption at rest via SQLCipher per SP12, RBAC per SP23, audit +log per SP11), and the items that would be needed to expand that contract (AS2/AS4, SNIP 1–7, HITRUST, 276/277 status, 278 referrals, COB) are intentionally out of scope.