docs(adr): record what ADR-009 actually enforces and amend ADR-004

**ADR-009** lists six "non-negotiable" mandatory security defaults. Checked each
against `core/container/src/manifest.rs` and `core/security/src/`:

- `seccomp_profile: Default` — the string `seccomp` appears **nowhere in
  `core/`**. Not as code, not as a TODO. This constraint is entirely fictional.
- AppArmor — `container_policies.rs` generates and `apparmor_parser -r`s a
  profile, but its own comment reads `TODO: Configure Podman to use the
  profile`. `security.apparmor_profile` parses into a manifest field that
  nothing ever reads.
- `user` UID > 1000 — no UID validation exists in the runtime parser at all.
- `image_tag` pinned — preflight script only; the parser accepts `:latest`.
- `readonly_root` / `no_new_privileges` — safe defaults when omitted, but
  `validate_security()` never rejects an explicit `false`, so the ADR's
  "Reject manifests that violate mandatory defaults" step does not exist.

Genuinely enforced: the capability allow-list and bind-mount confinement (the
latter stronger than the ADR describes). Added an Implementation status section
saying so per-row. The decision stands; the claim of enforcement did not, and on
a security ADR that gap is the whole point of writing it down.

**ADR-004** said Tor carries *all* inter-node communication and runs as the
`archy-tor` container. Neither holds: transport priority is mesh → LAN → FIPS →
Tor (`TransportKind` 1-4, Tor as last fallback, largely because of the latency
this ADR itself lists), and Tor is the host Debian service driven by
`archipelago-tor-helper` — `container-doctor.sh` actively removes an `archy-tor`
container if it finds one, and no `apps/tor` manifest exists. Added an amendment
rather than rewriting the record. Worth flagging that both changes landed
without their own ADR.

All 10 ADRs are Status: Accepted; 001-003, 005-008 and 011 verified consistent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-08 04:06:55 -04:00
co-authored by Claude Opus 5
parent 7c214e6497
commit b33138a13d
2 changed files with 48 additions and 1 deletions
+26 -1
View File
@@ -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 14). 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.
@@ -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