mirror of
https://github.com/azaion/missions.git
synced 2026-06-21 06:41:07 +00:00
a26d7b163b
The .woodpecker/build-arm.yml already pushes ${REGISTRY_HOST}/azaion/missions
(landed earlier as part of the B5 csproj/namespace rename). What this commit
fixes is the missions-internal documentation that still described the legacy
azaion/flights image as the *current* state.
Edits:
- _docs/02_document/deployment/environment_strategy.md: drop "today's edge
compose still references azaion/flights" — B10 is done. Container/service
name 'flights' still noted as B6/B11 work.
- _docs/02_document/deployment/containerization.md: drop "today's Dockerfile
ENTRYPOINT is dotnet Azaion.Flights.dll, image tag base is azaion/flights"
— both AZ-544 (B5) and AZ-549 (B10) done.
- _docs/02_document/deployment/ci_cd_pipeline.md: same fix.
- _docs/02_document/components/07_host/description.md: same fix.
- _docs/02_document/04_verification_log.md row for AZ-549: explicitly
marked "done"; Code symbol column converged to post-rename value.
- _docs/00_problem/restrictions.md E6: parenthetical reworded so the row
reads as a present-state assertion (B10 done) instead of a forward-
looking note.
- _docs/02_document/glossary.md "Synonym pairs" heading flipped from
"today's code ↔ post-rename target" to "pre-rename ↔ post-rename"
(adjacent hygiene — B5-B9+B10 are done across the missions rename
Epic; the table's "today" framing no longer matches reality).
Spec _docs/tasks/todo/AZ-549a_missions_rename_b10_pipeline.md moved to
_docs/tasks/done/.
rg -F 'azaion/flights' missions/ | grep -v done/ now returns only
intentional pre-rename historical references in glossary.md /
architecture.md / restrictions.md / verification_log.md — the "current
state" wording is gone.
Suite-side slice (AZ-549b — _infra/deploy/*/docker-compose.yml image
ref + ci/README.md example) shipped separately in the suite repo.
Co-authored-by: Cursor <cursoragent@cursor.com>
10 KiB
10 KiB
Restrictions — Azaion.Missions
Status: derived-from-code (autodev
/documentStep 6, 2026-05-14). Each restriction below is grounded in code, configuration, or Dockerfile evidence — none are aspirational. References point to the artefact that establishes the constraint.
Hardware restrictions
| # | Restriction | Evidence |
|---|---|---|
| H1 | Service runs on operator-owned edge devices (Jetson Orin / OrangePI / operator-PC), one container per device | Dockerfile multi-arch; ../../suite/_docs/00_top_level_architecture.md § Edge Tier |
| H2 | Multi-arch container — ARM64 dominant (Jetson / OrangePI), AMD64 supported (operator-PC) | Dockerfile --platform=$BUILDPLATFORM, dotnet publish --os linux --arch $arch; .woodpecker/build-arm.yml tag suffix -arm |
| H3 | Vertical scale only — exactly one instance per device, no horizontal scale-out | _docs/02_document/architecture.md § 3 Deployment Model; suite arch doc § Edge Tier |
| H4 | No managed cloud — every deployment is on customer-owned hardware | suite arch doc § Edge Tier |
| H5 | Watchtower handles container restarts; flight-gate prevents container restart mid-mission |
suite arch doc § Edge Tier; _docs/02_document/architecture.md § 6 Availability |
| H6 | Resource limits not enforced inside the container; device-level cgroups / docker compose limits set at suite level | Dockerfile (no --memory / cpu); suite _infra/_compose/ |
Software restrictions
| # | Restriction | Evidence |
|---|---|---|
| S1 | Language: C#; runtime: .NET 10 (net10.0) |
Azaion.Flights.csproj (post-B5: Azaion.Missions.csproj) |
| S2 | Web framework: ASP.NET Core (Microsoft.NET.Sdk.Web) |
csproj |
| S3 | Data access library: linq2db 6.2.0 |
csproj <PackageReference Include="linq2db" Version="6.2.0" /> |
| S4 | Database driver: Npgsql 10.0.2 |
csproj |
| S5 | Auth library: Microsoft.AspNetCore.Authentication.JwtBearer 10.0.5 |
csproj |
| S6 | Swagger / OpenAPI: Swashbuckle 10.1.5, mounted unconditionally (NOT gated on IsDevelopment()) — ADR-005 carry-forward |
csproj + Program.cs |
| S7 | Database engine: PostgreSQL (no other DB engines supported) | Program.cs UsePostgreSQL; suite arch doc § Database Topology |
| S8 | One csproj, one root namespace (Azaion.Missions.* post-B5) — components are logical groupings, not compilation units |
csproj; _docs/02_document/architecture.md ADR-008 |
| S9 | No src/ directory — project sits at the repo root |
repo layout; _docs/02_document/00_discovery.md § Repository Layout |
| S10 | Layer-organized layout (Controllers/, Services/, DTOs/, Enums/, Auth/, Middleware/, Database/ at repo root) |
repo layout; _docs/02_document/module-layout.md |
| S11 | No automated tests today (no tests/ directory; no test sibling project) |
repo layout; _docs/02_document/00_discovery.md § Test Layout |
| S12 | No migration tool — schema bootstrap via raw CREATE TABLE IF NOT EXISTS + one-shot B9 DROP TABLE IF EXISTS block |
Database/DatabaseMigrator.cs; ADR-004 |
| S13 | No in-process message queue, no event bus, no RPC — components communicate via direct C# calls registered in DI | _docs/02_document/architecture.md § 5; Program.cs |
| S14 | Tables OWNED by this service (post-B7+B9): vehicles, missions, waypoints, map_objects (4 owned). 3 borrowed read-only stubs (media, annotations, detection) |
Database/DatabaseMigrator.cs; Database/AppDataConnection.cs; _docs/02_document/data_model.md |
| S15 | gps-denied is decoupled by design — no runtime call in either direction; gps-denied references mission_id / waypoint_id as plain GUIDs in its own tables |
ADR-007 |
Environment / configuration restrictions
| # | Restriction | Evidence |
|---|---|---|
| E1 | Four required env vars at runtime: DATABASE_URL, JWT_ISSUER, JWT_AUDIENCE, JWT_JWKS_URL. Each resolved via Infrastructure/ConfigurationResolver.cs → ResolveRequiredOrThrow (env-first, then IConfiguration config key Database:Url / Jwt:Issuer / Jwt:Audience / Jwt:JwksUrl, else throw at startup). The legacy JWT_SECRET env var is no longer consulted |
Program.cs, Auth/JwtExtensions.cs, Infrastructure/ConfigurationResolver.cs |
| E2 | DATABASE_URL accepts either a postgresql://user:pass@host:port/db URL OR a raw Npgsql connection string (local helper ConvertPostgresUrl). ConvertPostgresUrl does NOT URL-decode user/password — credentials with @, :, /, % need raw Npgsql form |
Program.cs ConvertPostgresUrl |
| E3 | No hardcoded development fallbacks. ResolveRequiredOrThrow throws InvalidOperationException at startup if any of DATABASE_URL / JWT_ISSUER / JWT_AUDIENCE / JWT_JWKS_URL is missing or whitespace-only. ADR-005's "dev fallback secret" branch is obsolete; only the Swagger-unconditional branch remains |
Infrastructure/ConfigurationResolver.cs; Program.cs |
| E4 | JWT signature validation is asymmetric (ECDSA-SHA256) against the JWKS at JWT_JWKS_URL. admin holds the private key; this service caches the public JWKS via Microsoft.IdentityModel.Protocols.ConfigurationManager<JsonWebKeySet> (fetched at startup, refreshed on default schedule, HTTPS-only via HttpDocumentRetriever { RequireHttps = true }). JWKS rotation does NOT require a coordinated redeploy — consumers pick up the new keys at the next refresh tick |
Auth/JwtExtensions.cs; _docs/02_document/components/05_identity/description.md |
| E5 | Container EXPOSE 8080; edge compose maps host port 5002:8080 |
Dockerfile; suite _infra/_compose/ |
| E6 | Image tag: ${REGISTRY_HOST}/azaion/missions:${BRANCH}-arm (B10 done — AZ-549; was azaion/flights:*-arm pre-B10) |
.woodpecker/build-arm.yml |
| E7 | Entrypoint: dotnet Azaion.Missions.dll post-B5 (was Azaion.Flights.dll pre-B5) |
Dockerfile (post-B5) |
| E8 | No environment-specific overrides in appsettings.*.json today, but IConfiguration lookups (e.g. Database:Url, Jwt:Issuer) are wired so adding appsettings.*.json later requires no code changes |
Program.cs; no appsettings.*.json in repo |
| E9 | CORS is gated by Infrastructure/CorsConfigurationValidator.cs. In Production (case-insensitive on ASPNETCORE_ENVIRONMENT) startup THROWS when CorsConfig:AllowedOrigins is empty AND CorsConfig:AllowAnyOrigin != true. In non-Production environments, an empty allow-list with AllowAnyOrigin=false falls back to permissive (AllowAnyOrigin/Method/Header) and emits the PermissiveDefaultWarning startup log. The "all environments permissive" claim no longer holds |
Program.cs, Infrastructure/CorsConfigurationValidator.cs |
| E10 | TLS termination is the suite reverse proxy's responsibility — container exposes plain HTTP on :8080. The JWKS fetch itself is independently constrained to HTTPS (RequireHttps = true) |
Dockerfile; suite arch doc; Auth/JwtExtensions.cs |
Operational restrictions
| # | Restriction | Evidence |
|---|---|---|
| O1 | Migrator runs at every process start; idempotent (IF NOT EXISTS); B9 adds a one-shot DROP TABLE IF EXISTS orthophotos / gps_corrections block for fielded legacy devices |
Database/DatabaseMigrator.cs |
| O2 | flight-gate (suite-level) is the ONLY orchestration that prevents restart mid-mission; no Kubernetes |
suite arch doc § Edge Tier |
| O3 | No version table; the migrator runs every startup | Database/DatabaseMigrator.cs |
| O4 | Single Woodpecker CI job per repo: docker build + push on [dev, stage, main] branches; no test, no security scan, no migration check |
.woodpecker/build-arm.yml |
| O5 | No structured logging (Serilog / Seq) — LogError(ex, "Unhandled exception") is the only application-level log |
Middleware/ErrorHandlingMiddleware.cs; _docs/02_document/architecture.md § 7 |
| O6 | No correlation ID, no per-request audit trail, no per-user attribution (JWT user-id claim parsed but not consumed) | Auth/JwtExtensions.cs; _docs/02_document/components/05_identity/description.md |
| O7 | Health endpoint: GET /health returns { status: "healthy" } with no DB ping (process-liveness only) |
Program.cs MapGet("/health") |
| O8 | Cascade-delete is NOT transaction-wrapped today (ADR-006) — partial failure leaves orphan rows in media / annotations / detection / map_objects |
Services/FlightService.cs (post-B6: MissionService.cs); Services/WaypointService.cs |
| O9 | Each backend service is responsible for its own table migrations; if annotations is absent at deploy time, the cascade-delete walk fails on relation does not exist (abnormal edge deployment) |
_docs/02_document/components/02_mission_planning/description.md Caveats #6 |
| O10 | One-instance-per-device constraint means session state, in-memory caches, and rate limits are NOT cluster-aware (none of these are implemented today either) | Program.cs; suite arch doc |
Out-of-scope (NOT this service's responsibility)
| Concern | Where it lives | Why it's not here |
|---|---|---|
| Token issuance (sign / mint) | admin (central .NET service) |
Local validation only; offline-tolerant edge design |
| User CRUD, role assignment | admin + ../../suite/_docs/00_roles_permissions.md |
Suite-level concern |
| Media storage / upload | annotations (sibling edge service) |
annotations owns the table schema |
| AI annotation rules | annotations |
Schema and behaviour both owned by annotations |
| Object detection / class definitions | Detection pipeline (sibling edge service) | Pipeline owns the detection table |
map_objects write path |
autopilot (sibling edge service) |
This service owns the schema + cascade-delete only |
| Orthophoto / live-GPS / GPS correction | gps-denied (separate service after B7) |
ADR-007 |
| TLS / HTTPS termination | Suite reverse proxy | _docs/02_document/architecture.md § 7 |
| Schema rename / column drop / type change | Future migration tool (ADR-004 carry-forward) | Today's IF NOT EXISTS migrator can't reshape existing schema; B9's DROP TABLE IF EXISTS is the single explicit destructive step |
iss / aud JWT validation |
Now implemented in this service's code (ValidateIssuer=true against JWT_ISSUER, ValidateAudience=true against JWT_AUDIENCE). The CMMC L2 row 3 finding is structurally fixed here; suite-level docs may still describe the legacy model and have a separate sync task pending |
n/a — no longer a carry-forward in this repo |
| camelCase wire-shape migration | Suite-wide cutover (ADR-002 carry-forward) | All-or-nothing; UI + autopilot consume PascalCase today |