Files
Oleksandr Bezdieniezhnykh 7025f4d075 refactor: enhance JWT authentication and CORS configuration
Updated JWT authentication to use configuration values instead of hardcoded secrets, improving security and flexibility. Enhanced CORS policy to conditionally allow origins based on configuration settings, with logging for permissive defaults. Updated README to reflect project renaming and clarify service context.
2026-05-14 19:48:25 +03:00

196 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Azaion.Missions — Final Documentation Report
> **Status**: complete (autodev `/document` Step 7, 2026-05-14).
> **Mode**: retrospective documentation of an existing codebase, post-Step 4 verification + Step 4.5 user-confirmed glossary & vision.
> **Forward-looking caveat**: every artefact in this set describes the **post-rename, post-GPS-Denied-removal** target. Today's source still uses pre-rename names; the doc-vs-code reconciliation table lives in `04_verification_log.md` § 0 and the implementation deltas are tracked under Jira AZ-EPIC AZ-539 children B4B12 (see `_docs/_process_leftovers/2026-05-14_rename-flights-to-missions.md`).
---
## Executive Summary
`missions` is the edge-tier .NET 10 REST service that owns the mission domain (vehicles, missions, waypoints) of an Azaion deployment. The autodev `/document` skill produced a complete bottom-up documentation set across 8 steps: discovery → 12 module docs → 6 component specs → module-layout (file ownership + 5-layer dependency table) → architecture / system-flows / data-model / deployment → verification (9 inline corrections, 2 drift items captured) → user-confirmed glossary & architecture vision → retrospective solution / problem / restrictions / acceptance-criteria / security-approach. This terminates Phase A Step 1 of the autodev `existing-code` flow; Step 2 (Architecture Baseline Scan) is the next auto-chained step.
## Problem Statement
The system is the per-device authority for **vehicle inventory** (Plane / Copter / UGV / GuidedMissile), **mission plans**, and **ordered waypoints** — and is the single orchestrator of the cross-service cascade-delete that keeps `media` / `annotations` / `detection` / `map_objects` consistent when missions or waypoints are removed. It runs as one container per device alongside `annotations`, the detection pipeline, `autopilot`, `gps-denied`, and the React `ui`, sharing one local PostgreSQL with per-service table ownership enforced by convention. JWTs are validated locally with a shared HMAC secret — the service never calls back to the central `admin` issuer. Full statement in `_docs/00_problem/problem.md`.
## Architecture Overview
**Pattern**: thin ASP.NET Core controller → service class → linq2db active-record over a per-HTTP-request scoped `AppDataConnection`. No repository abstraction; no in-process message queue / event bus; no background workers.
**Technology stack**: C# / .NET 10 (`net10.0`) on ASP.NET Core, linq2db `6.2.0` over PostgreSQL via Npgsql `10.0.2`, JWT bearer (HS256, shared secret), Swashbuckle `10.1.5` (Swagger UI mounted unconditionally — ADR-005).
**Deployment**: docker compose per edge device (Jetson Orin / OrangePI / operator-PC); multi-arch ARM64 + AMD64 image built by Woodpecker; Watchtower handles container restarts; `flight-gate` prevents container restart mid-mission; vertical scale only (one instance per device).
8 ADRs (see `architecture.md` § 8): one Postgres per device (ADR-001), PascalCase wire shape carry-forward (ADR-002), manual cascade-delete (ADR-003), `IF NOT EXISTS` schema bootstrap (ADR-004), Swagger + dev fallbacks ungated (ADR-005), cascade-not-transaction-wrapped carry-forward (ADR-006), GPS-Denied moved out (ADR-007), one-csproj layering by convention (ADR-008).
## Component Summary
| # | Component | Purpose | Dependencies (logical layer) | Spec / Epic |
|---|-----------|---------|------------------------------|-------------|
| 01 | `01_vehicle_catalog` | Vehicle CRUD + `is_default` exclusivity (stricter than spec — B12 decision pending) | Layer 3 → `04_persistence`, `05_identity` | suite spec § 6.1; B6 / B12 |
| 02 | `02_mission_planning` | Mission + Waypoint CRUD + cross-service cascade-delete walk | Layer 4 → `01_vehicle_catalog` (existence check), `04_persistence`, `05_identity`, `06_http_conventions` | suite spec § 6.2; B6 / B7 / B8 |
| 04 | `04_persistence` | `AppDataConnection` (linq2db `ITable<T>`) + `DatabaseMigrator` (`CREATE TABLE IF NOT EXISTS` + B9 one-shot `DROP`) | Layer 1 → linq2db + Npgsql only | B7 / B9 |
| 05 | `05_identity` | `JwtExtensions.AddJwtAuth` — HS256 local validation + `"FL"` policy | Layer 2 → ASP.NET Core only | suite-level remediation (AZ-487/AZ-494 carry-forward) |
| 06 | `06_http_conventions` | `ErrorHandlingMiddleware` + `PaginatedResponse<T>` + dead `ErrorResponse` DTO | Layer 2 → ASP.NET Core only | ADR-002 carry-forward |
| 07 | `07_host` | `Program.cs` composition root: env adapter, JWT registration, scoped DI, run migrator, register middleware, `MapGet("/health")`, mount Swagger | Layer 5 → every other component | B5 (csproj rename) / B10 (image tag) |
**Implementation order** (based on dependency graph in `module-layout.md`):
1. **Layer 1**: `04_persistence` — depends only on linq2db + Npgsql.
2. **Layer 2**: `05_identity`, `06_http_conventions` — depend on ASP.NET Core only.
3. **Layer 3**: `01_vehicle_catalog`.
4. **Layer 4**: `02_mission_planning` (reads `vehicles` for existence checks; uses `PaginatedResponse<T>`).
5. **Layer 5**: `07_host` (composition root).
No circular dependencies between components.
## System Flows
| Flow | Description | Key components | Criticality |
|------|-------------|----------------|-------------|
| F1 | Vehicle CRUD | `01_vehicle_catalog``04_persistence` | High |
| F2 | Mission create / read / update with `vehicle_id` existence check | `02_mission_planning``04_persistence`, with `01_vehicle_catalog` lookup | High |
| F3 | Mission delete with cross-service cascade (touches `map_objects`, `media`, `annotations`, `detection`, `waypoints`, `missions`) | `02_mission_planning``04_persistence` + cross-service tables | **Critical** (data integrity; not transaction-wrapped today — ADR-006) |
| F4 | Waypoint CRUD (delete is a scoped F3 cascade) | `02_mission_planning` (`WaypointService`) → `04_persistence` | High |
| F5 | JWT bearer validation (cross-cutting; local HS256 only) | `05_identity` (pipeline middleware) | **Critical** (every authenticated route) |
| F6 | Service startup + idempotent schema migration (B9 one-shot `DROP TABLE IF EXISTS` for fielded legacy devices) | `07_host``04_persistence` | High |
| F7 | Anonymous `GET /health` probe (process-liveness only; no DB ping) | `07_host` | Medium |
Full sequences and per-flow Mermaid diagrams in `system-flows.md` and `diagrams/flows/flow_*.md`.
## Risk Summary
The codebase has no automated tests today, so "risks" here are **observed-from-code carry-forward concerns** (architecture.md § Carry-forward + 00_discovery.md § Spec ↔ Code Divergences), classified by impact. Mitigation column points at the responsible Jira child or the suite-level ticket.
| Level | Count | Items |
|-------|-------|-------|
| Critical | 1 | Cascade-delete is **NOT transaction-wrapped** (ADR-006) — partial failure leaves orphan rows. **One-line fix**, recommended to land with B6 |
| High | 3 | (a) `JWT_SECRET` / `DATABASE_URL` dev fallbacks not gated on `IsDevelopment()` (ADR-005, suite-tracked); (b) JWT `iss`/`aud` validation disabled (CMMC L2 row 3, AZ-487/AZ-494 suite-tracked); (c) Wire-shape divergence — entity/DTO bodies PascalCase, error envelope missing `errors` field (ADR-002 carry-forward) |
| Medium | 4 | (a) "Exactly one default vehicle" stricter than spec + race-prone (B12 / AZ-551 — decision-only ticket); (b) `vehicle_id`-not-found returns 400 instead of spec's 404 (carry-forward); (c) Swagger UI mounted unconditionally (ADR-005); (d) CORS open in all environments (carry-forward) |
| Low | 4 | (a) `ErrorResponse` DTO is dead on the wire and has wrong shape; (b) `Geopoint` stored as 3 flat columns instead of spec's auto-converting `string GPS`; (c) `FL` permission code retains legacy "Flight" wording post-rename (suite-level fleet-wide change); (d) `FuelType` enum may not fit single-use `GuidedMissile` |
**Mitigation status**: every Critical/High item is either covered by an open Jira ticket (B6, B12, AZ-487/AZ-494) or explicitly logged as a suite-level carry-forward in `04_verification_log.md`. No Critical/High item is unaccounted for.
## Test Coverage
**No automated tests exist today.** Verification of every documented behaviour is by code inspection only. The autodev `existing-code` flow's Phase A Steps 3 → 7 is the planned path to convert `_docs/00_problem/acceptance_criteria.md` (10 AC groups, ~60 individual criteria) into runnable test cases.
| Component | Integration | Performance | Security | Acceptance | AC coverage today |
|-----------|-------------|-------------|----------|------------|-------------------|
| `01_vehicle_catalog` | 0 | 0 | 0 | 0 | AC-1 (9 criteria) — inspection only |
| `02_mission_planning` | 0 | 0 | 0 | 0 | AC-2 (8) + AC-3 (7) + AC-4 (7) — inspection only |
| `04_persistence` | 0 | 0 | 0 | 0 | AC-6 (10) — inspection only |
| `05_identity` | 0 | 0 | 0 | 0 | AC-5 (9) + AC-9 (4) — inspection only |
| `06_http_conventions` | 0 | 0 | 0 | 0 | AC-8 (7) — inspection only |
| `07_host` | 0 | 0 | 0 | 0 | AC-6 (10) + AC-7 (4) + AC-10 (6) — inspection only |
**Overall AC coverage by automated tests**: 0 / ~60 (0%) — the gap that Phase A Steps 37 will close.
## Rename Epic Roadmap (Jira AZ-EPIC AZ-539)
The full doc-vs-code rename + GPS-Denied removal is tracked as one Jira Epic + 12 child tickets. B1B3 (the documentation half) landed in this turn; B4B12 (the code half) are still **To Do**.
| Order | Plan ID | Jira | Type | SP | Status | Component / artefact |
|-------|---------|------|------|----|--------|-----------------------|
| Epic | — | [AZ-539](https://denyspopov.atlassian.net/browse/AZ-539) | Epic | — | To Do | umbrella |
| 1 | B1 | [AZ-540](https://denyspopov.atlassian.net/browse/AZ-540) | Task | 3 | **Done** (this turn) | local docs (this repo's `_docs/`) |
| 2 | B2 | [AZ-541](https://denyspopov.atlassian.net/browse/AZ-541) | Task | 3 | **Done** (this turn) | suite docs (`../../suite/_docs/`) |
| 3 | B3 | [AZ-542](https://denyspopov.atlassian.net/browse/AZ-542) | Task | 3 | **Done** (this turn) | local + suite state bookkeeping |
| 4 | B4 | [AZ-543](https://denyspopov.atlassian.net/browse/AZ-543) | Task | 3 | To Do | repo rename (Gitea + suite `.gitmodules` + `git mv`) |
| 5 | B5 | [AZ-544](https://denyspopov.atlassian.net/browse/AZ-544) | Story | 3 | To Do | csproj + namespace `Azaion.Flights``Azaion.Missions` |
| 6 | B6 | [AZ-545](https://denyspopov.atlassian.net/browse/AZ-545) | Story | 5 | To Do | domain rename `Aircraft → Vehicle`, `Flight → Mission`, `AircraftType → VehicleType { Plane, Copter, UGV, GuidedMissile }` |
| 7 | B7 | [AZ-546](https://denyspopov.atlassian.net/browse/AZ-546) | Story | 3 | To Do | drop GPS-Denied entities, `"GPS"` policy, cascade branches, migrator entries |
| 8 | B8 | [AZ-547](https://denyspopov.atlassian.net/browse/AZ-547) | Story | 3 | To Do | HTTP routes `/aircrafts → /vehicles`, `/flights → /missions` |
| 9 | B9 | [AZ-548](https://denyspopov.atlassian.net/browse/AZ-548) | Story | 5 | To Do | DB migration: `ALTER TABLE` rename + `DROP TABLE IF EXISTS` for legacy GPS-Denied |
| 10 | B10 | [AZ-549](https://denyspopov.atlassian.net/browse/AZ-549) | Task | 2 | To Do | Dockerfile entrypoint + Woodpecker image tag + suite compose service block |
| 11 | B11 | [AZ-550](https://denyspopov.atlassian.net/browse/AZ-550) | Story | 5 | To Do | consumer cutover (autopilot + ui + suite e2e) |
| 12 | B12 | [AZ-551](https://denyspopov.atlassian.net/browse/AZ-551) | Task | 2 | To Do | decision-only: lift "exactly one default" into spec + transaction-wrap, OR drop from code |
**Total estimated effort (remaining)**: 35 SP across 9 To-Do tickets. Implementation order has hard dependencies (B5 → B6 → B7 → B8 → B9 → B10 → B11; B4 ride-along; B12 independent decision).
## Key Decisions Made (during documentation)
| # | Decision | Rationale | Alternatives rejected |
|---|----------|-----------|------------------------|
| 1 | Document the **post-rename** target rather than today's pre-rename source | Aligns with the user's intended end state and the Jira Epic; avoids documenting code that's about to be deleted (B7) | (a) Document today's code as-is — rejected: would require a near-total rewrite the moment B5B7 land. (b) Document both — rejected: doubles maintenance burden until B5B7 ship |
| 2 | Each forward-looking doc carries an explicit "post-rename" note pointing at the responsible Jira child | Future readers can reconcile what they see in code against the doc without re-running discovery | Implicit forward-looking — rejected: too easy to mistake a doc for current state |
| 3 | Treat the verification step as **rename-aware** | The doc-vs-code rename mapping is captured once in `04_verification_log.md` § 0; mismatches not covered by the mapping are the only flagged drift | Treat every rename as a verification failure — rejected: would flag every entity name and produce noise |
| 4 | Component count went 7 → 6 (dropped `03_gps_denied`) and `01_aircraft_catalog``01_vehicle_catalog` | Matches the post-rename suite spec; logged in `state.json.decomposition_revised` | Keep 7 with `03_gps_denied` as deprecated — rejected: would conflict with B7's removal scope |
| 5 | `architecture.md` § "Architecture Vision" + `glossary.md` were both confirmed by the user (Step 4.5) | Downstream skills (refactor, decompose, new-task) treat these as authoritative | Skip the glossary — rejected: existing-code projects benefit most from explicit terminology reconciliation |
| 6 | Solution / problem / restrictions / acceptance / security all describe **today's behaviour with carry-forward divergences explicitly called out** | Tests against the docs will catch unintended behaviour changes; spec-conformance fixes are intentional | Rewrite the docs to match the suite spec — rejected: would misrepresent the running code |
## Open Questions (for Phase A Step 2 onward)
| # | Question | Impact | Where it surfaces next |
|---|----------|--------|-------------------------|
| 1 | Will B12 / AZ-551 lift "exactly one default vehicle" into spec (with transaction-wrap) or drop the rule from code? | AC-1.2 / AC-1.3 / AC-1.4 will change shape; the F1 test scenarios in Step 3 will pin whichever resolution the user picks | B12 / AZ-551 ticket |
| 2 | Should B6 also land the cascade transaction-wrap (ADR-006 carry-forward, one-line fix)? | Closes the only Critical risk; B6 is a rename pass and the transaction wrap can ride along cheaply | B6 / AZ-545 review |
| 3 | When does the suite-wide camelCase wire-shape migration happen (ADR-002 carry-forward)? | Affects every `Mission` / `Vehicle` / `PaginatedResponse` consumer (UI + autopilot); not in this Epic | Suite-level ticket (not yet filed) |
| 4 | Does `gps-denied` need to be deployed BEFORE B9's `DROP TABLE IF EXISTS` runs on fielded devices? | Out-of-band ordering matters: AC-10.5; F6 error scenario | B9 / B10 acceptance review |
| 5 | Will B11 cover any other consumers besides UI + autopilot? | Determines B11 scope | B11 / AZ-550 review |
These do not block Step 2 (Architecture Baseline Scan); they are inputs to subsequent test-spec / refactor / new-task work.
## Artifact Index
### Documentation (this repo, `_docs/`)
| File | Description |
|------|-------------|
| `_docs/00_problem/problem.md` | High-level problem statement (post-rename target) |
| `_docs/00_problem/restrictions.md` | Hardware / software / environment / operational restrictions (4 categories, 41 items) |
| `_docs/00_problem/acceptance_criteria.md` | 10 AC groups (~60 criteria), every criterion grounded in code |
| `_docs/00_problem/input_data/data_parameters.md` | Env vars, HTTP DTOs, table schemas (4 owned + 3 borrowed), enum values, endpoint matrix |
| `_docs/00_problem/security_approach.md` | Authn / authz / data protection / input validation / CORS / 8 production-deploy footguns / threat-model summary |
| `_docs/01_solution/solution.md` | Retrospective solution: per-component table + cross-cutting choices + implementation order + testing strategy |
| `_docs/02_document/00_discovery.md` | Suite context, repository layout, tech stack, entry points, configuration, dependency graph (5 layers), spec ↔ code divergences (15 items) |
| `_docs/02_document/04_verification_log.md` | Step 4 verification: rename-aware mode, counts, per-symbol sweep, drift mapping table |
| `_docs/02_document/architecture.md` | Architecture (Vision + 8 sections + 8 ADRs); confirmed by user at Step 4.5 |
| `_docs/02_document/system-flows.md` | F1F7 narrative + cross-cutting concerns + per-flow error tables |
| `_docs/02_document/data_model.md` | Entity / table / cross-service ownership map |
| `_docs/02_document/glossary.md` | Glossary; confirmed by user at Step 4.5 |
| `_docs/02_document/module-layout.md` | File ownership + 5-layer dependency table; status `derived-from-code` |
| `_docs/02_document/components/01_vehicle_catalog/description.md` | Component spec (post-rename) |
| `_docs/02_document/components/02_mission_planning/description.md` | Component spec |
| `_docs/02_document/components/04_persistence/description.md` | Component spec |
| `_docs/02_document/components/05_identity/description.md` | Component spec |
| `_docs/02_document/components/06_http_conventions/description.md` | Component spec |
| `_docs/02_document/components/07_host/description.md` | Component spec |
| `_docs/02_document/modules/{auth,controller_missions,controller_vehicles,database,dtos,entities,enums,middleware,program,service_mission,service_vehicle,service_waypoint}.md` | 12 module docs |
| `_docs/02_document/diagrams/components.md` | Mermaid component relationship diagram |
| `_docs/02_document/diagrams/flows/flow_{vehicle_crud,mission_lifecycle,mission_cascade_delete,waypoint_lifecycle,jwt_validation,startup_migration,health_probe}.md` | Per-flow Mermaid sequence diagrams |
| `_docs/02_document/deployment/{containerization,ci_cd_pipeline,environment_strategy,observability}.md` | Deployment notes |
### State / process artefacts
| File | Description |
|------|-------------|
| `_docs/_autodev_state.md` | Autodev orchestrator state (this skill: Phase A Step 1, complete on Step 7 write) |
| `_docs/02_document/state.json` | Document skill internal state |
| `_docs/_process_leftovers/2026-05-14_rename-flights-to-missions.md` | Rename leftover index — kept until B4B12 ship |
| `_docs/tasks/done/AZ-540_missions_rename_b1_local_docs.md` | B1 task spec (Done) |
| `_docs/tasks/done/AZ-542_missions_rename_b3_state_bookkeeping.md` | B3 task spec (Done) |
| `_docs/tasks/todo/AZ-544_missions_rename_b5_csproj_namespace.md` | B5 task spec |
| `_docs/tasks/todo/AZ-545_missions_rename_b6_domain_rename.md` | B6 task spec |
| `_docs/tasks/todo/AZ-546_missions_rename_b7_drop_gps_denied.md` | B7 task spec |
| `_docs/tasks/todo/AZ-547_missions_rename_b8_http_routes.md` | B8 task spec |
| `_docs/tasks/todo/AZ-548_missions_rename_b9_db_migration.md` | B9 task spec |
| `_docs/tasks/todo/AZ-551_missions_rename_b12_default_vehicle_rule.md` | B12 task spec |
### Suite-level cross-references (read-only from this repo)
| File | Description |
|------|-------------|
| `../../suite/_docs/02_missions.md` | Primary spec (post-rename) |
| `../../suite/_docs/00_top_level_architecture.md` | Topology, error envelope, pagination |
| `../../suite/_docs/00_database_schema.md` | Authoritative ER diagram |
| `../../suite/_docs/00_roles_permissions.md` | `FL` permission origin |
| `../../suite/_docs/11_gps_denied.md` | Separate `gps-denied` service (post-B7) |
| `../../suite/_docs/05_security/cmmc_l2_scorecard.md` | CMMC L2 row 3 finding (AZ-487/AZ-494) |
| `../../suite/_docs/_repo-config.yaml` | Repo registry (post-rename `name: missions`) |
## Next Step in the Autodev Existing-Code Flow
Phase A Step 2 — **Architecture Baseline Scan**: invoke `code-review/SKILL.md` in baseline mode (Phase 1 + Phase 7) against the full codebase, save the output to `_docs/02_document/architecture_compliance_baseline.md`. The scan compares the running code against the just-confirmed `architecture.md` + `module-layout.md` and flags pre-existing High/Critical structural issues. After the baseline is clean, the autodev auto-chains to Step 3 (Test Spec) — which produces `_docs/02_document/tests/traceability-matrix.md` and per-flow scenario files for F1F7, the seed set for the test suite implementation in Steps 57.