diff --git a/docs/adr/004-tor-for-peer-communication.md b/docs/adr/004-tor-for-peer-communication.md index b007e846..55b77cbd 100644 --- a/docs/adr/004-tor-for-peer-communication.md +++ b/docs/adr/004-tor-for-peer-communication.md @@ -1,6 +1,7 @@ # ADR-004: Tor Hidden Services for Peer Communication -**Status**: Accepted +**Status**: Accepted (2026-03) — **partially superseded in practice, see +Amendment below** **Date**: 2026-03 ## Context @@ -33,3 +34,27 @@ Use Tor hidden services (.onion addresses) for all inter-node communication. - Implement retry with backoff for Tor connections - Container `archy-tor` runs automatically with host networking for hidden service access - Federation sync interval (5 min) tolerates occasional connection failures + +## Amendment (recorded 2026-08) + +Two things in this ADR no longer describe the system. Both changes happened +without their own ADR, which is itself worth noting. + +**1. Tor is no longer used for *all* inter-node communication — it is the last +fallback.** The transport layer now tries, in order, mesh radio → LAN → FIPS +overlay → Tor (`transport::TransportKind`, priority 1–4). The latency and +bandwidth costs listed above are exactly why: FIPS was introduced to carry WAN +peering that Tor made too slow, and direct LAN peering skips the overlay +entirely for co-located nodes. Tor's NAT-traversal and IP-privacy properties are +still what make it the dependable floor when the others are unavailable. + +**2. Tor does not run as the `archy-tor` container.** It is the host's Debian +`tor` package, running as `debian-tor` and driven by the +`archipelago-tor-helper` path unit (`scripts/tor-helper.sh`), which installs a +staged `/etc/tor/torrc` and restarts the service. The migration was deliberate +and is still enforced: `scripts/container-doctor.sh` removes an `archy-tor` +container if it finds one and switches the node to system Tor. There is no +`apps/tor` manifest. + +The decision to use onion services for peer reachability stands; only its +exclusivity and its packaging changed. diff --git a/docs/adr/009-manifest-container-security.md b/docs/adr/009-manifest-container-security.md index 31de074b..4e27433a 100644 --- a/docs/adr/009-manifest-container-security.md +++ b/docs/adr/009-manifest-container-security.md @@ -70,6 +70,28 @@ Some apps legitimately need elevated privileges: - Build-time validation catches issues before deployment - Override mechanism for legitimate exceptions (with audit trail) +## Implementation status + +The decision above stands; this section records how much of it is actually +enforced today, because the "non-negotiable" table overstates it. Verified +against `core/container/src/manifest.rs` and `core/security/src/`: + +| Constraint | Reality | +|---|---| +| `capabilities` drop-all + allow-list | ✅ **Enforced.** A capability outside the nine-entry allow-list is a parse error, so the app cannot install | +| Bind-mount confinement | ✅ **Enforced** (stronger than this ADR describes): sources must be under `/var/lib/archipelago/`, a named volume, or one of two reviewed exceptions | +| `readonly_root` / `no_new_privileges` | ◐ **Defaults, not gates.** Both default to `true` when omitted, but `validate_security()` does not reject an explicit `false` — the "reject manifests that violate mandatory defaults" step does not exist | +| `image_tag` pinned | ◐ **Preflight only.** `scripts/validate-app-manifest.sh` grades it; the parser accepts `:latest` and the app installs | +| `user` UID > 1000 | ❌ **Not validated.** The runtime manifest parser has no UID check at all (the marketplace schema has an advisory one, which is a different type) | +| `seccomp_profile` | ❌ **Does not exist.** The string `seccomp` appears nowhere in `core/` — not as code, not as a TODO | +| AppArmor | ❌ **Inert.** `container_policies.rs` can generate and `apparmor_parser -r` a profile, but its own comment says `TODO: Configure Podman to use the profile`. `security.apparmor_profile` is parsed into a manifest field that nothing ever reads | + +So the accurate summary is: capability and mount confinement are hard gates, +the process-hardening flags are safe-by-default rather than enforced, and the +kernel-level sandboxing (seccomp/AppArmor) named in the decision was never +wired up. Closing the last two rows is tracked in +[`1.8.0-RELEASE-HARDENING-PLAN.md`](../1.8.0-RELEASE-HARDENING-PLAN.md). + ## References - `docs/app-manifest-spec.md` — Full manifest specification