Files
missions/_docs/02_document/deployment/environment_strategy.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

6.6 KiB

Environment Strategy

Note

: image tag and namespace reflect the post-rename state (B10 done). The container name and compose service name are still flights — that rename is B6/B11 (consumer cutover), tracked separately.

Environments

Environment Where it runs Audience Image tag (post-B10)
Development Local workstation (dotnet run from the repo root, or docker run against a local PG) Engineers none — built ad-hoc
Edge production Each customer-owned edge device (Jetson Orin / OrangePI / operator-PC), one container per device Operators, autopilot, UI azaion/missions:dev-arm / stage-arm / main-arm per device tier

There is no centralized staging environment in the suite. Each edge device is its own deployment; the stage image tag is for pre-prod customer demo devices, not for a separate cloud staging cluster.

Configuration sources (precedence)

Program.cs resolves each setting in this order:

  1. IConfiguration (defaults: appsettings.json + ASPNETCORE_-prefixed env vars)
  2. Environment.GetEnvironmentVariable(...) (legacy fallback for unprefixed env)
  3. Hardcoded dev fallback (last resort)

DATABASE_URL

Environment Value source Resolved value
Development (no env set) hardcoded fallback Host=localhost;Database=azaion;Username=postgres;Password=changeme
Development (env set) env var Whatever the engineer sets (URL form OR raw Npgsql key=value form both work)
Edge production env var passed via docker compose postgresql://postgres:${PG_LOCAL_PASSWORD}@postgres-local/azaion (per ../../../suite/_docs/00_top_level_architecture.md)

ConvertPostgresUrl helper parses the URL form into Npgsql key=value form. Does NOT URL-decode user/password — credentials with @, :, /, % will be mis-parsed. 07_host Caveats #4 calls this out. Mitigation: avoid those characters in the local PG password (the standard suite provisioning does), or pass a raw Npgsql key=value string instead of a URL.

JWT_SECRET

Environment Value source Resolved value
Development (no env set) hardcoded fallback development-secret-key-min-32-chars!! (well-known; never use in production)
Development (env set) env var Whatever the engineer sets
Edge production env var (compose env_file or per-device-provisioned secret) The shared HMAC secret used by admin + every backend service on the device. Rotation is suite-coordinated — see architecture.md § Security

Critical foot-gun (ADR-005 carry-forward): there is no runtime gate that blocks startup with the dev fallback in production. A misconfigured production deploy will silently boot with the well-known dev secret. The CMMC L2 scorecard tracks the broader fix at suite level (AZ-487 / AZ-494).

Other configuration

These settings are NOT environment-overridable today (carry-forward improvements):

Setting Current behavior Should it differ between dev and prod?
Swagger UI mount Always on Yes — production should gate on IsDevelopment() (ADR-005)
CORS policy AllowAnyOrigin/Method/Header always Yes — production should restrict to the suite's reverse-proxy origin
HTTPS redirection None — assumes upstream TLS termination No — the suite's reverse proxy handles TLS; this is correct
Logging verbosity ASP.NET Core defaults (Information+) Probably not at this scale; LogLevel:Default = Warning could be useful in prod
Migrator DROP TABLE IF EXISTS (B9 one-shot) Runs every startup; idempotent on already-cleaned devices No — the idempotent design means this is safe everywhere

Edge compose excerpt (suite-wide pattern, post-B10)

Per ../../../suite/_docs/00_top_level_architecture.md § Edge compose:

services:
  missions:                                       # was: flights (pre-B10)
    image: ${REGISTRY_HOST}/azaion/missions:${BRANCH:-main}-arm
    container_name: missions
    restart: unless-stopped
    depends_on:
      postgres-local:
        condition: service_healthy
    environment:
      DATABASE_URL: postgresql://postgres:${PG_LOCAL_PASSWORD}@postgres-local/azaion
      JWT_SECRET:   ${JWT_SECRET}
    ports:
      - "5002:8080"
    networks:
      - azaion-edge

The actual file lives in the suite repo (../../../suite/_infra/_compose/) — the snippet here is illustrative.

Secrets management

  • Local dev: hardcoded fallbacks in Program.cs (the dev-secret values listed above).
  • Edge production: env vars sourced from a per-device .env file (created at provisioning) OR a local secrets manager (per-customer choice — the suite supports both patterns). The JWT_SECRET is suite-wide (one value across all backend services on the device).
  • Rotation: changing JWT_SECRET invalidates every issued token until new ones are minted. Coordinated procedure across the device's backend services + UI re-login. There is no online rotation — every backend must be restarted with the new secret simultaneously.

Network / port layout

  • Container EXPOSE 8080; bound HTTP only (no TLS in this service).
  • Edge compose maps 5002:8080 per the suite convention.
  • Reverse proxy (Caddy fronting the suite per ../../../suite/_docs/00_top_level_architecture.md) terminates TLS upstream.
  • No outbound network calls except to postgres-local (DB).

Restart / lifecycle

  • Container restart policy: unless-stopped in production compose (manually-stopped containers stay stopped; crash → auto-restart).
  • Watchtower polls the registry and triggers re-create on new image digest.
  • flight-gate (suite component) gates container restart so it does not happen mid-mission. Once the active mission completes, Watchtower's queued restart goes through.
  • missions itself does not implement graceful shutdown beyond ASP.NET Core's defaults — in-flight HTTP requests are allowed to complete; idle connections are closed.

Backup / disaster recovery

  • Out of scope for this service. PostgreSQL backup is a per-device, suite-level concern (not per-service). Each edge device runs postgres-local; backup cadence and offsite replication are decided at provisioning time and documented at suite level (currently informally).
  • No application-level export — the only way to read mission / waypoint data out of the device is through the API or pg_dump against postgres-local.