# Slice B + C: Add fips and c-relay to the n-OS-tr ISO ## Status: **Ready for implementation** This document specifies the work required to enable the [fips](../includes/fips) mesh-routing daemon and the [c-relay](../includes/c-relay) Nostr relay on the live ISO built by [`build.sh`](../build.sh), replacing the current Slice A (ginxsom + nginx only) with the full three-service stack planned in [`iso_architecture.md`](./iso_architecture.md). --- ## 1. Design Decisions (captured from user) | # | Topic | Decision | |---|---|---| | 1 | Source of c-relay binary | User-provided, already downloaded into [`includes/c-relay/`](../includes/c-relay) | | 2 | Source of fips binaries | Build locally via `cargo deb` during `build.sh` | | 3 | c-relay admin nsec strategy | **Ephemeral** — c-relay self-provisions per boot, admin nsec captured from journald | | 4 | fips identity | **Ephemeral** — new npub per boot, no persistent key management for this slice | | 5 | fips peer config | Connect to the same public bootstrap node used elsewhere in the repo: `npub1qmc3cvfz0yu2hx96nq3gp55zdan2qclealn7xshgr448d3nh6lks7zel98` at `217.77.8.91:2121` (UDP), per [`examples/sidecar-nostr-relay/.env`](../includes/fips/examples/sidecar-nostr-relay/.env:9) et al. | | 6 | fips transport | **Ethernet** enabled, plus UDP for the static peer | | 7 | fips-gateway | Disabled (default) | | 8 | c-relay exposure | nginx reverse-proxies `wss://host/relay` → `ws://127.0.0.1:8888/`; nginx reverse-proxies `/admin/` → `http://127.0.0.1:8888/api/` | | 9 | Scope | Full Slice B+C — services, nginx wiring, package deps, smoke test, MOTD, README updates | | 10 | Key management / identity continuity | Out of scope for this slice — revisit in a later pass | --- ## 2. What Already Exists (Slice A baseline) Confirmed present and working, per source inspection: - [`iso/auto/config`](../iso/auto/config) — live-build config - [`iso/config/package-lists/base.list.chroot`](../iso/config/package-lists/base.list.chroot) — nginx-light, spawn-fcgi, ca-certificates, curl, jq, vim, htop - [`iso/config/includes.chroot/etc/systemd/system/ginxsom.service`](../iso/config/includes.chroot/etc/systemd/system/ginxsom.service) - [`iso/config/includes.chroot/etc/systemd/system/n-os-tr-firstboot.service`](../iso/config/includes.chroot/etc/systemd/system/n-os-tr-firstboot.service) - [`iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf`](../iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf) — `:443` TLS, Blossom routing, static landing page - [`iso/config/includes.chroot/usr/local/sbin/n-os-tr-firstboot`](../iso/config/includes.chroot/usr/local/sbin/n-os-tr-firstboot) — generates ginxsom nsec + self-signed TLS - [`iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest`](../iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest) — 5 PASS + 8 SKIP placeholder - [`iso/config/includes.chroot/usr/local/bin/nak`](../iso/config/includes.chroot/usr/local/bin/nak) — Nostr CLI helper - [`iso/config/includes.chroot/usr/local/bin/ginxsom/ginxsom-fcgi`](../iso/config/includes.chroot/usr/local/bin/ginxsom/ginxsom-fcgi) — Blossom FastCGI binary - [`iso/config/hooks/live/0020-enable-services.hook.chroot`](../iso/config/hooks/live/0020-enable-services.hook.chroot) — enables nginx, ginxsom, firstboot Slice A smoke test passes `5 PASS, 0 FAIL, 8 SKIP` on a freshly-booted USB. --- ## 3. What Needs to Change (target state) ```mermaid flowchart LR subgraph Build [Build-time build.sh] BS[build.sh] CDB[cargo deb in
includes/fips] FDeb[iso/local-artifacts/fips_0.3.0_amd64.deb] CRX[includes/c-relay/c_relay_x86] CRC[iso/local-artifacts/c_relay_x86] BS --> CDB --> FDeb BS --> CRC end subgraph Chroot [live-build chroot phase] H1[0010-fetch-artifacts.hook.chroot] FDeb --> H1 CRC --> H1 H1 -->|dpkg -i| FInst[/usr/bin/fips etc] H1 -->|install -m 0755| CInst[/opt/c-relay/c_relay_x86] H1 -->|adduser --system| CUsr[c-relay system user] H1 -->|write fips.yaml| FCfg[/etc/fips/fips.yaml] end subgraph Boot [Runtime at boot] Fb[n-os-tr-firstboot] Fips[fips.service] FDns[fips-dns.service] CRel[c-relay.service] Gx[ginxsom.service] Ng[nginx.service] Fb --> Gx Fips --> FDns Ng -->|wss /relay| CRel Ng -->|http /admin/| CRel end ``` ### Summary of changes | Area | Change | |---|---| | Build system | [`build.sh`](../build.sh) gains a pre-build step: build fips `.deb` via `cargo deb` and stage artifacts into [`iso/local-artifacts/`](../iso/local-artifacts) | | Artifact layout | New directory [`iso/local-artifacts/`](../iso/local-artifacts) holds `fips_*_amd64.deb` and `c_relay_x86` — gitignored | | Chroot hook (new) | [`iso/config/hooks/live/0010-fetch-artifacts.hook.chroot`](../iso/config/hooks/live/0010-fetch-artifacts.hook.chroot) installs those two artifacts into the chroot | | Package list | [`iso/config/package-lists/base.list.chroot`](../iso/config/package-lists/base.list.chroot) adds runtime deps: `libdbus-1-3`, `iproute2`, `nftables` (optional for gateway mode later) | | fips config | New [`iso/config/includes.chroot/etc/fips/fips.yaml`](../iso/config/includes.chroot/etc/fips/fips.yaml) overriding the package default with ethernet + static UDP peer enabled | | c-relay service | New [`iso/config/includes.chroot/etc/systemd/system/c-relay.service`](../iso/config/includes.chroot/etc/systemd/system/c-relay.service) — adapted from [upstream unit](../includes/c-relay/systemd/c-relay.service) | | c-relay user | Chroot hook creates `c-relay` system user + `/opt/c-relay/` working dir | | nginx config | [`iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf`](../iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf) gains `/relay` wss upgrade and `/admin/` proxy locations | | Service-enable hook | [`0020-enable-services.hook.chroot`](../iso/config/hooks/live/0020-enable-services.hook.chroot) also enables `fips.service`, `fips-dns.service`, `c-relay.service` | | Smoke test | [`n-os-tr-smoketest`](../iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest) replaces the 8 SKIPs with real checks | | MOTD / README | First-boot script and docs updated to surface how to find c-relay admin nsec and fips identity | | `.gitignore` | Add `iso/local-artifacts/` | --- ## 4. Concrete Artifact Details ### 4.1 c-relay static binary - **Source:** [`includes/c_relay_static_x86_64`](../includes/c_relay_static_x86_64) — user-downloaded static-musl build, already present at the top level of `includes/` (NOT inside `includes/c-relay/`). Filename confirmed by directory listing. - **Staged as:** `iso/config/includes.chroot_before_packages/live-artifacts/c_relay_x86` (canonical name used by the chroot hook and systemd unit — we rename on copy so `c-relay.service` can reference a stable filename regardless of which upstream build variant the user drops in). - **Target in chroot:** `/opt/c-relay/c_relay_x86`, mode `0755`, owner `c-relay:c-relay`. - **Copied by:** `build.sh` copies [`includes/c_relay_static_x86_64`](../includes/c_relay_static_x86_64) → staging area → chroot hook installs to `/opt/c-relay/`. ### 4.2 `iso/local-artifacts/fips_0.3.0_amd64.deb` - **Source:** built by `build.sh` via `cd includes/fips && packaging/debian/build-deb.sh`. Requires `cargo-deb` installed on the build host (`cargo install cargo-deb` if absent; `build.sh` will check and fail clearly). - **Contents (per [`Cargo.toml`](../includes/fips/Cargo.toml:70-83)):** - `/usr/bin/fips`, `/usr/bin/fipsctl`, `/usr/bin/fipstop`, `/usr/bin/fips-gateway` - `/etc/fips/fips.yaml` (package default — will be overridden by our includes.chroot version) - `/etc/fips/hosts` - `/lib/systemd/system/fips.service`, `/lib/systemd/system/fips-dns.service`, `/lib/systemd/system/fips-gateway.service` - `/usr/lib/fips/fips-dns-setup`, `/usr/lib/fips/fips-dns-teardown` - `/usr/lib/tmpfiles.d/fips.conf` - **Runtime deps (per Cargo.toml):** `libc6, systemd, libdbus-1-3`, recommends `bluez` (skipped — not needed for non-BLE). - **Installed by:** chroot hook runs `dpkg -i /tmp/artifacts/fips_*_amd64.deb` then cleans up. The postinst enables `fips.service` and `fips-dns.service` automatically. ### 4.3 `iso/config/includes.chroot/etc/fips/fips.yaml` Overrides the Debian-packaged default at `/etc/fips/fips.yaml`. The live-build `includes.chroot` mechanism copies this on top after package install. Content: ```yaml # n-OS-tr fips configuration. # Ephemeral identity: new keypair every boot (default). # To make identity persistent across reboots on a live USB with a # persistence partition, set node.identity.persistent: true. node: identity: # ephemeral by default tun: enabled: true name: fips0 mtu: 1280 dns: enabled: true port: 5354 transports: udp: bind_addr: "0.0.0.0:2121" tcp: bind_addr: "0.0.0.0:8443" ethernet: # Ethernet transport auto-discovers LAN peers. # interface: omitted → fips picks the first non-loopback interface. # Override with an explicit interface name if your board uses # something other than the default (e.g. enp5s0, eno1, eth0). discovery: true announce: true auto_connect: true accept_connections: true # Static peer for internet bootstrap. # Matches the same "fips-test-node" / "vps-chi" public node used across # the repo's example configs (see includes/fips/examples/... and # includes/fips/testing/sidecar/.env). peers: - npub: "npub1qmc3cvfz0yu2hx96nq3gp55zdan2qclealn7xshgr448d3nh6lks7zel98" alias: "fips-test-node" addresses: - transport: udp addr: "217.77.8.91:2121" connect_policy: auto_connect ``` Notes: - Omitting `ethernet.interface` relies on fips's auto-selection logic. If that doesn't pick a sensible interface, add `interface: "eth0"` (verified post-boot via `ip link show`). - Keeping the `udp` bootstrap peer in addition to `ethernet` gives the mesh internet reachability even when the LAN has no other fips peers. ### 4.4 `iso/config/includes.chroot/etc/systemd/system/c-relay.service` Adapted from [upstream](../includes/c-relay/systemd/c-relay.service) with minor tweaks for the live environment: ```ini [Unit] Description=C Nostr Relay (n-OS-tr slice C) Documentation=file:///usr/share/doc/n-os-tr/README After=network.target n-os-tr-firstboot.service Wants=network-online.target [Service] Type=simple User=c-relay Group=c-relay WorkingDirectory=/opt/c-relay Environment=DEBUG_LEVEL=0 # c-relay self-provisions admin + relay keys on first start and prints # the admin nsec to stdout (captured by journald). ExecStart=/opt/c-relay/c_relay_x86 --debug-level=${DEBUG_LEVEL} Restart=always RestartSec=5 StandardOutput=journal StandardError=journal SyslogIdentifier=c-relay # Hardening NoNewPrivileges=true ProtectSystem=strict ProtectHome=true ReadWritePaths=/opt/c-relay PrivateTmp=true ProtectKernelTunables=true ProtectKernelModules=true ProtectControlGroups=true RestrictAddressFamilies=AF_INET AF_INET6 LimitNOFILE=65536 LimitNPROC=4096 [Install] WantedBy=multi-user.target ``` ### 4.5 Updated nginx site — `/relay` wss + `/admin/` proxy New locations appended to [`iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf`](../iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf) inside the existing `server { listen 443 ssl; ... }` block, before the ginxsom FastCGI blocks: ```nginx # --- c-relay reverse proxy (slice C) --------------------------------- # Nostr relay WebSocket: wss://host/relay → ws://127.0.0.1:8888/ # The client library expects a WebSocket upgrade on /relay; c-relay # itself listens on "/" so we rewrite and proxy to its root. location = /relay { proxy_pass http://127.0.0.1:8888/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 86400; proxy_send_timeout 86400; } # NIP-11 relay info doc (available at wss://host/relay with # Accept: application/nostr+json). Proxied to the same backend. # If you want /relay (GET without upgrade) to return NIP-11 too, # the location above already handles it — c-relay's NIP-11 handler # triggers based on the Accept header, not the path. # c-relay embedded web admin UI: https://host/admin/ → http://127.0.0.1:8888/api/ location /admin/ { proxy_pass http://127.0.0.1:8888/api/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } ``` Notes: - The `/admin/` path deliberately overlaps with a Blossom admin path, but Blossom's admin routes go via the FastCGI `@ginxsom` named location and c-relay's admin is explicitly reverse-proxied — nginx's longest-prefix match means `/admin/` → c-relay wins for GET/POST, and ginxsom admin is reached via a different path (if ginxsom has one). **Potential conflict with Slice A** — need to confirm ginxsom doesn't rely on `/admin/`. Current [n-os-tr.conf:53](../iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf:53) has `location /admin/ { try_files $uri @ginxsom; }` — this will need to be **removed or renamed** (e.g. `/blossom-admin/`) to avoid collision. See §6 Open Questions. - The landing page at `/` (static `/var/www/html/index.html`) is unaffected. ### 4.6 New chroot hook — `0010-fetch-artifacts.hook.chroot` ```sh #!/bin/sh # Install c-relay and fips into the chroot from iso/local-artifacts/. set -e ART=/live-artifacts if [ ! -d "$ART" ]; then echo "[fetch-artifacts] no $ART; skipping" >&2 exit 0 fi # --- c-relay ----------------------------------------------------------- if [ -f "$ART/c_relay_x86" ]; then echo "[fetch-artifacts] installing c-relay binary" # Create system user and directory if ! getent passwd c-relay >/dev/null 2>&1; then adduser --system --no-create-home --group --home /opt/c-relay c-relay fi mkdir -p /opt/c-relay install -m 0755 -o c-relay -g c-relay \ "$ART/c_relay_x86" /opt/c-relay/c_relay_x86 chown -R c-relay:c-relay /opt/c-relay else echo "[fetch-artifacts] WARNING: $ART/c_relay_x86 not found" >&2 fi # --- fips -------------------------------------------------------------- FIPS_DEB=$(ls "$ART"/fips_*_amd64.deb 2>/dev/null | head -1 || true) if [ -n "$FIPS_DEB" ] && [ -f "$FIPS_DEB" ]; then echo "[fetch-artifacts] installing fips from $FIPS_DEB" dpkg -i "$FIPS_DEB" || apt-get -f install -y else echo "[fetch-artifacts] WARNING: no fips_*_amd64.deb in $ART" >&2 fi ``` How `$ART` gets populated in the chroot: live-build copies [`iso/local-artifacts/`](../iso/local-artifacts) into `/live-artifacts/` inside the chroot via an `includes.chroot_before_packages/` entry that bind-mounts or symlinks the directory, OR more simply, we place the artifacts directly under `iso/config/includes.chroot_before_packages/live-artifacts/` so live-build copies them before package install. See §5 step 2 for the exact location. ### 4.7 Updated `build.sh` Inserted **before** the `sudo "$REPO_ROOT/lb-wrapper.sh" config` line: ```bash # --- Stage Slice B artifacts ------------------------------------------------ ARTIFACTS="$WORKSPACE_ISO_DIR/config/includes.chroot_before_packages/live-artifacts" mkdir -p "$ARTIFACTS" # 1. c-relay: copy the user-provided binary from includes/c-relay/ CRELAY_SRC="" for candidate in \ "$REPO_ROOT/includes/c-relay/c_relay_x86" \ "$REPO_ROOT/includes/c-relay/build/c_relay_x86" \ "$REPO_ROOT/includes/c-relay/c-relay" ; do if [[ -x "$candidate" ]]; then CRELAY_SRC="$candidate"; break; fi done if [[ -z "$CRELAY_SRC" ]]; then echo "[build.sh] ERROR: no c-relay binary found. Looked for:" >&2 echo " includes/c-relay/c_relay_x86" >&2 echo " includes/c-relay/build/c_relay_x86" >&2 echo " includes/c-relay/c-relay" >&2 exit 1 fi cp "$CRELAY_SRC" "$ARTIFACTS/c_relay_x86" chmod 0755 "$ARTIFACTS/c_relay_x86" echo "[build.sh] staged c-relay from $CRELAY_SRC" # 2. fips: build the .deb (requires cargo-deb) if ! command -v cargo-deb >/dev/null 2>&1; then echo "[build.sh] ERROR: cargo-deb not installed. Run: cargo install cargo-deb" >&2 exit 1 fi pushd "$REPO_ROOT/includes/fips" >/dev/null packaging/debian/build-deb.sh popd >/dev/null FIPS_DEB=$(ls "$REPO_ROOT/includes/fips/deploy"/fips_*_amd64.deb | sort | tail -1) if [[ -z "$FIPS_DEB" || ! -f "$FIPS_DEB" ]]; then echo "[build.sh] ERROR: fips .deb build produced no artifact" >&2 exit 1 fi cp "$FIPS_DEB" "$ARTIFACTS/" echo "[build.sh] staged fips .deb: $(basename "$FIPS_DEB")" ``` ### 4.8 Updated package list — `base.list.chroot` Add runtime deps needed by fips (already pulled in transitively by the .deb but explicit is safer) and by the smoke test: ``` nginx-light spawn-fcgi libfcgi-bin ca-certificates openssl curl jq vim htop # Slice B/C additions: libdbus-1-3 iproute2 nftables ``` ### 4.9 Updated `0020-enable-services.hook.chroot` ```sh #!/bin/sh set -e # Nginx rm -f /etc/nginx/sites-enabled/default ln -sf /etc/nginx/sites-available/n-os-tr.conf /etc/nginx/sites-enabled/n-os-tr.conf systemctl enable nginx.service # Slice A systemctl enable ginxsom.service systemctl enable n-os-tr-firstboot.service # Slice B (fips) — units installed by the .deb postinst already enable # them, but we're explicit for idempotence: systemctl enable fips.service || true systemctl enable fips-dns.service || true # Slice C (c-relay) systemctl enable c-relay.service ``` ### 4.10 Updated smoke test Replace the fips and c-relay SKIP blocks in [`n-os-tr-smoketest`](../iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest) with real checks: ```bash # -------------------- fips mesh daemon ----------------------------- section "fips mesh daemon" check "fips.service active" systemctl is-active fips.service check "fips-dns.service active" systemctl is-active fips-dns.service check "fips0 interface present" ip link show fips0 # fipsctl identity should return an npub-shaped string npub=$(fipsctl identity 2>/dev/null | head -1 | tr -d '[:space:]') case "$npub" in npub1*) pass "fipsctl identity returns npub" "${npub:0:20}..." ;; *) fail "fipsctl identity returns npub" "got: ${npub:-}" ;; esac # fips UDP transport should be listening check "fips UDP bound on :2121" bash -c 'ss -ulnp 2>/dev/null | grep -q ":2121"' # -------------------- c-relay nostr relay -------------------------- section "c-relay nostr relay" check "c-relay.service active" systemctl is-active c-relay.service check "c-relay listening on :8888" bash -c 'ss -tlnp 2>/dev/null | grep -q ":8888"' # The admin nsec should have been logged during first startup if journalctl -u c-relay.service --no-pager 2>/dev/null | \ grep -q "Admin Private Key:"; then pass "admin nsec logged in journal" "found" else fail "admin nsec logged in journal" "grep miss" fi # wss end-to-end via nginx, using the shipped nak: if timeout 10 nak req --relay wss://localhost/relay -k 1 -l 1 \ >/dev/null 2>&1; then pass "wss://localhost/relay answers REQ" "ok" else fail "wss://localhost/relay answers REQ" "timeout or error" fi # NIP-11 relay info doc nip11=$(curl -sk -H 'Accept: application/nostr+json' \ https://localhost/relay 2>/dev/null) if echo "$nip11" | jq -e '.name' >/dev/null 2>&1; then pass "NIP-11 info doc reachable" "name=$(echo "$nip11" | jq -r .name)" else fail "NIP-11 info doc reachable" "no JSON .name" fi # c-relay embedded admin UI via nginx status=$(curl -sk -o /dev/null -w '%{http_code}' https://localhost/admin/ 2>/dev/null) case "$status" in 200|302|401) pass "admin UI reachable via nginx" "HTTP $status" ;; *) fail "admin UI reachable via nginx" "HTTP $status" ;; esac ``` Target final summary: `~15 PASS / 0 FAIL / 0 SKIP`. ### 4.11 MOTD and README updates The [`n-os-tr-firstboot`](../iso/config/includes.chroot/usr/local/sbin/n-os-tr-firstboot) script's MOTD block gains additional hints: ``` n-OS-tr — Nostr-addressed mesh node (preview) Ginxsom (Blossom) server pubkey (hex): $GINXSOM_SERVER_PUBKEY retrieve nsec: cat /var/lib/n-os-tr/keys/ginxsom.nsec c-relay Nostr relay: wss://$(hostname)/relay (admin nsec: journalctl -u c-relay | grep 'Admin Private Key') https://$(hostname)/admin/ (web admin UI) fips mesh daemon: fipsctl identity (show npub) fipsctl peers (list peers) Quick health check: n-os-tr-smoketest ``` The [`iso/config/includes.chroot/usr/share/doc/n-os-tr/README`](../iso/config/includes.chroot/usr/share/doc/n-os-tr/README) gets corresponding additions. --- ## 5. Build & Deploy Sequence 1. **Place c-relay binary** at `includes/c-relay/c_relay_x86` (or one of the fallback paths `build.sh` checks). Ensure it's `chmod +x` and runs standalone. 2. **Install `cargo-deb`** on the build host: `cargo install cargo-deb`. 3. **Edit** all files listed in §3 according to §4. 4. **Move artifacts staging location:** - New directory: `iso/config/includes.chroot_before_packages/live-artifacts/` — gitignored. - `build.sh` writes c-relay binary and fips .deb here. - `0010-fetch-artifacts.hook.chroot` reads from `/live-artifacts/` inside the chroot (live-build copies the `includes.chroot_before_packages` tree into the chroot root before packages install). 5. **Add** `iso/config/includes.chroot_before_packages/live-artifacts/` to [`.gitignore`](../.gitignore). 6. **Full clean build:** `sudo ./build.sh --clean` (needed because package list changed). 7. **Burn** the resulting `iso/live-image-amd64.hybrid.iso` to USB with `dd`. 8. **Boot** on the B650I machine. 9. **Verify:** run `n-os-tr-smoketest` — expect all checks to PASS. --- ## 6. Open Questions / Known Conflicts ### 6.1 nginx `/admin/` path collision Slice A's [n-os-tr.conf:53](../iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf:53) routes `/admin/` to ginxsom. Slice C wants `/admin/` for c-relay. **Resolution proposed:** change Slice A's Blossom admin to `/blossom/` (if ginxsom supports path rewriting) or `/blossom-admin/`. Need to check ginxsom's source to see whether its admin endpoints are hardcoded to `/admin/` or whether the FastCGI layer can remap them. ### 6.2 Fips ethernet auto-interface selection If the B650I's Ethernet shows up as `enp5s0` or similar and fips's "auto-pick first non-loopback" heuristic grabs the wrong interface (e.g. Wi-Fi — though we explicitly said runtime Wi-Fi is fine, we don't want fips scanning it), we may need to hardcode `ethernet.interface: "enp5s0"` in [fips.yaml](../iso/config/includes.chroot/etc/fips/fips.yaml). **Resolution:** check `ip link show` on first boot; update if needed. ### 6.3 Stray file `=4` in [`includes/c-relay/`](../includes/c-relay) Shown in the directory listing but not the c-relay binary (the real binary is [`includes/c_relay_static_x86_64`](../includes/c_relay_static_x86_64) at the top level of `includes/`). Likely a shell-redirect typo artifact (`command >=4 file` or similar). Safe to delete during cleanup; does not affect the build. ### 6.4 c-relay writable database location c-relay writes `.nrdb` files to its `WorkingDirectory` (`/opt/c-relay/`). In a live session with ephemeral rootfs this works — the writes go to the tmpfs union layer. For persistence-partition boots we'd want `/opt/c-relay/` on the persistence mount. **Not addressed in this slice** per decision #10. ### 6.5 fips running as root The fips Debian package runs fips as root (needs TUN + raw sockets). The hardening in [`fips.service`](../includes/fips/packaging/debian/fips.service:17-22) is meaningful but fips itself is privileged. Acceptable for this slice. ### 6.6 Reverse proxy's X-Forwarded-Proto and c-relay's NIP-42 NIP-42 requires the relay to know its own URL (for the `["relay", "url"]` tag in the AUTH challenge). c-relay has [logic](../includes/c-relay/src/nip042.c:103) that falls back to `ws://127.0.0.1:PORT` — this means NIP-42 challenges will reference the internal address, not `wss://host/relay`. Clients may then fail auth. **Mitigation:** c-relay has a configurable `relay_url` config key (set via admin event). Post-boot procedure documented in updated README for users who want NIP-42 to work correctly. **Not blocking for basic operation** — the relay accepts unauthenticated connections by default. --- ## 7. Success Criteria From a booted USB on fresh hardware with wired Ethernet: - [ ] `systemctl status fips-dns ginxsom c-relay nginx n-os-tr-firstboot` — all active - [ ] `fipsctl identity` — prints a valid npub - [ ] `fipsctl peers` — at minimum lists `fips-test-node` (may show as "connecting" if UDP blocked) - [ ] `ip link show fips0` — fips0 TUN interface up - [ ] `journalctl -u c-relay | grep 'Admin Private Key'` — returns the nsec (once) - [ ] `curl -k https://localhost/` — landing page - [ ] `curl -k -I https://localhost/admin/` — returns 200 or 302 (c-relay UI) - [ ] `nak req --relay wss://localhost/relay -k 1 -l 1` — returns `["EOSE",...]` after an empty result - [ ] `curl -ksI https://localhost/0000000000000000000000000000000000000000000000000000000000000000` — 404 (ginxsom still works) - [ ] `n-os-tr-smoketest` — `15 PASS / 0 FAIL / 0 SKIP` --- ## 8. Rollback If something goes sideways during development: - The entire Slice A → Slice B+C diff is confined to these files/directories: ``` build.sh (additive step) iso/config/hooks/live/0010-fetch-artifacts.hook.chroot (new) iso/config/hooks/live/0020-enable-services.hook.chroot (edited) iso/config/package-lists/base.list.chroot (edited) iso/config/includes.chroot/etc/fips/ (new dir) iso/config/includes.chroot/etc/systemd/system/c-relay.service (new) iso/config/includes.chroot/etc/nginx/sites-available/n-os-tr.conf (edited) iso/config/includes.chroot/usr/local/sbin/n-os-tr-firstboot (edited motd) iso/config/includes.chroot/usr/local/bin/n-os-tr-smoketest (edited) iso/config/includes.chroot/usr/share/doc/n-os-tr/README (edited) iso/config/includes.chroot_before_packages/ (new, gitignored) .gitignore (edited) ``` - A revert is a `git checkout` of those paths plus removal of `iso/config/includes.chroot_before_packages/`. --- ## 9. Out of Scope (deferred to later slices) - Persistent identity (ginxsom nsec, c-relay admin nsec, fips npub survive reboots). - Gitea release fetch for artifacts (current plan is local-only). - c-relay `relay_url` auto-configuration for NIP-42 (manual admin event for now). - `/admin/` conflict resolution if ginxsom truly hardcodes that path. - fips-gateway service (disabled; users can enable manually for LAN translation). - Tor transport for fips (transport is available but not wired into the config). - Installer variant (live-only for now). - arm64 build. - Signed / reproducible builds. - Hardening tests: SELinux, AppArmor profiles. --- ## 10. References - [`plans/iso_architecture.md`](./iso_architecture.md) — overall target design - [`plans/network_boot.md`](./network_boot.md) — sibling plan, paused - [`build.sh`](../build.sh) / [`lb-wrapper.sh`](../lb-wrapper.sh) — build entry - [`iso/auto/config`](../iso/auto/config) — live-build options - [`includes/fips/packaging/debian/build-deb.sh`](../includes/fips/packaging/debian/build-deb.sh) - [`includes/fips/packaging/common/fips.yaml`](../includes/fips/packaging/common/fips.yaml) - [`includes/fips/packaging/debian/fips.service`](../includes/fips/packaging/debian/fips.service) - [`includes/c-relay/systemd/c-relay.service`](../includes/c-relay/systemd/c-relay.service) - [`includes/c-relay/README.md`](../includes/c-relay/README.md) - [`includes/c-relay/API.md`](../includes/c-relay/API.md) - [Live-build manual — includes.chroot_before_packages](https://live-team.pages.debian.net/live-manual/html/live-manual/customizing-contents.en.html)