mirror of
https://github.com/azaion/missions.git
synced 2026-06-21 15:01:07 +00:00
78dea8ebab
ci/woodpecker/push/build-arm Pipeline was successful
Enhanced the .gitignore to exclude test results and updated the Dockerfile to include a new entrypoint script for improved container initialization. Refactored JWT configuration to support additional parameters for automatic refresh intervals, ensuring better control over token management. Updated the ConfigurationResolver to enforce required environment variables without hardcoded fallbacks, enhancing security and flexibility.
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 post-B10 (was azaion/flights:*-arm pre-B10) |
.woodpecker/build-arm.yml (post-B10) |
| 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 |