# Restrictions — Azaion UI

> Output of `/document` Step 6b. Constraints **actually evidenced** in code,
> configs, Dockerfiles, CI configs, and dependency manifests. Inferred
> aspirations are NOT included unless the source is cited. Categorised as
> Hardware / Software / Environment / Operational per the document skill
> template.

**Status**: synthesised-from-verified-docs (Step 6b — `/document`)
**Date**: 2026-05-10

---

## Hardware

| # | Restriction | Source / Evidence |
|---|-------------|--------------------|
| H1 | **ARM64-only production image** today (no AMD64 build in CI). | `.woodpecker/build-arm.yml` (the only pipeline file); `_docs/02_document/architecture.md` § 3 Deployment Model "Missing from the pipeline today" |
| H2 | **Edge-device deployment target** — operator laptops, OrangePi, Jetson — alongside suite services. | `_docs/legacy/wpf-era.md` §1; `_docs/02_document/architecture.md` § 2 |
| H3 | **No GPU expectation in the UI image** — all AI inference happens server-side; the UI only renders detections. | `nginx:alpine` runtime; no client-side ML libs in `package.json` |
| H4 | **Browser-rendering capability minimum**: HTML5 `<video>` + `<canvas>` + `EventSource`. Operates on Chromium-based + Firefox latest 2 versions. | `ADR-003` (HTML5 video over LibVLC); `_docs/02_document/architecture.md` § 6 NFR row "Browser support" |

## Software

| # | Restriction | Source / Evidence |
|---|-------------|--------------------|
| S1 | **TypeScript strict mode**. | `tsconfig.json` (`strict: true`) per `_docs/02_document/architecture.md` § 2 Tech Stack |
| S2 | **React 19** — latest stable; React Server Components NOT used. | `package.json` `react@19`; `ADR-001` |
| S3 | **Vite 6** as the bundler. | `package.json` `vite@6`; `vite.config.ts` |
| S4 | **Bun 1.3.11** as the package manager (declared via `packageManager`). CI image is `oven/bun:1.3.11-alpine`. | `package.json` `packageManager` field; `Dockerfile`; `.woodpecker/build-arm.yml` |
| S5 | **Static-bundle output only** — production runtime is `nginx:alpine`; **no Node.js in production**. | `Dockerfile` multi-stage build; `_docs/02_document/architecture.md` § 3 |
| S6 | **REST + SSE only** — no WebSocket, no GraphQL, no gRPC-Web. | `src/api/client.ts` + `src/api/sse.ts` are the only transports; `ADR-002`, P1 |
| S7 | **Two React Contexts only** for cross-cutting state (`AuthContext`, `FlightContext`). No Redux / Zustand / TanStack Query. | `src/auth/AuthContext.tsx`, `src/components/FlightContext.tsx`; `ADR-004`, P4 |
| S8 | **Tailwind 4** + `az-*` design tokens are the styling source of truth. | `src/index.css`; `ADR-005` |
| S9 | **Map**: `leaflet@1.9.4` + `react-leaflet@5` (+ `leaflet-draw`, `leaflet-polylinedecorator`). Not Mapbox / Cesium / OpenLayers. | `package.json` |
| S10 | **Charts**: `chart.js@4` + `react-chartjs-2@4`. | `package.json` |
| S11 | **DnD**: `@hello-pangea/dnd@18` for waypoint reorder. | `package.json` |
| S12 | **i18n**: `i18next` + `react-i18next` with English + Ukrainian bundles only. | `src/i18n/i18n.ts`; `_docs/02_document/architecture.md` § ADR-007 |
| S13 | **No client-side persistence library** (no IndexedDB wrapper, no localForage). Bearer is in memory; refresh is in HttpOnly cookie. | `src/auth/AuthContext.tsx`; P3 |
| S14 | **No test framework configured today** — `package.json` has zero test deps; `src/**/*.test.*` is empty. Test runner choice deferred to autodev Step 5 (Decompose Tests) per Step 4.5 decision. | `04_verification_log.md` §1; `architecture.md` § Architecture Vision Open Questions item 7 |

## Environment

| # | Restriction | Source / Evidence |
|---|-------------|--------------------|
| E1 | **Air-gap-friendly bundle** — the SPA ships fully; only OpenWeatherMap and map tiles need internet. (Field deployments need an offline tile cache; not implemented today.) | `_docs/02_document/architecture.md` § 2 "Key constraints driving the stack" |
| E2 | **nginx reverse-proxy strips `/api/<service>/` per service** before forwarding. The SPA's `/api/...` URLs are coupled to this routing. | `nginx.conf` (9 routes); `ADR-006` |
| E3 | **`Secure HttpOnly SameSite=Strict` refresh cookie** issued by `admin/`. Browser MUST use the same origin (or proxied origin) so the cookie scopes correctly. | `_docs/02_document/architecture.md` § 7 Security Architecture |
| E4 | **Vite dev proxy** at `/api → http://localhost:8080` (developers run the suite docker-compose locally). | `vite.config.ts` |
| E5 | **`AZAION_REVISION` env var** is stamped into the production image at build time (`$CI_COMMIT_SHA`). | `Dockerfile`; `.woodpecker/build-arm.yml` |
| E6 | **OCI image labels** — `org.opencontainers.image.{revision,created,source}` are mandatory at push time. | `.woodpecker/build-arm.yml` |
| E7 | **Image registry** is `${REGISTRY_HOST}/azaion/ui:${branch}-arm`; tag scheme is `branch-arm`. | `.woodpecker/build-arm.yml` |
| E8 | **Branch triggers**: CI runs on push to `dev` / `stage` / `main` (mapping to environment names). | `.woodpecker/build-arm.yml` |
| E9 | **`client_max_body_size 500M`** — the server-side hard cap on file uploads (annotation-media batch). | `nginx.conf` |
| E10 | **OpenWeatherMap is consumed directly from the browser** today (CORS-enabled OWM endpoint). The hardcoded API key (P10 violation) is the security concern; the routing pattern itself is the structural concern (Step 6 surface — proxy via suite). | `mission-planner/src/utils/flightPlanUtils.ts:60`; `architecture.md` § Architecture Vision Open Questions item 8 |

## Operational

| # | Restriction | Source / Evidence |
|---|-------------|--------------------|
| O1 | **Bilingual UI is mandatory** (English + Ukrainian). English-only UX is a regression. | P6; `ADR-007`; `_docs/legacy/wpf-era.md` |
| O2 | **Bearer never written to localStorage / sessionStorage**. | P3; `src/auth/AuthContext.tsx` (zero `storage.*` calls) |
| O3 | **All authenticated `fetch` requests must include `credentials:'include'`** for the HttpOnly refresh cookie to flow. The bootstrap refresh in `AuthContext.tsx:24` violates this and is a Step 4 fix. | `src/api/client.ts:44` (correct path); `src/auth/AuthContext.tsx:24` (broken path); `04_verification_log.md` F2 |
| O4 | **RBAC is server-enforced**. The UI MUST NOT trust `AuthUser.role` for security; it is used only for nav rendering. | P3 / `architecture.md` § 7 Authorization |
| O5 | **`Secure HttpOnly SameSite=Strict` refresh cookie** is the single source of refresh-token authority. | `architecture.md` § 7 |
| O6 | **No hardcoded credentials in source** (P10). Current violation: OpenWeatherMap key in `mission-planner/src/utils/flightPlanUtils.ts:60` — Step 4 fix candidate. | P10; `architecture.md` § Architecture Vision |
| O7 | **Spec is the source of truth for numeric enums** (`AnnotationStatus`, `MediaStatus`, `Affiliation`, `CombatReadiness`). UI types file matches the spec verbatim with inline numeric-meaning comments. | P9; `src/types/index.ts`; `04_verification_log.md` enum drift |
| O8 | **Persist what you type** (P11) — fields declared in `UserSettings` (incl. resizable-panel widths) MUST be persisted by the writers; reading without writing back is a violation. Current violation: `useResizablePanel` (Step 4 fix). | P11; `src/hooks/useResizablePanel.ts` |
| O9 | **Admin can edit existing detection classes** (P12) — full CRUD surface. Current code is add + delete only; edit (`PATCH /api/admin/classes/{id}`) is to be re-introduced. | P12; `04_verification_log.md` F10 |
| O10 | **Destructive actions require `ConfirmDialog`** confirmation. Current violations: `AdminPage.handleDeleteClass` (no dialog); `MediaList` uses `alert()` instead. | `_docs/ui_design/README.md` confirmation-dialogs spec; finding B4 |
| O11 | **No SSR / React Server Components** (P2). | `Dockerfile`; `ADR-001` |
| O12 | **The `mission-planner/` tree is NOT compiled by the production Vite build**. It is the port-source for `05_flights` and is on a multi-cycle path to deletion. | `vite.config.ts`; `ADR-009`; `architecture.md` § Mission-planner convergence plan |
| O13 | **Bundle size budget**: ≤ ~2 MB gzipped initial JS (target). Currently no CI gate. | `architecture.md` § 6 NFR row "Bundle size (initial JS)" |
| O14 | **CI test step does not exist today**. To be added once a test framework is selected (autodev Step 5 — Decompose Tests). | `.woodpecker/build-arm.yml`; `architecture.md` § 3 "Missing from the pipeline today" |
| O15 | **No vulnerability scan / SBOM emission / image signing** in the pipeline today. Step 6 surface (security_approach.md). | `.woodpecker/build-arm.yml` |

## Notes on items NOT in this list

- **Browser support matrix** is **not enforced** (no `browserslist` config). The "Chromium + Firefox latest 2" target is aspirational per `architecture.md` § 6.
- **Performance budgets** beyond bundle size and the 500 MB upload cap are **not enforced** in code or CI today.
- **Accessibility floor**: WCAG-level conformance is **not declared**. Multiple a11y findings are recorded for Step 4 / Step 8 (see `architecture.md` § 6 NFR row "Accessibility").
- **Telemetry / observability**: no centralized client telemetry today. Logging is browser-console only. Step 6 surface (`_docs/02_document/deployment/observability.md`).
