# Security boundaries

## Trust zones

```mermaid
flowchart LR
    subgraph DMZ["DMZ (public)"]
        LB[Load balancer<br/>TLS termination]
    end

    subgraph App["Application zone (private)"]
        Nginx[Nginx]
        BE[FastAPI]
        PB[(PocketBase)]
        MCP[MCP]
    end

    subgraph Secrets["Secret zone (Vault / GitHub Actions Secrets)"]
        Vault[(Vault / Secret store)]
    end

    subgraph LLM["LLM zone (your gateway)"]
        Gateway[LLM gateway]
    end

    User[Bank user] -->|HTTPS| LB
    LB -->|mTLS or VPC| Nginx
    Nginx --> BE
    BE --> PB
    BE --> MCP
    BE -->|cert auth| Gateway
    Vault -.->|env injection| BE
```

| Zone            | Contains                        | Trusts                     |
| --------------- | ------------------------------- | -------------------------- |
| **DMZ**         | TLS terminator                  | No one                     |
| **App**         | Nginx, FastAPI, PocketBase, MCP | DMZ inbound, LLM outbound  |
| **Secret zone** | Vault or GH Actions Secrets     | App reads via env at boot  |
| **LLM zone**    | Your gateway                    | App calls with cert/bearer |

The application zone trusts **no external input** beyond the LLM call and the user's authenticated request. Outbound from the LLM call is restricted to the gateway URL via egress allowlist.

## Authentication & RBAC

```mermaid
sequenceDiagram
    participant User
    participant Nginx
    participant API
    participant PB

    User->>Nginx: POST /auth/login {email, password}
    Nginx->>API: /auth/login
    API->>PB: query users + verify hash
    PB-->>API: user record + workspace memberships
    API->>API: sign JWT (JWT_SECRET, 24h)
    API-->>User: { token, workspaces }

    User->>Nginx: GET /datasets (Bearer token)
    Nginx->>API: forward
    API->>API: require_user() + require_workspace_role()
    API->>PB: list scoped to workspace_id
    PB-->>API: rows
    API-->>User: filtered rows
```

* **JWT** signed with `JWT_SECRET`, 24-hour expiry, refresh on rotate.
* **Workspaces** isolate data — every PocketBase query is workspace-scoped.
* **Platform admin** can cross workspaces; actions logged in `admin_actions`.
* **Audit log** — `admin_actions` collection captures actor + intent + diff.

## Secret handling

| Surface    | Mechanism                                                     |
| ---------- | ------------------------------------------------------------- |
| Local dev  | `.env` file, gitignored, never tracked                        |
| CI         | GitHub Actions Secrets (org-scoped where possible)            |
| Production | Vault or bank-side secret manager → env at container boot     |
| LLM auth   | PFX certificate or bearer token, mounted read-only            |
| Pre-commit | Gitleaks v8.28+ blocks commits with known patterns            |
| CI guard   | `secret-scan` job (gitleaks on full history of the PR branch) |

**Rotation SLA**: 24h on exposure. See [Operations / Incident response](/operations/incident-response.md).

## Network surface

| Port        | Service          | Exposed publicly? | Notes                |
| ----------- | ---------------- | ----------------- | -------------------- |
| 8080        | Nginx            | Yes (behind LB)   | Only entry point     |
| 8000        | FastAPI          | No                | Internal only        |
| 8090        | PocketBase admin | No                | Behind nginx `/pb/*` |
| 8180        | MCP              | No                | Internal only        |
| LLM gateway | Outbound         | Allowlist only    |                      |

## Image supply chain

```mermaid
flowchart LR
    Code[Source on main] -->|tag vX.Y.Z| CI[release.yml]
    CI -->|build| Img[OCI image]
    CI -->|sign| Prov[Provenance]
    CI -->|emit| SBOM[SBOM SPDX-JSON]
    CI -->|scan| Trivy[Trivy HIGH+CRITICAL]
    Trivy -->|block on fail| Img
    Img --> GHCR[(GHCR<br/>private)]
    SBOM --> Artifact[(90-day artifact)]
```

All images are:

* Tagged `vX.Y.Z`, `sha-<commit>`, and `latest` (latest banned in prod).
* Built with provenance (`mode=max`) and SBOM (BuildKit + Syft).
* Scanned by Trivy; HIGH+CRITICAL block the publish.
* Pinned by the bank's deployment as `vX.Y.Z` — never `latest`.

## Threat model summary

| Threat                                | Control                                  | Where                        |
| ------------------------------------- | ---------------------------------------- | ---------------------------- |
| Credential leak in source             | Pre-commit gitleaks + CI secret-scan     | App repo                     |
| Vulnerable dependency                 | pip-audit + npm audit + Trivy            | App repo CI                  |
| Unauthorised UI access                | JWT + workspace RBAC                     | FastAPI middleware           |
| Cross-workspace data leak             | Every query filtered by workspace\_id    | `services/access_control.py` |
| Prompt injection / data exfil via LLM | Egress allowlist + Langfuse trace review | App + ops                    |
| Tampered deploy                       | Immutable tags + provenance              | GHCR + infra pin             |
| Insider audit-log tampering           | `admin_actions` append-only              | PocketBase                   |

## What is **not** guaranteed yet

* No cosign keyless signing yet (planned post-cutover). Provenance + SBOM are the current attestation.
* Branch protection at GitHub level requires Team plan; today enforced via CODEOWNERS + reviewer discipline. See [Gate 03](/banking-readiness/gate-03-cicd-security.md).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.edge.nyami.fr/architecture/security.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
