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>
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:
IConfiguration(defaults: appsettings.json + ASPNETCORE_-prefixed env vars)Environment.GetEnvironmentVariable(...)(legacy fallback for unprefixed env)- 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
.envfile (created at provisioning) OR a local secrets manager (per-customer choice — the suite supports both patterns). TheJWT_SECRETis suite-wide (one value across all backend services on the device). - Rotation: changing
JWT_SECRETinvalidates 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:8080per 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-stoppedin 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.missionsitself 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_dumpagainstpostgres-local.