Files
archy/apps/router/manifest.yml
T
archipelagoandClaude Opus 5 0c4826f8cc feat(security): declare which app ports may skip authentication
Groundwork for the app gate (item 1): before anything can enforce
authentication on app ports, the node has to know which ports are
*supposed* to be reachable without it.

`PortMapping` grows `auth` (PortAuth::Session | None, defaulting to
Session) and `auth_rationale`. The default is deliberately the protected
one. Every app port on this node answered with no credential at all over
LAN, Tailscale, Tor and the FIPS mesh alike — reproduced 2026-08-03 —
precisely because exposure was what you got by saying nothing. Inverting
the default means a new app is protected unless its manifest argues for
an exemption.

Validation makes the argument mandatory: `auth: none` without a
rationale is rejected, and so is a rationale without `auth: none` (that
combination means the author wrote an exemption and did not get one —
shipping it silently would leave them believing otherwise).

17 ports across 12 apps are declared exempt, each with its reason. They
are the ports that cannot sit behind an HTTP login page at all: Lightning
p2p (BOLT-8 noise), LND gRPC/REST and CLN gRPC (macaroon / mutual TLS —
Zeus and remote wallets dial these directly), Bitcoin p2p gossip,
electrum wire protocol, Wyoming voice streams, git-over-SSH, and the UDP
discovery protocols (mDNS, SSDP, STUN). Everything else — 39 published
ports — now defaults to gated.

Bitcoin's RPC 8332 is deliberately NOT exempted: it is already
`bind: 127.0.0.1`, so the gate never sees it, and claiming an exemption
it does not need would put a line in the audit list that means nothing.
If the loopback bind is ever dropped, it fails closed.

Two corpus tests keep this honest: every shipped manifest must parse
under the new rules, and the exempt set is pinned at 17 so any change to
the node's unauthenticated surface has to be a deliberate edit.

Tests: 73/73 archipelago-container, workspace builds clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 13:51:15 -04:00

74 lines
1.8 KiB
YAML

app:
id: router
name: Mesh Router
version: 1.0.0
description: Mesh routing and local network management. Provides device discovery, routing, and network topology visualization.
container:
image: archipelago/router:1.0.0
image_signature: cosign://...
pull_policy: if-not-present
dependencies:
- storage: 500Mi
resources:
cpu_limit: 2
memory_limit: 512Mi
disk_limit: 500Mi
security:
capabilities: [NET_ADMIN, NET_RAW] # Required for network management
readonly_root: true
no_new_privileges: true
user: 1000
seccomp_profile: default
network_policy: host # Requires host network for routing
apparmor_profile: router
ports:
- host: 8084
container: 8080
protocol: tcp # Web UI
- host: 5353
container: 5353
protocol: udp # mDNS/Bonjour
auth: none
auth_rationale: >-
mDNS is UDP multicast service discovery; gating it would break .local name resolution for every device on the LAN.
- host: 1900
container: 1900
protocol: udp # SSDP
auth: none
auth_rationale: >-
SSDP/UPnP discovery is UDP multicast — there is no HTTP request to gate and no client that could hold a session.
volumes:
- type: bind
source: /var/lib/archipelago/router
target: /app/data
options: [rw]
- type: bind
source: /var/run/dbus
target: /var/run/dbus
options: [ro]
environment:
- NETWORK_INTERFACE=eth0
- MESH_ENABLED=true
- DEVICE_DISCOVERY=true
health_check:
type: http
endpoint: http://localhost:8084
path: /health
interval: 30s
timeout: 5s
retries: 3
networking:
mesh_enabled: true
local_network_access: true
device_discovery: true
routing_protocols: [olsr, babel]