# n-OS-tr Threat Model and Privacy Contract

> **Status:** Draft. This document is the authoritative definition of what privacy properties n-OS-tr promises its users. Every future feature, package, service, and configuration change **must** be checked against this document. If a feature would violate a guarantee listed here, the feature does not ship. If a subsystem cannot be implemented without violating a guarantee, the guarantee wins.
>
> Scope: this document covers the *core* of n-OS-tr — the boot flow, identity subsystem, config projection, and the ephemeral rootfs contract. It does not attempt to extend privacy guarantees to *arbitrary user-installed software* beyond what the OS can enforce at the boundary.

## 1. What n-OS-tr is trying to protect

The single-sentence product promise is:

> After you power off an n-OS-tr machine, it should be indistinguishable — to any reasonable examiner with access to the machine and its boot media afterwards — from a machine that has never been booted at all.

That sentence implies several concrete properties:

- **No persistent local identity.** The machine does not retain your npub, nsec, or any derived key after shutdown.
- **No persistent local state.** Dotfiles, preferences, history, caches, cookies, and credentials do not survive shutdown.
- **No persistent cross-boot fingerprint.** `machine-id`, MAC address, hostname, and similar identifiers do not carry information between boots.
- **No secondary leakage.** Logs, swap, crash dumps, and journald do not contain your identity or activity after shutdown.
- **Identity portability.** The only source of truth for "who you are" is the 12-word mnemonic you hold externally (in memory, on paper, etc.). Reconstructing your environment on any other n-OS-tr machine produces the same identity, keys, and configuration.

## 2. Attacker model

This document defines which adversaries n-OS-tr attempts to defend against. Attackers not listed here are explicitly out of scope.

### 2.1 Adversaries we defend against

| Adversary                                 | Scenario                                                                                          | Defense                                                       |
|-------------------------------------------|---------------------------------------------------------------------------------------------------|---------------------------------------------------------------|
| **Subsequent user of the same machine**   | Someone boots the same laptop/PC after you and inspects local storage.                            | Root is tmpfs overlay. Nothing was written to the disk.       |
| **Forensic analysis of the boot media**   | Someone obtains the USB/SD after shutdown and mounts it read-write on their own machine.          | Boot media is read-only during operation. Identity is not on it. |
| **Relay operator passively collecting events** | A relay operator sees all events you publish or fetch.                                        | Private config is NIP-44-encrypted to yourself; only metadata is visible. |
| **Passive local network observer**        | Someone watching the local Wi-Fi / Ethernet segment.                                              | fips mesh + TLS for local services. Local relay preferred over remote. |
| **Hostile Wi-Fi / captive portal**        | A network that tries to fingerprint, MITM, or redirect your traffic.                              | MAC randomization, no credential caching, DoH/DoT for upstream DNS. |
| **Accidental self-doxxing via shell history / logs** | You type your nsec into a terminal for debugging, or a service logs a key.                | Volatile journald, `HISTFILE=/dev/null` by default, hardened stdout/stderr handling. |
| **Cross-machine correlation via fingerprints** | An observer tries to correlate two sessions on different machines as "the same user."        | Fresh `machine-id`, fresh MAC, fresh hostname, no persistent UUIDs. |

### 2.2 Adversaries we do **not** defend against

These are listed explicitly so that we don't pretend. Users relying on n-OS-tr for privacy should understand these limits.

| Non-defense                                 | Why not                                                                                                         |
|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
| **Compromised hardware with a hypervisor, TPM attack, or firmware implant** | We cannot defend a user whose laptop has been physically modified or whose firmware runs hostile code. |
| **Adversary physically present while the machine is booted** | If RAM contents and display can be captured live, the session's keys and plaintext are readable. |
| **Screen/keylogger implanted on the laptop** | Out of scope. We cannot enforce the integrity of I/O peripherals.                                                |
| **Correlation by content / writing style** | Posting the same text from two different keys leaks the linkage. Stylometric defenses are out of scope.          |
| **Relay operator collusion across many relays** | If every relay you use colludes, metadata correlation (timing, sizes, IPs) is possible.                      |
| **RF side channels, TEMPEST, acoustic attacks** | Out of scope.                                                                                                |
| **Nation-state adversary with global passive capability** | We aim for "meaningfully private against reasonable adversaries," not "NSA-proof."                      |
| **User leaking their own mnemonic** | If the user writes the 12 words on a sticky note attached to the monitor, no technical defense helps.             |
| **Browser/app fingerprinting performed by software the user runs** | We provide a clean environment; we do not intercept or rewrite traffic from arbitrary user apps.       |

## 3. Trust boundaries

Every subsystem in n-OS-tr trusts specific inputs and distrusts others. Listing them explicitly helps avoid implicit trust creep.

| Subsystem              | Trusts                                                             | Does not trust                                                |
|------------------------|--------------------------------------------------------------------|---------------------------------------------------------------|
| **boot loader + kernel** | The ISO/IMG that was flashed. Verified at build time by hash.    | Anything on removable media other than the boot media itself. |
| **privacy-hardener**   | Its own unit ordering, sysctl config, hook scripts baked into image. | Anything read from the network during or after hardening.   |
| **identity-agent**     | Keyboard input from the user; an imported mnemonic file if explicitly selected. | Files on persistent media by default. Network input. Any other process except its own supervised children. |
| **config-loader**      | Events signed by the user's main key at NIP-06 index 0.            | Events from any other key. Relay operators. Event metadata is not assumed confidential. |
| **config-writer**      | User-initiated file changes; explicit "publish on shutdown" opt-in. | Relay uptime; concurrent edits from other clients without version tags. |
| **shutdown-wiper**     | Its own deterministic cleanup script.                              | External signals ("clean shutdown") that cannot be verified.  |
| **user services** (fips, c-relay, ginxsom, nginx) | Keys derived by identity-agent at fixed indices. Local network namespace. | Each other. Run with minimum privileges, distinct users, and hardened systemd units. |

## 4. Forbidden behaviors

This is the acceptance-criteria list. Every release must satisfy all of these. Deviations require a documented exception in this file, not a silent change.

### 4.1 The mnemonic and derived keys

- **F1.** The 12-word mnemonic MUST NOT be written to any block-backed storage (ext4, FAT, SD/USB block device) at any time.
- **F2.** The mnemonic MUST NOT be written to swap. Swap MUST be disabled by default.
- **F3.** Derived private keys (indices 0..N) MUST reside only in the identity-agent's process memory. They MUST NOT be persisted to disk.
- **F4.** Memory holding the mnemonic or any derived private key MUST be `mlock()`-ed (or equivalent) so that it cannot be paged out.
- **F5.** The shell MUST NOT record commands to a file. `HISTFILE=/dev/null` is the default; no `~/.bash_history`, no `~/.zsh_history`.
- **F6.** No service and no library may log or echo a private key, even at debug levels. Tests in the CI pipeline grep the shipped image for `nsec1` / `priv` patterns in logs and fail the build if any are found. *(CI rule to add — not yet present.)*
- **F7.** On clean shutdown, identity-agent MUST zero the memory regions holding the mnemonic and derived keys.

### 4.2 On-disk state

- **F8.** The root filesystem MUST be an overlayfs with a `tmpfs` upper layer. Writes MUST NOT reach the boot media during normal operation.
- **F9.** `/var/log`, `/var/lib`, `/home`, `/tmp`, `/run` MUST be tmpfs-backed.
- **F10.** systemd-journald MUST be configured `Storage=volatile` with RAM cap. No on-disk journal persistence is permitted by default.
- **F11.** The image MUST NOT ship with `rsyslog` or any syslog daemon that writes to `/var/log/*.log`.
- **F12.** Bash/zsh histories, `.viminfo`, `.lesshst`, and similar per-user history files are suppressed or redirected to `/dev/null` via shell profile defaults.
- **F13.** APT, pip, npm, cargo caches are either disabled or tmpfs-backed; they MUST NOT persist to the boot media.

### 4.3 Cross-boot fingerprints

- **F14.** `/etc/machine-id` MUST be regenerated at boot (e.g., empty at image-build time, filled by `systemd-machine-id-setup` on first boot of each session). It MUST NOT be the same across two boots.
- **F15.** MAC addresses on all network interfaces MUST be randomized at each boot by default. Opt-out available per-interface via a user-signed config event, not via persistent local state.
- **F16.** Hostname is set to a randomized or generic default; no user-chosen hostname persists unless explicitly set via Nostr config (and therefore tied to the mnemonic, not the machine).
- **F17.** NetworkManager / `wpa_supplicant` MUST NOT cache Wi-Fi credentials, SSIDs, or PSKs to the boot media. Wi-Fi credentials, if stored at all, live on Nostr (NIP-44 encrypted) and are scoped to the user's identity, not the machine.
- **F18.** Bluetooth pairing keys (`/var/lib/bluetooth/*`) MUST NOT persist.
- **F19.** The Linux kernel random number generator SHOULD be seeded from hardware RNG plus `getrandom()` at boot. No `/var/lib/systemd/random-seed` on the boot media.

### 4.4 Networking

- **F20.** DNS resolution SHOULD default to DNS-over-TLS or DNS-over-HTTPS to prevent local operator snooping. Fallback to plain DNS only when explicitly permitted by user config.
- **F21.** No service SHOULD bind on 0.0.0.0 / `::` by default unless explicitly required (e.g., fips ethernet transport listens on 0.0.0.0 by design, relay admin does not).
- **F22.** There SHOULD be a clearly documented way to operate fully offline — with network disabled — while still loading cached Nostr config from a local c-relay or local file.

### 4.5 Shutdown

- **F23.** Shutdown MUST run a wiper hook that:
  - Kills the identity-agent (triggering its own memory-zero routine),
  - Flushes/zeros known sensitive memory ranges (e.g., `/run/ginxsom/*.sock`, `/run/fips/*`),
  - Unmounts tmpfs filesystems before the journald shutdown message is emitted.
- **F24.** Shutdown MUST NOT write any new files to the boot media.
- **F25.** Emergency shutdown (power loss, crash) MUST still leave the boot media indistinguishable from its pre-boot state, because nothing was ever written during normal operation (F8).

### 4.6 Image build and reproducibility

- **F26.** The image build MUST be deterministic given a fixed commit hash of this repo and its submodules. Two builds from the same inputs produce byte-identical ISOs (modulo embedded timestamps, which are pinned via `SOURCE_DATE_EPOCH`).
- **F27.** No built-in user account may have a password. If a `live` user is created by live-build, it is configured for auto-login on TTY only, with NOPASSWD sudo explicitly scoped (or disabled entirely in favor of root TTY + identity-agent-gated sudo).
- **F28.** The image MUST NOT phone home at build time or at runtime for telemetry, update checks, or crash reports. Zero outbound connections initiated by the OS itself (as distinct from services explicitly started by the user).
- **F29.** The image MUST NOT ship SSH host keys. If SSH is enabled, host keys are generated at first boot and exist only in tmpfs; reboot yields fresh host keys.
- **F30.** Every n-OS-tr C binary we ship (identity-agent, TUI, config-loader, config-writer, smoketest helpers, anything we write ourselves) MUST be **C99, built inside Alpine Docker, statically linked against musl libc**. `ldd <binary>` on a shipped artifact MUST print "not a dynamic executable". Rationale: smaller runtime trust surface (no `.so` resolution at runtime), identical behavior across amd64 and arm64 flavors, and alignment with [`includes/nostr_core_lib`](../includes/nostr_core_lib/) / [`includes/ginxsom`](../includes/ginxsom/) / [`includes/c-relay`](../includes/c-relay/). See [`plans/tui_login.md §0.1`](tui_login.md#01-toolchain-alpine--musl-dev-built-in-docker).
- **F31.** No Python, Node.js, or other interpreter runtime may be installed in the image on the critical boot path (identity entry, config projection, privacy-hardener). Interpreters MAY be available as user-installed software after a session is live, but the OS itself boots without them.

## 5. Shutdown contract

On `systemctl poweroff`, the following MUST hold before the kernel halts:

| Invariant                                                          | Enforcement                                     |
|--------------------------------------------------------------------|-------------------------------------------------|
| No plaintext mnemonic in any mapped memory page                    | identity-agent zero-on-exit + shutdown-wiper    |
| No derived private key in any mapped memory page                   | identity-agent zero-on-exit                     |
| No writes to the boot media since boot                             | Read-only mount + overlayfs topology (F8)        |
| No journal entries written to disk                                 | `Storage=volatile` (F10)                         |
| No residual Unix sockets with auth material in `/run/*`            | shutdown-wiper removes + zeroes                 |
| No dumpable core files, no crash reports queued                    | `ulimit -c 0` default + no `systemd-coredump` on disk |
| No Wi-Fi state files in NM profiles / `/etc/NetworkManager`        | Verified by CI grep (F17)                        |

## 6. Out-of-scope but worth documenting

- **The mnemonic input channel.** On the amd64 ISO, the user types the phrase into the keyboard attached to the machine. A keylogger on that machine defeats this. On the Pi Zero 2 W flavor, entry may be via the Waveshare LCD HAT buttons, reducing (not eliminating) shoulder-surfing risk. Neither is a substitute for physical security.
- **Randomness quality.** If a machine has no hardware RNG and a broken `/dev/urandom` seed, BIP-39 *generation* (not derivation) could produce weak mnemonics. Recommendation: use `nostr-id generate --dice` or an external hardware source when generating new identities on machines you don't fully trust.
- **Software supply chain.** We inherit Debian's package supply chain. A compromised upstream package compromises the image. Reproducible builds (F26) partially mitigate this but do not eliminate it.

## 7. What this document is **not**

- Not a design doc for *how* each property is enforced. See [`plans/identity_subsystem.md`](identity_subsystem.md), [`plans/nostr_config_projection.md`](nostr_config_projection.md), and future `plans/privacy_hardener.md` for mechanism-level detail.
- Not a conformance checklist for applications the user installs themselves. A user running Firefox can still be fingerprinted by Firefox — that's Firefox's problem, not the OS's.
- Not a promise that the current Phase 2 Slice B+C ISO already satisfies everything here. Most of these guarantees become real in **Phase 3** (identity + config + hardener). Phase 2 is the plumbing; Phase 3 is the promise.

## 8. Revision policy

Any PR that weakens a guarantee (removes an F-item, adds an exception, broadens an allowed behavior) must:

1. Update this document to reflect the new guarantee.
2. Update the corresponding smoke-test check, so behavior drift is caught by CI.
3. Include an explicit rationale in the PR description.

Any PR that strengthens a guarantee is always welcome and should update this document to match.
