> ## Documentation Index
> Fetch the complete documentation index at: https://docs.a7manager.se/llms.txt
> Use this file to discover all available pages before exploring further.

# Environments

> The production environment topology, its security posture, and how development differs.

There are two environments: **production**, and a shared **development**
environment. They are deliberately different — production is network-isolated, development
is publicly reachable.

## Production

### Topology

```mermaid theme={null}
flowchart TB
    I[Internet] -->|HTTPS 443| AGW[Application Gateway WAF v2<br/>the only public entry]
    AGW -->|private| ACA
    subgraph VNET["Virtual network · 10.20.0.0/16 · Sweden Central"]
        ACA[Container Apps environment<br/>internal ingress, no public endpoint]
        ACA -->|private endpoint| PG[(PostgreSQL Flexible Server<br/>public access disabled)]
        ACA -->|private endpoint| AOAI[Azure OpenAI]
        ACA -->|VNet firewall rule| KV[Key Vault]
    end
    ADF[Data Factory<br/>Managed VNet runtime] -->|managed private endpoint| PG
    PROD[Producers] -->|SAS| ST[Storage account<br/>landing zones and photos]
    ADF --> ST
```

Production runs in a **dedicated Azure subscription**, separate from development.

### App Gateway

Application Gateway WAF v2 as the single public entry point. Everything behind it is
private.

|                   |                                                                                                                                                  |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| **TLS**           | 1.2 minimum. Certificate held in Key Vault, referenced by versionless URI, picked up automatically on renewal without a gateway redeploy.        |
| **WAF**           | Microsoft Default Rule Set 2.1, in **Prevention** mode from go-live. Bot Manager 1.0 in detection. Request body inspection enabled.              |
| **Rate limiting** | 10 requests per minute per client address on `/api/auth/`, action Block — credential-stuffing protection at the edge, before the application.    |
| **Headers**       | `Strict-Transport-Security` (1 year, includeSubDomains) and `X-Content-Type-Options: nosniff`, applied centrally so all three apps inherit them. |
| **Availability**  | Zone-redundant across zones 1–3, autoscaling 0–3 capacity units.                                                                                 |

Routing is path-based: `/api/*` to the backend, `/inspect/*` to the inspector PWA, and
everything else to the frontend SPA.

### Network isolation

The Container Apps environment is created with **internal ingress** — its load balancer is
private and no public endpoint exists. App FQDNs resolve, through a private DNS zone, to an
address inside the VNet.

Three subnets, each with its own NSG: the gateway subnet, the private-endpoint subnet, and
the Container Apps subnet. The gateway subnet can reach the apps on 443 and nothing else.

### Data platform

**PostgreSQL Flexible Server** sits behind a private endpoint with `publicNetworkAccess`
disabled. There are no firewall rules because there is no public listener to firewall.

* **Geo-redundant backups** with **35-day retention**, committed at first provisioning —
  this setting is immutable after creation, so it was not deferred.
* **RPO ≈ minutes** from continuous backup. **RTO ≈ tens of minutes** for a
  point-in-time restore; geo-restore covers a regional disaster.
* TLS required on all connections.
* A zone-redundant HA standby is **not** enabled — recovery is restore-based. Unlike the
  backup setting this one is mutable, so HA can be switched on later without recreating the
  server.

**Azure OpenAI** is reached over a private endpoint with public access disabled.
**Key Vault** runs in firewall mode, denying by default and admitting only the two VNet
subnets. **Container Registry** stays public but authenticated — images are pulled by
managed identity and pushes are gated by Entra RBAC, on the reasoning that images carry no
secrets, since secrets are injected at runtime from Key Vault.

### Storage and the ingestion contract

The storage account keeps a **public endpoint by deliberate decision**, because both of its
workloads require one: inspection photos are uploaded and read directly from the browser
via SAS URLs, and producers drop ingestion files into landing containers.

Authorisation is by SAS with stored access policies rather than by network position:

* Producer tokens are **create, write and list only** — no read, no delete. A leaked token
  cannot read ingestion history from `archive/` or tamper with processed files.
* Policy expiry is 12 months, with rotation built into project onboarding.
* Anonymous blob access is disabled; HTTPS-only, TLS 1.2.

The practical consequence for an integrating system is that **there are no network
prerequisites**. No static egress IP to register, no firewall rule to request. Drop
correctly formatted files into the project's `landing/` container with the issued SAS, and
the pipeline takes them from there. The landing zone is the interface, and it is
producer-agnostic by design.

Data Factory reaches the private database through a **Managed VNet integration runtime**
with a managed private endpoint — pipeline definitions, staging schema and stored
procedures are unchanged from development.

## Development

The development environment is **public by design and unchanged** — Container Apps with
external ingress, a PostgreSQL server reachable on its public endpoint, and public Key
Vault, registry and OpenAI. It is the reference for what production would look like
*without* hardening, which is precisely why the two are kept separate.

<Warning>
  Because development is publicly reachable, a **data-hygiene rule** applies: production data
  does not go into the development environment. Treat everything there as test data.
</Warning>

Both environments run the same application images. The differences are build arguments and
environment variables, not code branches — the production frontend build points at a
relative `/api` path since it is same-origin behind the gateway, and the inspector PWA is
mounted under `/inspect/`.
