- Changed current step from 15 (Performance Test) to 9 (New Task) in _docs/_autodev_state.md, reflecting the transition to Cycle 3. - Updated cycle count from 2 to 3 and modified sub-step details to indicate progress in gathering feature descriptions. - Added new lessons to _docs/LESSONS.md, emphasizing best practices for API key management, dependency handling, and reporting inline fixes during security audits. - Enhanced CI/CD pipeline documentation in _docs/02_document/deployment/ci_cd_pipeline.md to include new gates for vulnerability scans and SBOM emissions, along with dependency overrides for transitive dependencies. - Expanded environment strategy documentation in _docs/02_document/deployment/environment_strategy.md to include the new Google Geocode API key management. Co-authored-by: Cursor <cursoragent@cursor.com>
7.0 KiB
Azaion UI — Environment Strategy
Synthesis output of
/documentStep 3d (environment_strategy). Derived fromvite.config.ts,nginx.conf,.gitignore, the workspaceREADME.md, and the absence of a workspace.env.example.
1. Environments
| Env | How it runs | API base | Auth | Tile providers |
|---|---|---|---|---|
| Development | bun run dev (Vite dev server, port 5173) |
Vite dev proxy: /api → http://localhost:8080 (configured in vite.config.ts) |
Suite admin/ service running locally (typically via parent suite docker-compose up) |
Suite-internal satellite-provider via env-configurable VITE_SATELLITE_TILE_URL (defaults to http://localhost:5100/tiles/{z}/{x}/{y} when unset). Cookie auth requires same-origin; running the SPA at localhost:5173 and satellite-provider at localhost:5100 cannot send the auth cookie cross-port — recommend reaching satellite-provider through the suite's local nginx OR running it with auth disabled in dev (per AZ-498 risk #2). mission-planner/ keeps its own independent VITE_SATELLITE_TILE_URL. |
| Stage | nginx in container, ARM image :stage-arm |
nginx /api/<service>/ → http://<service>:8080/ (intra-cluster) |
Stage suite admin/ service | Suite-internal satellite-provider on the same origin (nginx-fronted); cookie auth attached automatically. |
| Production | nginx in container, ARM image :main-arm |
nginx /api/<service>/ → http://<service>:8080/ |
Prod suite admin/ service | Same as Stage. Replaces the previously-used external OpenStreetMap and Esri tile providers as of cycle 2 / 2026-05-12 (AZ-498) — production deploy is gated on the cross-workspace satellite-provider cookie-auth ticket landing (autodev Step 16). |
2. Configuration model
The SPA bundle is fully static. No env vars are read at runtime by the bundle. Every cross-environment difference is resolved at the deployment edge (nginx) or at the suite-service level.
| Concern | Where it's set | Notes |
|---|---|---|
| Backend API URL | nginx proxy_pass (nginx.conf) — same nginx config across stage / prod |
Base URLs are intra-cluster service names (http://annotations:8080, etc.); the URL difference between environments is hidden by the orchestrator's DNS |
| Auth cookie domain | Set by suite admin/ service on Set-Cookie |
UI does not control |
| Refresh-token lifetime | Set by suite admin/ service | UI tolerates any TTL |
| Satellite tile provider URL (main SPA) | .env.example declares VITE_SATELLITE_TILE_URL; resolved at build time via getTileUrl() (src/features/flights/types.ts) with DEFAULT_SATELLITE_TILE_URL fallback. Cycle 2 / AZ-498. |
|
| Satellite tile provider URL (mission-planner) | mission-planner/.env.example declares its own independent VITE_SATELLITE_TILE_URL |
mission-planner only; not deployed |
| OpenWeatherMap API key + base URL (main SPA) | .env.example declares VITE_OWM_API_KEY + VITE_OWM_BASE_URL; resolved by getOwmBaseUrl() and the flightPlanUtils.ts builder. Closed AZ-448 / AZ-449 (no longer hardcoded). |
|
| OpenWeatherMap API key + base URL (mission-planner) | mission-planner/.env.example declares VITE_OWM_API_KEY + VITE_OWM_BASE_URL; WeatherService.getWeatherData(lat, lon) returns null and issues NO outbound fetch when the key is unset (fail-soft). Closed cycle 2 / AZ-499. The previously-committed literal value MUST be revoked at the OWM dashboard (manual deliverable — AC-42 / AZ-499 AC-7); STC-SEC1C defends against re-introduction. |
|
| Google Geocode API key (mission-planner) | mission-planner/.env.example declares VITE_GOOGLE_GEOCODE_KEY; GeocodeService.geocodeAddress(address) returns null and issues NO outbound fetch when the key is unset (fail-soft, console.warn). Closed cycle 2 / AZ-501 (AC-43). The previously-committed literal value MUST be revoked at the Google Cloud Console (manual deliverable — AC-43 / AZ-501 AC-6); STC-SEC1D defends against re-introduction. |
|
AZAION_REVISION |
Stamped into image at build time | For diagnostics |
3. .env strategy
Step 4 testability + cycle 2 added a workspace .env.example (resolved by Vite at build time via import.meta.env.VITE_*). Today it declares: VITE_OWM_API_KEY, VITE_OWM_BASE_URL (AZ-448 / AZ-449), and VITE_SATELLITE_TILE_URL (AZ-498). mission-planner/.env.example mirrors the OWM pair (AZ-499), declares its own independent VITE_SATELLITE_TILE_URL, and (AZ-501) adds VITE_GOOGLE_GEOCODE_KEY for the address-search lookup.
Trade-off: Vite resolves import.meta.env.VITE_* at build time, so dist/ is environment-specific once a non-empty VITE_OWM_API_KEY is baked in — the OpenWeatherMap key (and any future build-time config) cannot be changed without a rebuild. This trades promotability for the air-gap-friendly pattern that lets a deploy ship with VITE_OWM_API_KEY="" (no OWM call, fail-soft null return) when the deployment must not touch the internet.
Future direction (still open):
- Move the OpenWeatherMap call server-side (
flights/service) — would eliminate the bundled key entirely; the env-var hardening in cycle 2 reduces the urgency but does not remove the option. - Introduce a runtime
/config.jsonthat nginx serves — would let ops change feature flags / tile URLs without rebuilding. - OR keep the static bundle and continue using Vite's
import.meta.envfor build-time injection of safe-to-publish values (current approach).
4. Promotability
The same image (:dev-arm, :stage-arm, :main-arm) is built per branch from the same Dockerfile. Theoretically the :dev-arm image is functionally identical to the :main-arm image except for the AZAION_REVISION label.
In practice: branch separation is the gating mechanism. Once dev → stage → main propagation is normalized, the safer pattern is to build ONE image per commit and re-tag it across environments (immutable image promotion). The Woodpecker pipeline does not implement this today; it rebuilds per-branch.
5. Local-dev quirks
- Vite dev proxy (
vite.config.ts) requires the suite to be reachable onhttp://localhost:8080. If the parent suite's docker-compose binds to a different port, the developer must editvite.config.ts(no env-driven override today). bun.lock: committed (perpackage.json'spackageManagerfield).package-lock.jsonis gitignored..idea/,.claude/,.superpowers/: gitignored — IDE / agent metadata.- Playwright entries in
.gitignore: present but aspirational — Playwright is not installed (Step 5–7 territory). - mission-planner: has its own
.env.exampledeclaringVITE_SATELLITE_TILE_URL, (cycle 2 / AZ-499)VITE_OWM_API_KEY+VITE_OWM_BASE_URL, and (cycle 2 / AZ-501)VITE_GOOGLE_GEOCODE_KEY. Runs as a sibling Vite app; not bundled into the deployed image (per AC-31 / NFT-RES-LIM-04). Despite not being deployed, the keys must still be revoked at their respective dashboards because the literals were committed and exist in git history.