Files
missions/_docs/00_problem/restrictions.md
T
Oleksandr Bezdieniezhnykh a26d7b163b [AZ-549] B10a: clean up forward-looking notes; mark image rename done
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>
2026-05-16 11:57:09 +03:00

10 KiB

Restrictions — Azaion.Missions

Status: derived-from-code (autodev /document Step 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.csResolveRequiredOrThrow (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