# SENTINEL — Critical-Infrastructure Air-Gap

A token-gated, one-way air-gap for critical infrastructure (nuclear reactor
control, grid SCADA, records vaults). It models — and self-checks — the fact
that a real OT high side is **not on any network**, and it enforces that every
crossing into the protected side carries a valid **Sovereign Identity Fabric**
token.

## What it does

- **True isolation posture** (`isolation.py`, ADD-A). `assert_isolated(zone)`
  verifies the zone's HIGH side has **no network bindings** and no outbound
  capability. A zone starts isolated; the test hook `simulate_open_socket()`
  models an operator error, after which `assert_isolated()` fails. Honest label:
  **enforced-by-design in the simulation; physically/logically disconnected in a
  real deployment** (software alone can't guarantee an air-gap — the physical
  disconnect does).
- **One-way data diode** (`data_diode.py`). `transfer_forward()` moves data
  `LOW -> HIGH` only, and only with a valid fabric token. `transfer_reverse()`
  is **refused at the API level and never performed** — there is no HIGH -> LOW
  code path. Sides are local directories, never sockets.
- **Air-gapped vault on the high side** (ADD-A). Each zone can host an instance
  of the existing `sentinel/vault` AES-256-GCM vault. The vault lives **only** on
  the isolated side and is reachable only through a completed token-gated
  crossing; it unlocks only with the correct passphrase.
- **Multiple named zones** (ADD-A). The gateway manages any number of zones
  (`reactor-control`, `grid-scada`, `records-vault`, …), each with its own
  diode, isolation posture, and optional vault.
- **Breach response** (`air_gap_gateway.py`). A bad / forged / wrong-type /
  wrong-zone token: (a) refuses the crossing, (b) `lockdown()` seals the diode,
  (c) calls `identity_fabric.rotate(cause="breach")` for the entity, (d) writes
  a **hash-chained** breach event. `status()` shows `OPEN` / `LOCKED_DOWN`;
  `reset()` re-opens.

## Files

| File | Purpose |
|------|---------|
| `isolation.py`        | High-side "no network" posture + `assert_isolated()` |
| `data_diode.py`       | One-way LOW→HIGH diode (reverse refused) |
| `air_gap_gateway.py`  | Multi-zone gateway: crossing, vault, lockdown, breach log |
| `verify_run.py`       | 13 headless PASS/FAIL checks |
| `demo_seed.py`        | Stands up 3 zones with data + a breach demo |

## Run

```bash
pip install -r requirements.txt   # flask + cryptography (for the vault)
python demo_seed.py
python verify_run.py              # 13 PASS/FAIL proof checks
```

## Honest scope note

- **Track-back, never hack-back.** ZERO network egress: no sockets, no outbound
  requests. Sides are local directories; the breach log and vault are local
  SQLite / on-disk crypto.
- The one-way and air-gap properties are **enforced-by-design here and
  hardware/physically enforced in a real deployment**. This module makes the
  intended posture explicit and testable; it does not replace a physical diode
  or physical disconnection.
- The reused vault is a prototype and **not independently audited**. All data,
  entities, and passphrases are synthetic demo values.
