A Python platform for aircraft weight & balance, load planning, loadsheet generation, IATA post-flight messaging (LDM, MVT, UCM, CPM, NOTOC), EFB pilot acceptance, and audit / retention / DR hardening — for a 40-aircraft mixed-fleet carrier (A320 family, B787-8/9, Embraer E2).
End-to-end load control workflow:
math_core— pure AHM 560 functions (Index, MAC, DOW, envelope, stab trim)aircraft_types— JSON catalog + SQLAlchemy 2 + FastAPI CRUDflights— schedule, multi-leg, activity plan (T-XX templates)load_planning— EZFW statistical v1 (per (route, type, season, dow) + Bayesian shrinkage) + ML v2 (GradientBoostingRegressor)distribution_solver— CP-SAT autoload (OR-Tools) for narrow-body bulk loadingloadsheet— AHM 517 (Loadsheet) + AHM 519 (Trim Sheet) + LIR + NOTOC as PDFmessages— AHM 583/780/424/587/382 generators + Type-B wire format + outboxmessages/transports— SITA, Email, ACARS, AIDL, Test transports + workermessages/inbound— BSM (AHM 565) + PNL (AHM 588) parsers + inbox with idempotencyefb— captain acceptance/decline with signature + GPS, audit chainhardening— append-only audit log, ISAGO 7-year retention, API keys, deep health check, backup/restore scripts
src/wb_platform/
├── math_core/ # Phase 0
├── aircraft_types/ # Phase 1
├── flights/ # Phase 2.1
├── load_planning/ # Phase 2.2
│ ├── ml/ # Phase 8 (GradientBoostingRegressor)
│ └── reconciliation.py # Phase 7
├── distribution_solver/ # Phase 3.1
├── loadsheet/ # Phase 3.2
├── messages/ # Phase 4
│ ├── transports/ # Phase 5
│ └── inbound/ # Phase 7
├── efb/ # Phase 6
├── hardening/ # Phase 10
├── main.py
alembic/ # migrations
data/ # JSON seed data
scripts/ # backup.sh, restore.sh
tests/ # 510 tests
docker-compose.yml # PostgreSQL dev
frontend/ # React + Vite operator UI (Phase 9)
cd wb_platform
pip install -e ".[dev]"pytest # 510 testsPYTHONPATH=src WB_DATABASE_URL=sqlite:///./wb.db alembic upgrade head
PYTHONPATH=src python3 -m uvicorn wb_platform.main:app --reloadTo run with PostgreSQL instead:
docker compose up -d postgres
WB_DATABASE_URL=postgresql+psycopg://wb:wb@localhost:5432/wb \
PYTHONPATH=src alembic upgrade head
WB_DATABASE_URL=postgresql+psycopg://wb:wb@localhost:5432/wb \
PYTHONPATH=src uvicorn wb_platform.main:app --reloadAircraft types, registrations, flights, load plans, EZFW, loadsheets (Phases 1-3)
Messages (Phase 4)
POST /messages/address-book # add SITA / email / ACARS address
GET /messages/address-book/{registration} # list for one aircraft
GET /messages/address-book # list all
POST /messages/build/ldm/{flight_id} # build a LDM (Type-B encoded)
POST /messages/build/mvt/{flight_id}?event_code=AX
POST /messages/build/ucm/{flight_id}?direction=OUT [+ ULD list]
POST /messages/build/cpm/{flight_id} [+ ULD list with positions]
POST /messages/build/notoc/{flight_id} [+ special loads]
POST /messages/queue/ldm/{flight_id} # build + queue in outbox
GET /messages/outbox # list (filter by flight_id, status)
POST /messages/outbox/{id}/send # mark sent, write to log
GET /messages/log?flight_id=... # list logs
GET /messages/log/{id} # log details (with formatted text)
Transports (Phase 5)
GET /messages/transports # list available transports + health
POST /messages/outbox/{id}/send?transport=TEST # send via a specific transport
POST /messages/outbox/process?transport=SITA # drain the outbox
GET /messages/transports/test/captured # inspect TEST-captured messages
DELETE /messages/transports/test/captured # clear TEST capture
EFB (Phase 6)
POST /efb/loadsheets/{flight_id}/create-acceptance # ops creates PENDING
POST /efb/acceptances/{id}/deliver # mark DELIVERED
GET /efb/inbox[?captain=...] # captain's pending
GET /efb/acceptances/{id} # view record
POST /efb/acceptances/{id}/accept # captain signs ACCEPT
POST /efb/acceptances/{id}/decline # captain signs DECLINE
GET /efb/acceptances/{id}/verify # verify SHA-256 signature
GET /efb/flights/{flight_id} # latest acceptance for flight
POST /loadsheets/{flight_id}/v/{version}/create-efb-acceptance # one-shot
Inbound (Phase 7)
POST /inbox/receive # parse raw Type-B → inbox
GET /inbox[?flight_id=&unapplied_only=] # list
GET /inbox/{id} # view
POST /inbox/{id}/apply # mark applied (audit hook)
GET /inbox/flight/{flight_id} # list for a flight
GET /load-plans/{flight_id}/reconcile # PNL/BSM vs plan
ML EZFW (Phase 8)
GET /ezfw/ml/model # current model info
POST /ezfw/ml/train # train (or retrain) the ML model
POST /ezfw/ml/predict # predict ZFW (ML or v1 fallback)
Admin / Hardening (Phase 10)
GET /health # liveness
GET /health/deep # DB + transports + audit chain
GET /admin/audit # list audit entries
GET /admin/audit/verify # walk the hash chain
GET /admin/retention/status # compliance dashboard
POST /admin/retention/archive # archive > retention
A small React + Vite SPA for load controllers lives in frontend/. Five pages,
no business logic in the browser — the API is the source of truth.
cd frontend
npm install
npm run dev # http://localhost:5173 (proxies /api → :8000)
npm run build # static build into dist/See frontend/README.md for layout, conventions, and screenshots.
| Transport | Real impl | Dev impl |
|---|---|---|
| SITA | SITA Messaging API or telex terminal | Writes sita_outbox/TS_ADDR_MSG.txt |
| SMTP via smtplib | Writes email_outbox/TS_ADDR.eml |
|
| ACARS | ARINC / SITA datalink | Writes acars_outbox/TS_REG.arl (with Q1 envelope) |
| AIDL | ARINC 633 / EFB vendor | Writes aidl_outbox/TS_REG.aidl (XML envelope) |
| TEST | n/a | In-memory capture for assertions |
To enable real SMTP for EMAIL, set SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASSWORD in the environment.
Captain reviews the loadsheet on the EFB tablet, signs it with their name + EFB device id + GPS coordinates. The signature is a SHA-256 hash of the canonical record (flight_id + version + captain + decision + signed_at + GPS). Stored in efb_acceptance with a signature_hash column; the original record is tamper-evident (modifying any field breaks the signature).
Decline requires a reason (e.g. CG_OUT_OF_ENVELOPE, FUEL_MISMATCH).
- Audit log: append-only table with SHA-256 hash chain.
/admin/audit/verifywalks the chain and reports any tampering. - Retention: ISAGO 7-year retention policy.
/admin/retention/statusreports violations;/admin/retention/archivemoves them to<table>_archivetables. Strict policies (audit log, EFB acceptance) require explicitapprove_strict=true. - API key auth: set
WB_API_KEYS=key:name:scope1,scope2;...to enable. Empty = dev mode (no auth). - Deep health:
/health/deepchecks DB connectivity, transport health, and audit chain integrity. - Backup/restore:
scripts/backup.shandscripts/restore.shwork for both SQLite and PostgreSQL. - PostgreSQL:
docker compose up -d postgresspins up a 16-alpine Postgres with thewbschema and user. All SQLAlchemy 2 code is database-agnostic; no schema changes are needed.
- Phase 0 (done): math_core
- Phase 1 (done): aircraft_types
- Phase 2 (done): flights + load_planning
- Phase 3 (done): distribution_solver + loadsheet
- Phase 4 (done): messages + Type-B encoder + outbox
- Phase 5 (done): transports (SITA, Email, ACARS, AIDL) + outbox worker
- Phase 6 (done): EFB acceptance (signature + GPS + decline reason)
- Phase 7 (done): inbound BSM/PNL parsers + inbox + reconciliation
- Phase 8 (done): ML EZFW (GradientBoostingRegressor with v1 fallback)
- Phase 9 (skipped): LPM, SLS, DIV, RQM — deferred (more parsers, no new architecture)
- Phase 10 (done): hardening (audit chain, ISAGO retention, auth, deep health, backup/restore, PostgreSQL)
The load_planning/ml/ module provides a v2 ZFW predictor that uses
scikit-learn's GradientBoostingRegressor. The v1 statistical
predictor is still available as a fallback.
Features (built from FlightContext):
- route hash, iata_type hash
- season (one-hot), day-of-week (one-hot), hour of day
- capacity_kg, pax_booked, payload_capacity_kg, load_factor
- route history mean ZFW and n
Training: POST /ezfw/ml/train?n_samples=300&n_estimators=100&max_depth=4
synthesizes training data and trains a model. In production this would
read from actual ZFW measurements (one row per flight, joining PNL/BSM
data with the loadsheet).
Inference: POST /ezfw/ml/predict with flight_id, route, iata_type, std_utc, pax_booked, capacity_kg, payload_capacity_kg returns a
prediction with 95% CI and confidence score.
Model persistence: Models are saved to WB_EZFW_MODEL_PATH
(default ./models/ezfw_v1.pkl). The predictor lazy-loads the
model on first predict, and persists each retrain.