From d3e3a3d35a7d97f6ad19afe123d9ae1ce3121ab2 Mon Sep 17 00:00:00 2001 From: Laan Tungir Date: Tue, 21 Jul 2026 13:15:53 -0400 Subject: [PATCH] . --- .gitignore | 1 - README.md | 192 --------- configs/laantungir/.gitignore | 2 + configs/laantungir/README.md | 368 ++++++++++++++++++ configs/laantungir/fips.pub | 1 + configs/laantungir/fips.service | 28 ++ configs/laantungir/fips.yaml | 60 +++ configs/laantungir/hosts | 8 + configs/laantungir/torrc.fips.conf | 16 + plans/fips-browser-extension-possibilities.md | 355 +++++++++++++++++ plans/fips-extension-no-daemon-access.md | 271 +++++++++++++ plans/fips-extension-running-fipsd.md | 260 +++++++++++++ 12 files changed, 1369 insertions(+), 193 deletions(-) delete mode 100644 .gitignore delete mode 100644 README.md create mode 100644 configs/laantungir/.gitignore create mode 100644 configs/laantungir/README.md create mode 100644 configs/laantungir/fips.pub create mode 100644 configs/laantungir/fips.service create mode 100644 configs/laantungir/fips.yaml create mode 100644 configs/laantungir/hosts create mode 100644 configs/laantungir/torrc.fips.conf create mode 100644 plans/fips-browser-extension-possibilities.md create mode 100644 plans/fips-extension-no-daemon-access.md create mode 100644 plans/fips-extension-running-fipsd.md diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 3ebca51..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -fips/ \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 5a8f620..0000000 --- a/README.md +++ /dev/null @@ -1,192 +0,0 @@ -# sys-fips Setup for Qubes OS - -> Goal: run a **single FIPS node** inside one ProxyVM (`sys-fips`) so any AppVM routed through it can access the FIPS mesh. - -## Architecture - -```text -AppVMs → sys-fips (ProxyVM) → sys-firewall → sys-net → Internet - └─ fips0 (TUN) → FIPS mesh (encrypted) -``` - -This setup is intentionally single-node per VM. -If you want another independent FIPS identity/path, clone the VM (for example, `sys-fips2`). - ---- - -## Files in this directory - -| File | Purpose | -|---|---| -| `README.md` | This guide | -| `configs/fips.yaml` | Single-node config template | -| `scripts/00-build-fips.sh` | Run in build qube: build `fips`, `fipsctl`, `fipstop` into local `bin/` | -| `scripts/01-dom0-create-proxyvm.sh` | Run in dom0: creates `sys-fips` ProxyVM | -| `scripts/02-install-fips.sh` | Run in `sys-fips`: installs deps, binaries, and systemd units | -| `scripts/03-configure-identity.sh` | Run in `sys-fips`: configures `/etc/fips/fips.yaml`, identity mode | -| `scripts/03-generate-node-configs.sh` | Compatibility wrapper to `03-configure-identity.sh` | -| `scripts/04-start-fips.sh` | Run in `sys-fips`: starts single FIPS node | -| `scripts/04-start-fips-nodes.sh` | Compatibility wrapper to `04-start-fips.sh` | -| `scripts/05-stop-fips.sh` | Run in `sys-fips`: stops node + cleanup | -| `scripts/05-stop-fips-nodes.sh` | Compatibility wrapper to `05-stop-fips.sh` | -| `scripts/06-configure-dns.sh` | Run in `sys-fips`: dnsmasq forwards `.fips` to localhost:5354 and applies Qubes `/32` DNS compatibility patch | -| `scripts/07-route-appvms.sh` | Run in `sys-fips`: IPv6/ip6tables forwarding for routed AppVMs | -| `scripts/08-add-peer.sh` | Run in `sys-fips`: add static peer to `/etc/fips/fips.yaml` | -| `scripts/09-add-known-peers.sh` | Run in `sys-fips`: add two known bootstrap peers and restart FIPS | -| `scripts/test-connectivity.sh` | Unified test script | -| `scripts/test-local.sh` | Compatibility wrapper to `test-connectivity.sh --local` | -| `scripts/test-from-appvm.sh` | Compatibility wrapper to `test-connectivity.sh --appvm` | -| `scripts/test-full-suite.sh` | Compatibility wrapper to `test-connectivity.sh --full` | - ---- - -## Quick start - -### 1) Create `sys-fips` (run in dom0) - -```bash -sudo bash 01-dom0-create-proxyvm.sh [template] [upstream_netvm] -``` - -Defaults: -- template: `debian-12` -- upstream netvm: `sys-firewall` - -For VPN chaining, pass `sys-vpn` as upstream: - -```bash -sudo bash 01-dom0-create-proxyvm.sh debian-12 sys-vpn -``` - -### 2) Build in this repo, then copy this entire folder - -From this `fips_setup` repo in your build qube: - -```bash -bash ./scripts/00-build-fips.sh -qvm-copy-to-vm sys-fips /home/user/anvil/fips_setup/ -``` - -This keeps your update workflow simple: pull latest code, rebuild once in this repo, copy the folder, run install in `sys-fips`. - -### 3) Install/configure/start (inside `sys-fips`) - -```bash -cd ~/QubesIncoming/*/fips_setup/scripts -sudo bash 02-install-fips.sh -bash 03-configure-identity.sh -sudo bash 04-start-fips.sh -sudo bash 06-configure-dns.sh -sudo bash 07-route-appvms.sh -``` - -### 4) Add at least one external peer - -```bash -sudo bash 08-add-peer.sh -sudo systemctl restart fips -``` - -Or add both known bootstrap peers automatically: - -```bash -sudo bash 09-add-known-peers.sh -``` - -### 5) Test - -```bash -sudo bash test-connectivity.sh --full -``` - -### 6) Point AppVMs at `sys-fips` (in dom0) - -```bash -qvm-prefs my-appvm netvm sys-fips -``` - ---- - -## Clone workflow - -```bash -# dom0 -qvm-clone sys-fips sys-fips2 -qvm-start sys-fips2 -``` - -Inside `sys-fips2`: - -```bash -sudo bash 05-stop-fips.sh -sudo rm -f /etc/fips/fips.key /etc/fips/fips.pub -bash 03-configure-identity.sh -sudo bash 04-start-fips.sh -``` - -Now `sys-fips` and `sys-fips2` are independent FIPS nodes. - ---- - -## Notes - -- One FIPS node per ProxyVM is the production target. -- The previous two-node local loopback approach was useful for local experiments but is not required for normal Qubes deployment. -- FIPS mesh encryption is end-to-end. A VPN upstream is optional and can be chained. -- `06-configure-dns.sh` removes dnsmasq `--local-service` from Debian's helper to avoid dropping AppVM DNS on Qubes `/32` links. - ---- - -## Lessons learned from real `sys-fips` troubleshooting - -These are the key fixes required to make AppVM → `sys-fips` → FIPS mesh work reliably in Qubes. - -### 1) Intercept AppVM DNS in `sys-fips` with nftables - -Qubes AppVM DNS normally targets the Qubes gateway addresses (for example `10.139.1.1` / `10.139.1.2`). -To make `.fips` resolution work from routed AppVMs, `sys-fips` must DNAT DNS to local dnsmasq. - -Required nft hooks in `sys-fips`: - -- `table ip qubes`, chain `dnat-dns` -- `table ip qubes`, chain `custom-input` - -`dnat-dns` redirects UDP/TCP 53 traffic to local DNS (`10.139.1.1:53`) and `custom-input` allows DNS from AppVM interfaces (for example `vif+`). - -### 2) IPv6 forwarding + route to FIPS space - -`sys-fips` must forward IPv6 and route FIPS mesh space (`fd00::/8`) to `fips0`: - -- enable IPv6 forwarding (`sysctl net.ipv6.conf.all.forwarding=1`) -- ensure route exists: `ip -6 route add fd00::/8 dev fips0` - -Without this, AppVM traffic may resolve `.fips` names but fail to reach the resolved FIPS IPv6 destination. - -### 3) AppVMs need a valid IPv6 source + route toward `sys-fips` - -In testing, routed AppVM access required: - -- a synthetic IPv6 on `eth0` (example: `fdaa::15/128`) -- route of FIPS space via Qubes link-local gateway: - - `ip -6 route add fd00::/8 via fe80::fcff:ffff:feff:ffff dev eth0` - -This ensures traffic to resolved `.fips` AAAA records leaves the AppVM through `sys-fips`. - -### 4) Persist network fixes in `/rw/config/rc.local` - -For reboot safety: - -- persist nftables DNS interception + IPv6 forwarding/route in `sys-fips` `/rw/config/rc.local` -- persist AppVM synthetic IPv6 + route in each AppVM `/rw/config/rc.local` that needs `.fips` - -Do not rely on one-off manual commands for production. - -### 5) Chromium-family browser caveat - -Observed behavior: - -- Firefox followed system resolver path and worked once networking was fixed. -- Chromium-family browsers (`agent-browser`, Brave) could still show `ERR_NAME_NOT_RESOLVED` even when `dig`, `getent`, `ping`, and `curl` worked. - -Current operational workaround is to pin the `.fips` hostname in `/etc/hosts` for Chromium-family use-cases. -See `plans/chromium-fips-workaround.md` for tested commands and policy notes. diff --git a/configs/laantungir/.gitignore b/configs/laantungir/.gitignore new file mode 100644 index 0000000..53fc4c8 --- /dev/null +++ b/configs/laantungir/.gitignore @@ -0,0 +1,2 @@ +# Never commit the private key +fips.key diff --git a/configs/laantungir/README.md b/configs/laantungir/README.md new file mode 100644 index 0000000..1d67619 --- /dev/null +++ b/configs/laantungir/README.md @@ -0,0 +1,368 @@ +# FIPS server setup — laantungir.net + +This directory (`configs/laantungir/`) contains the complete configuration +needed to recreate the FIPS mesh node running on `laantungir.net` (VPS at +`15.235.3.231`). + +It lives inside the `fips_setup` repo alongside the fips source tree +(`fips/`), build scripts (`scripts/`), and other configs (`configs/`). +This subdir is specifically for the **production laantungir.net server**; +the other `configs/*.yaml` files are for local Qubes/test nodes. + +It exists so that if the server dies, gets rebuilt, or migrates to a new +host, you can restore the node with the **same identity** (same npub, +same NodeAddr, same position in the spanning tree) and the **same working +advert publishing** in a few minutes. + +## Files + +| File | Installs to | Purpose | +|------|-------------|---------| +| [`fips.yaml`](fips.yaml) | `/etc/fips/fips.yaml` | Daemon config (transports, Nostr, peers, TUN, DNS) | +| [`fips.service`](fips.service) | `/etc/systemd/system/fips.service` | systemd unit | +| [`fips.key`](fips.key) | `/etc/fips/fips.key` | **Private key (nsec) — SECRET, gitignored** | +| [`fips.pub`](fips.pub) | `/etc/fips/fips.pub` | Public key (npub) | +| [`hosts`](hosts) | `/etc/fips/hosts` | Mesh host aliases (`.fips` → npub) | +| [`torrc.fips.conf`](torrc.fips.conf) | lines for `/etc/tor/torrc` | Tor hidden service for the FIPS Tor transport | +| [`.gitignore`](.gitignore) | — | Prevents `fips.key` from being committed (in this subdir) | + +## Advertised endpoints + +The node advertises **three** transports in its Nostr kind 37195 advert, +giving peers a choice based on their network situation: + +| Transport | Advertised address | Best for | Latency | +|---|---|---|---| +| `udp` | `15.235.3.231:2121` | Direct peers with no NAT | ~50ms | +| `tcp` | `15.235.3.231:8443` | Direct peers who prefer TCP | ~50ms | +| `tor` | `t62hfd2dvvbmqc5pd6umw2e6fl52g4soshzrfufuvzagkjxjdly5soid.onion:9443` | NAT'd/censored peers | ~200-500ms | + +A peer's `fipsd` tries endpoints in order and picks whichever works. +If UDP fails (NAT), it falls back to TCP; if TCP fails (firewall), it +falls back to Tor. This is the integrated transport layer — not just +"here's a Tor address," but "here are three ways to reach me, use +whichever your network allows." + +## Node identity + +- **npub:** `npub1crpldvy49ef8z34wlacwujnfudy4nd7k96aqdx5wgn6ckztz7z8q9t59ud` +- **NodeAddr:** `e3da8293e6ea58807bd02b4749824243` (first 16 bytes of SHA-256 of the pubkey) +- **Mesh IPv6:** derived from the NodeAddr in `fd00::/8` +- **Tor onion:** `t62hfd2dvvbmqc5pd6umw2e6fl52g4soshzrfufuvzagkjxjdly5soid.onion` + +The private key in `fips.key` is the **only** thing that ties this node +to its identity. Lose it and you become a new node with a new NodeAddr, +new tree position, and no peers. Back it up somewhere safe outside this +repo (the `.gitignore` keeps it out of git, which is correct for the +repo but means you need a separate secure backup). + +## Server details + +- **Host:** `laantungir.net` (SSH: `ssh ubuntu@laantungir.net`) +- **Public IP:** `15.235.3.231` on `eth0` +- **OS:** Ubuntu (VPS) +- **No NAT** — the public IP is directly on the interface +- **fips binary:** `/usr/local/bin/fips` (v0.4.0, rev d5ee526f0e-dirty) +- **fipsctl:** `/usr/local/bin/fipsctl` +- **Control socket:** `/run/fips/control.sock` (group `fips` — use `sudo fipsctl` if not in the group) + +## How to recreate from scratch + +### 1. Install the fips binary + +Copy `/usr/local/bin/fips` and `/usr/local/bin/fipsctl` from a build or +another server. They're a single static-ish Rust binary each. Version +used here: `fips 0.4.0 (rev d5ee526f0e-dirty)`. + +### 2. Create the fips user/group and directories + +```bash +sudo useradd --system --no-create-home --shell /usr/sbin/nologin fips 2>/dev/null || true +sudo mkdir -p /etc/fips /var/lib/fips +``` + +### 3. Install the identity + +```bash +sudo cp fips.key /etc/fips/fips.key +sudo cp fips.pub /etc/fips/fips.pub +sudo chmod 600 /etc/fips/fips.key +sudo chmod 644 /etc/fips/fips.pub +sudo chown root:root /etc/fips/fips.key /etc/fips/fips.pub +``` + +### 4. Install the config + +```bash +sudo cp fips.yaml /etc/fips/fips.yaml +sudo cp hosts /etc/fips/hosts +``` + +**If the server has a different public IP**, edit `fips.yaml` and change +every `15.235.3.231` to the new IP (there are two: UDP `bind_addr` and +TCP `bind_addr`). Do not change them to `0.0.0.0` — see +"Why bind to the public IP" below. + +### 5. Install the systemd unit + +```bash +sudo cp fips.service /etc/systemd/system/fips.service +sudo systemctl daemon-reload +``` + +### 6. Set up Tor (for the Tor transport) + +```bash +sudo apt install tor +sudo mkdir -p /var/lib/tor/fips_mesh +sudo chown debian-tor:debian-tor /var/lib/tor/fips_mesh +# Append the lines from torrc.fips.conf to /etc/tor/torrc +sudo systemctl enable --now tor +# Wait a few seconds, then verify the onion address: +sudo cat /var/lib/tor/fips_mesh/hostname +# Should print: t62hfd2dvvbmqc5pd6umw2e6fl52g4soshzrfufuvzagkjxjdly5soid.onion +``` + +If the onion address is **different**, you have a new hidden service +identity. That's fine for connectivity, but the advertised onion will +differ from the one in this README. Update this README if you want it +to stay accurate. + +### 7. Open firewall ports (if applicable) + +The daemon needs inbound on: +- **UDP 2121** (mesh peer links) +- **TCP 8443** (mesh peer links, TCP transport) +- **TCP 9443** on `127.0.0.1` only (Tor → fipsd, no external exposure) + +If you use UFW: +```bash +sudo ufw allow 2121/udp +sudo ufw allow 8443/tcp +``` + +### 8. Start the daemon + +```bash +sudo systemctl enable --now fips +sudo systemctl status fips +sudo journalctl -u fips -f +``` + +### 9. Verify the advert is published + +Wait ~30 seconds for the initial advert publish, then check from your +laptop (or any machine with `nak`): + +```bash +nak req -k 37195 -a c0c3f6b0952e527146aeff70ee4a69e34959b7d62eba069a8e44f58b0962f08e \ + --stream wss://nos.lol wss://offchain.pub +``` + +You should see a kind 37195 event with `endpoints` containing +`15.235.3.231:2121` (udp) and `15.235.3.231:8443` (tcp). If you see +nothing, see "Troubleshooting" below. + +### 10. Verify peers are connecting + +```bash +sudo fipsctl show status # check peer_count, estimated_mesh_size +sudo fipsctl show peers # list authenticated peers +sudo fipsctl show tree # spanning tree position +``` + +Within a few minutes `peer_count` should climb past 6 and keep rising. +`estimated_mesh_size` should be in the hundreds. + +## Why bind to the public IP (not 0.0.0.0) + +This is the single most important gotcha in this config, and the reason +this directory exists. + +**The problem:** `fips.yaml` has a top-level `nostr.advertise: true` +flag that makes you *think* your node is publishing its advert. But +there's a **second, per-transport flag** `advertise_on_nostr` that +**defaults to `false`** ([`fips/src/config/transport.rs:109`](../../fips/src/config/transport.rs:109)). +With it false, `build_overlay_advert()` skips every transport, the +endpoints list is empty, and `publish_advert()` returns `Ok(())` +silently — no error, no log line, no advert. Your node consumes the +mesh's adverts fine but never contributes its own, so other nodes can't +discover you via Nostr. + +**The fix has three parts, all required for UDP/TCP:** + +1. Set `advertise_on_nostr: true` on each transport you want advertised. + This applies to **all three transports** (UDP, TCP, Tor) — each + defaults to `false` independently. + +2. Bind to a **specific IP** (the public IP), not `0.0.0.0`. Why: + `build_overlay_advert()` uses `handle.local_addr()` and skips + endpoints where `addr.ip().is_unspecified()` + ([`fips/src/node/lifecycle.rs:1447`](../../fips/src/node/lifecycle.rs:1447)). + Binding to `0.0.0.0` makes `local_addr()` return `0.0.0.0`, which is + unspecified, so the endpoint gets skipped even with + `advertise_on_nostr: true`. (Tor is exempt — it uses + `handle.onion_address()` instead of `local_addr()`, so the bind IP + doesn't matter for Tor advertising.) + +3. For UDP on a public-IP server, also set `public: true` so the advert + says `addr: "15.235.3.231:2121"` (directly reachable) instead of + `addr: "nat"` (requires STUN hole-punching). A public-IP server + doesn't need hole-punching. + +**For Tor specifically**, the requirements are different: +- `advertise_on_nostr: true` (same as UDP/TCP). +- `advertised_port` must match the **virtual port** (first number) in + torrc's `HiddenServicePort 127.0.0.1:` directive. + Our torrc has `HiddenServicePort 9443 127.0.0.1:9443`, so + `advertised_port: 9443`. If you change torrc to the common + `HiddenServicePort 443 127.0.0.1:9443` convention, set + `advertised_port: 443` to match. +- The onion address itself comes from `handle.onion_address()`, which + is read from the `hostname_file` at startup — no bind-IP issue. + +**Symptom if you get this wrong:** `nak req -k 37195 -a ` +returns zero events from all relays, and `sudo journalctl -u fips | grep +-i advert` shows no `advert: published` lines and no error lines either. +Peer count stays low (only your 2 configured peers + whoever happened to +find you through the mesh). + +## How to check if the advert is live + +From any machine with `nak` installed: + +```bash +# Get your hex pubkey from the npub +HEX=$(nak decode npub1crpldvy49ef8z34wlacwujnfudy4nd7k96aqdx5wgn6ckztz7z8q9t59ud) + +# Query the relays +nak req -k 37195 -a "$HEX" --stream wss://nos.lol wss://offchain.pub wss://relay.damus.io +``` + +You should see one event with `endpoints` in the content. The advert +expires after ~1 hour and is republished every ~30 minutes +([`../../fips_playground/plans/fips-explained.md`](../../fips_playground/plans/fips-explained.md:104)), +so if the daemon is down for >1 hour the advert disappears. + +## Troubleshooting + +### `fipsctl` says "Permission denied" + +```bash +sudo fipsctl show status # works immediately +# or add yourself to the fips group for passwordless access: +sudo usermod -aG fips $USER +# log out and back in +``` + +### No advert on relays (`nak` returns nothing) + +1. Check `nostr.advertise: true` and `nostr.enabled: true` in `fips.yaml`. +2. Check `advertise_on_nostr: true` on at least one transport. +3. Check the transport `bind_addr` is a **specific IP**, not `0.0.0.0`. +4. Check `sudo journalctl -u fips | grep -i "advert\|publish"` for errors. +5. Check the relays are reachable: `nak relay wss://nos.lol`. +6. Bump log level to debug: + ```bash + sudo systemctl edit fips # add: Environment=RUST_LOG=fips::discovery::nostr=debug + sudo systemctl restart fips + sudo journalctl -u fips -f | grep -i advert + ``` + +### Peer count not growing + +- If the advert is live but peers aren't connecting, check firewall + rules (UDP 2121, TCP 8443 must be open inbound). +- Check `sudo fipsctl show routing` → `retries` for peers in backoff. +- Check `sudo journalctl -u fips | grep -i "handshake\|connect"` for + handshake failures. + +### `fips.v0l.io` alias rejected ("invalid character '.'") + +The alias validator in `fips` doesn't allow dots. Use dashes: +`fips-v0l-io` instead of `fips.v0l.io`. The `addresses[].addr` field +can still use dots (it's a hostname, not an alias). This config already +uses `fips-v0l-io` as the alias. + +### `Address family not supported by protocol (os error 97)` on handshake + +Seen intermittently on some links. Usually an IPv6/IPv4 mismatch on a +specific peer's transport. Not blocking; the daemon retries. If it +persists for a specific peer, check that the peer's advertised address +matches a transport you have enabled. + +## Useful commands (cheat sheet) + +```bash +# Status overview +sudo fipsctl show status + +# Peers (direct links) +sudo fipsctl show peers + +# Spanning tree position + peer tree positions +sudo fipsctl show tree + +# Bloom filter state (per-peer reachability counts) +sudo fipsctl show bloom + +# Coordinate cache (named nodes with fresh paths) +sudo fipsctl show cache + +# Routing state (pending lookups, retries, counters) +sudo fipsctl show routing + +# All known node identities +sudo fipsctl show identity-cache + +# Link quality (MMP metrics) +sudo fipsctl show mmp + +# Active links +sudo fipsctl show links + +# End-to-end sessions +sudo fipsctl show sessions + +# Historical peer count sparkline +sudo fipsctl stats history peer_count --window 1h --plot + +# Live logs +sudo journalctl -u fips -f + +# Check advert on Nostr +nak req -k 37195 -a c0c3f6b0952e527146aeff70ee4a69e34959b7d62eba069a8e44f58b0962f08e --stream wss://nos.lol wss://offchain.pub +``` + +## Config reference (what each block does) + +See [`../../fips_playground/plans/fips-explained.md`](../../fips_playground/plans/fips-explained.md) +for the full freshman-level explanation of how FIPS works. Quick map of +how this config maps to the four layers described there: + +| Config block | Layer | What it does | +|---|---|---| +| `node.discovery.nostr` | Discovery | Nostr rendezvous: publish/subscribe kind 37195 adverts | +| `transports.*` | Links | UDP/TCP/Tor listeners; `advertise_on_nostr` + `public` (UDP) + `advertised_port` (Tor) control advert content | +| `peers` | Links | Static peers to auto-connect on startup (the 2 you typed in) | +| `tun` | Application | `fips0` TUN device for `.fips` IPv6 routing | +| `dns` | Application | Local DNS resolver mapping `.fips` → mesh IPv6 | + +The spanning tree, bloom filters, and coordinate cache (layers 3–4 in +the explained doc) are automatic — no config needed. + +## Backups on the server + +The server keeps timestamped backups of `fips.yaml` in `/etc/fips/`: +``` +fips.yaml.bak.20260720_141730 # original (before advert fix) +fips.yaml.bak.20260720153329 # pre-fix snapshot +``` +These are created manually with `sudo cp /etc/fips/fips.yaml /etc/fips/fips.yaml.bak.$(date +%Y%m%d%H%M%S)`. + +## Date of this snapshot + +2026-07-20. Config verified working: advert live on relays with all +three transports (UDP, TCP, Tor), peer_count climbing (6 → 13+ within +3 minutes of the initial fix), mesh_size ~1231. Tor endpoint added +in a follow-up edit the same day. diff --git a/configs/laantungir/fips.pub b/configs/laantungir/fips.pub new file mode 100644 index 0000000..6bc7388 --- /dev/null +++ b/configs/laantungir/fips.pub @@ -0,0 +1 @@ +npub1crpldvy49ef8z34wlacwujnfudy4nd7k96aqdx5wgn6ckztz7z8q9t59ud diff --git a/configs/laantungir/fips.service b/configs/laantungir/fips.service new file mode 100644 index 0000000..2b00e92 --- /dev/null +++ b/configs/laantungir/fips.service @@ -0,0 +1,28 @@ +# systemd unit for the FIPS daemon +# Installed at: /etc/systemd/system/fips.service +# Enable with: sudo systemctl enable --now fips + +[Unit] +Description=FIPS Mesh Network Daemon +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +ExecStart=/usr/local/bin/fips --config /etc/fips/fips.yaml +Restart=on-failure +RestartSec=5 + +# Control socket directory (/run/fips/). +# Group-accessible so 'fips' group members can use fipsctl/fipstop. +RuntimeDirectory=fips +RuntimeDirectoryMode=0750 + +# Security hardening (daemon runs as root for TUN and raw sockets) +ProtectHome=yes +PrivateTmp=yes +ProtectKernelModules=yes +ProtectKernelTunables=no + +[Install] +WantedBy=multi-user.target diff --git a/configs/laantungir/fips.yaml b/configs/laantungir/fips.yaml new file mode 100644 index 0000000..5376ff6 --- /dev/null +++ b/configs/laantungir/fips.yaml @@ -0,0 +1,60 @@ +# FIPS daemon configuration for laantungir.net +# Server: VPS at 15.235.3.231 (public IP, no NAT) +# Node identity: npub1crpldvy49ef8z34wlacwujnfudy4nd7k96aqdx5wgn6ckztz7z8q9t59ud +# NodeAddr: e3da8293e6ea58807bd02b4749824243 +# Installed at: /etc/fips/fips.yaml +# +# IMPORTANT: This config binds transports to the public IP 15.235.3.231 +# (not 0.0.0.0) so that build_overlay_advert() produces a non-unspecified +# local_addr() and the endpoints actually get advertised on Nostr. +# See README.md "Why bind to the public IP" for the full rationale. + +node: + identity: + persistent: true + discovery: + nostr: + enabled: true + advertise: true + policy: open + +tun: + enabled: true + name: fips0 + mtu: 1280 + +dns: + enabled: true + bind_addr: "127.0.0.1" + port: 5354 + +transports: + udp: + bind_addr: "15.235.3.231:2121" + advertise_on_nostr: true # REQUIRED: defaults to false; without this no advert is published + public: true # Advertise as host:port (not "nat") — server has a public IP + tcp: + bind_addr: "15.235.3.231:8443" + advertise_on_nostr: true # REQUIRED: defaults to false + tor: + mode: "directory" + socks5_addr: "127.0.0.1:9050" + directory_service: + hostname_file: "/var/lib/tor/fips_mesh/hostname" + bind_addr: "127.0.0.1:9443" + advertise_on_nostr: true # REQUIRED: defaults to false; without this Tor endpoint isn't advertised + advertised_port: 9443 # Must match the virtual port in torrc's HiddenServicePort directive + +peers: + - npub: "npub1qmc3cvfz0yu2hx96nq3gp55zdan2qclealn7xshgr448d3nh6lks7zel98" + alias: "fips-test-node" + addresses: + - transport: udp + addr: "217.77.8.91:2121" + connect_policy: auto_connect + - npub: "npub1zv58cn7v83mxvttl70w5fwjwuclfmntv9cnmv5wmz2nzz88u5urqvdx96n" + alias: "fips-v0l-io" # NOTE: dots are rejected by the alias validator; use dashes + addresses: + - transport: udp + addr: "fips.v0l.io:2121" + connect_policy: auto_connect diff --git a/configs/laantungir/hosts b/configs/laantungir/hosts new file mode 100644 index 0000000..3a9ca96 --- /dev/null +++ b/configs/laantungir/hosts @@ -0,0 +1,8 @@ +# /etc/fips/hosts — FIPS mesh host aliases +# Maps friendly names to npubs so you can do `curl http://.fips/...` +# Format: +# NOTE: aliases must not contain dots (the validator rejects them). + +laantungir npub1crpldvy49ef8z34wlacwujnfudy4nd7k96aqdx5wgn6ckztz7z8q9t59ud +relay npub1crpldvy49ef8z34wlacwujnfudy4nd7k96aqdx5wgn6ckztz7z8q9t59ud +blossom npub1crpldvy49ef8z34wlacwujnfudy4nd7k96aqdx5wgn6ckztz7z8q9t59ud diff --git a/configs/laantungir/torrc.fips.conf b/configs/laantungir/torrc.fips.conf new file mode 100644 index 0000000..51db8fe --- /dev/null +++ b/configs/laantungir/torrc.fips.conf @@ -0,0 +1,16 @@ +# Tor hidden service for the FIPS mesh +# Add these lines to /etc/tor/torrc (or a file in /etc/tor/torrc.d/) +# then restart tor: sudo systemctl restart tor +# +# This exposes the FIPS daemon's Tor transport (bound to 127.0.0.1:9443) +# as a hidden service so that NAT'd peers can reach you over Tor. +# +# The onion address is written to /var/lib/tor/fips_mesh/hostname +# and read by fipsd at startup to populate the Tor endpoint in the +# Nostr advert. Current onion: +# t62hfd2dvvbmqc5pd6umw2e6fl52g4soshzrfufuvzagkjxjdly5soid.onion + +# Keep only FIPS mesh in this managed block. +HiddenServiceDir /var/lib/tor/fips_mesh/ +HiddenServicePort 9443 127.0.0.1:9443 +# === LAANTUNGIR TOR SERVICES END === diff --git a/plans/fips-browser-extension-possibilities.md b/plans/fips-browser-extension-possibilities.md new file mode 100644 index 0000000..cadef70 --- /dev/null +++ b/plans/fips-browser-extension-possibilities.md @@ -0,0 +1,355 @@ +# FIPS Browser Extension — Possibilities Discussion + +## Context + +FIPS uses Nostr keypairs (secp256k1) as node addresses. Browsers access FIPS sites via `.fips` hostnames (e.g., `npub1abc...xyz.fips`) that resolve to `fd00::/8` IPv6 addresses through a local FIPS DNS resolver on port 5354. Traffic is encrypted with Noise IK (hop-by-hop) + Noise XK (end-to-end), but browsers see plain HTTP and show "Not Secure" warnings. + +nos2x is an existing browser extension that provides Nostr signing via a `window.nostr` API. Since FIPS uses the same secp256k1 keypairs, a nos2x-based extension could serve double duty: Nostr signing + FIPS features, sharing one keypair. + +This document explores **everything** a FIPS browser extension could do, grouped by problem domain. + +--- + +## Problem 1: DNS Resolution for `.fips` Domains + +### The Problem + +Chromium-family browsers use their own DNS resolution path that bypasses the system resolver. Even when `dig`, `getent`, `ping`, and `curl` all resolve `.fips` correctly, Chromium shows `ERR_NAME_NOT_RESOLVED`. Firefox works because it follows the system resolver path. Current workaround is pinning `.fips` hostnames in `/etc/hosts`, which is manual and fragile. + +### What the Extension Could Do + +**Option A: `webRequest` DNS interception** + +Use the `webRequest` API to intercept requests to `*.fips` domains before they hit the browser's DNS resolver. The extension resolves the hostname itself (either by querying the local FIPS DNS on `127.0.0.1:5354`, or by computing the IPv6 directly from the npub) and redirects the request to the resolved IPv6 address. + +- Pros: Works without modifying browser DNS settings; portable +- Cons: `webRequest` is being restricted in Manifest V3; may need `declarativeNetRequest` instead; URL bar still shows `.fips` which is good, but the connection goes to the IPv6 + +**Option B: `proxy` API with custom PAC script** + +Use the `proxy` API to provide a Proxy Auto-Config (PAC) script that matches `*.fips` domains and routes them through a custom proxy handler. The extension resolves the `.fips` name and connects directly to the IPv6 address. + +- Pros: Clean separation; PAC scripts are well-supported +- Cons: More complex; need to handle the proxy connection logic + +**Option C: Direct npub → IPv6 computation in the extension** + +Since the npub → IPv6 derivation is pure computation (npub → public key → SHA-256 → node_addr → `fd00::/8` IPv6), the extension could compute the IPv6 address directly without any DNS query. The extension parses the `npub1...fips` hostname, decodes the bech32 npub, hashes it, and derives the IPv6 address. + +- Pros: Zero network dependency for resolution; works even if FIPS DNS isn't running; instant +- Cons: Only works for direct npub addresses (`npub1xxx.fips`), not friendly hostnames (`mysite.fips`); friendly hostnames still need a lookup against `/etc/fips/hosts` or the FIPS DNS resolver + +**Option D: Hybrid — compute for npub addresses, query for friendly hostnames** + +Combine Option C with a DNS query fallback. For `npub1*.fips`, compute the IPv6 directly. For other `*.fips` names, query the local FIPS DNS resolver on `127.0.0.1:5354` (or a configurable remote address). + +- Pros: Best of both worlds; npub addresses always resolve; friendly names work when FIPS DNS is available +- Cons: Slightly more complex implementation + +--- + +## Problem 2: Security Indicators — "Not Secure" Warning + +### The Problem + +FIPS traffic is encrypted with double-layer Noise protocol encryption (IK hop-by-hop + XK end-to-end), but the browser sees plain HTTP to an IPv6 address. The URL bar shows "Not Secure," which is misleading — the traffic is actually more encrypted than standard TLS. + +### What the Extension Could Do + +**Option A: Toolbar badge/icon** + +Show a custom toolbar icon that changes state based on whether the current page is a `.fips` site. When on a `.fips` site, show a green "FIPS" badge or lock icon. Clicking it shows a popup explaining the FIPS encryption (Noise IK + Noise XK, end-to-end encrypted, mesh-routed). + +- Pros: Simple, reliable, no browser security model conflicts +- Cons: Doesn't change the URL bar "Not Secure" text; users have to notice the badge + +**Option B: Page-level banner/indicator** + +Inject a content script that adds a small banner or corner badge on `.fips` pages showing "FIPS Encrypted" with details on hover. Similar to how the Tor Browser shows onion circuit information. + +- Pros: Visible on the page itself; can show rich detail +- Cons: Can be spoofed by page content (though extension can verify the URL is `.fips` first); visually intrusive + +**Option C: Local TLS proxy (MITM approach)** + +Run a local proxy within the extension (or a companion native app) that terminates TLS for `.fips` domains. The extension generates self-signed certificates for `.fips` hostnames on the fly and installs them as trusted. The browser sees HTTPS with a valid cert; the proxy handles the actual FIPS connection underneath. + +- Pros: Browser shows the green lock; proper HTTPS UX +- Cons: Complex; requires native messaging component; certificate trust management is fragile across browsers; security implications of MITM; may conflict with browser security policies + +**Option D: Custom protocol handler (`fips://`)** + +Register a custom protocol handler for `fips://` URLs. Instead of `http://npub1abc.fips`, users would use `fips://npub1abc`. The extension handles the protocol, resolves the address, and displays the content. The browser wouldn't show HTTP security warnings because it's not HTTP. + +- Pros: Clean separation from HTTP; no "Not Secure" issue; distinct UX +- Cons: Links and bookmarks need to use `fips://`; not all browser APIs work with custom protocols; may have rendering limitations; breaks compatibility with existing `.fips` HTTP URLs + +**Option E: Badge + popup with circuit details (Tor Browser style)** + +Combine Option A with a detailed popup that shows the FIPS "circuit" — which peers your traffic is routing through, the encryption layers, the destination npub, and the mesh path. Modeled after Tor Browser's circuit display. + +- Pros: Educational; builds trust; shows real security information +- Cons: Requires access to FIPS daemon routing info; more complex + +--- + +## Problem 3: Nostr Signing (nos2x Feature Parity) + +### The Problem + +Nostr users need a signing extension to interact with Nostr applications (signing events, encrypting/decrypting messages). nos2x provides this via `window.nostr` API. Since FIPS uses the same secp256k1 keypairs, the extension should provide this functionality too. + +### What the Extension Could Do + +**Full nos2x parity:** + +- Inject `window.nostr` object into web pages +- Implement the NIP-07 API: `getPublicKey()`, `signEvent()`, `getRelays()`, `nip04.encrypt()`, `nip04.decrypt()`, `nip44.encrypt()`, `nip44.decrypt()` +- Prompt user to approve/reject signing requests (popup with event details) +- Maintain a whitelist of sites allowed to request signatures + +**FIPS-specific signing:** + +- Sign FIPS authentication challenges (prove you own the npub that a FIPS service expects) +- Sign FIPS peer introduction messages +- Use the same keypair for both Nostr and FIPS identity — your `window.nostr` public key IS your FIPS node address + +**Multi-identity support:** + +- Manage multiple keypairs (different Nostr/FIPS identities) +- Switch between identities per-site +- Each identity has its own npub, .fips address, and IPv6 + +--- + +## Problem 4: FIPS Identity Management + +### The Problem + +Users need to generate, import, and manage their FIPS keypair. Currently this is done via command-line scripts (`03-configure-identity.sh`). The extension could provide a GUI for this. + +### What the Extension Could Do + +- **Generate new keypair** — Create a new secp256k1 keypair; show the npub, .fips address, and IPv6 +- **Import existing nsec** — Import a Nostr private key to use as FIPS identity +- **Export keypair** — Backup your private key (with encryption/password protection) +- **Display identity info** — Show your npub, .fips address, IPv6 address, and QR codes for sharing +- **Multiple identities** — Create and switch between multiple FIPS/Nostr identities +- **Identity linking** — Show that your Nostr identity and FIPS identity are the same key + +--- + +## Problem 5: FIPS Node Status and Monitoring + +### The Problem + +Users need to know if their FIPS node is running, connected to peers, and reachable. Currently this requires `fipstop` TUI or `fipsctl` CLI. The extension could show this in the browser. + +### What the Extension Could Do + +- **Connection status** — Show if the local FIPS node is running and reachable +- **Peer list** — Show connected peers, their npubs, aliases, and transport types +- **Mesh status** — Show network size, your tree depth, routing coordinates +- **Your address** — Show your npub, .fips hostname, and IPv6 address +- **Health alerts** — Notify if FIPS node goes down, peer count drops to zero, etc. +- **Traffic stats** — Show bytes sent/received over FIPS mesh + +Implementation: The extension would communicate with the local FIPS daemon via its control socket/API (same API that `fipsctl` uses). This could be via HTTP, WebSocket, or a native messaging port. + +--- + +## Problem 6: FIPS Hosts Management (Friendly Hostnames) + +### The Problem + +Friendly `.fips` hostnames (like `mysite.fips`) require entries in `/etc/fips/hosts` on the visitor's node. This is a manual command-line operation. The extension could provide a UI for managing these mappings. + +### What the Extension Could Do + +- **View host mappings** — List all friendly hostname → npub mappings +- **Add/edit/remove mappings** — GUI for managing `/etc/fips/hosts` entries +- **Auto-add from Nostr** — If a Nostr profile lists a FIPS address, auto-suggest adding it to hosts +- **Bookmark .fips sites** — Save and organize .fips sites you visit +- **Share host mappings** — Export/import host mapping lists +- **Sync via Nostr** — Publish your host mappings as a Nostr event so followers can import them + +Implementation: The extension would need to either (a) write to `/etc/fips/hosts` via native messaging, or (b) maintain its own host map and use it for DNS resolution (combining with Problem 1's hybrid approach). + +--- + +## Problem 7: FIPS Address Detection and Linkification + +### The Problem + +When someone shares a FIPS npub address in a web page, chat, or Nostr note, it's just text. Users have to manually copy it and construct a `.fips` URL. The extension could make npubs clickable. + +### What the Extension Could Do + +- **Detect npubs on pages** — Scan page content for `npub1...` patterns and `.fips` URLs +- **Linkify npubs** — Convert `npub1abc...` text into clickable links to `http://npub1abc.fips` +- **Context menu** — Right-click an npub to open it as a `.fips` site, copy the `.fips` URL, or add to hosts +- **QR code generation** — Generate QR codes for `.fips` addresses for easy sharing +- **Omnibox support** — Type an npub directly in the address bar and have it resolve to the `.fips` site + +--- + +## Problem 8: FIPS Content Discovery + +### The Problem + +There's no directory of FIPS-hosted services. Users have to know the npub of the node they want to visit. The extension could help users discover FIPS content. + +### What the Extension Could Do + +- **FIPS directory** — Maintain a curated or crowd-sourced list of known FIPS services (websites, relays, git servers, etc.) +- **Nostr integration** — Use Nostr events to discover FIPS services (e.g., a NIP that publishes "I host a FIPS service at npub X") +- **Bookmarks** — Pre-populate with known FIPS services from the project +- **Search** — Search across known FIPS services by name, type, or npub + +--- + +## Problem 9: Gateway/Proxy for Non-FIPS Users + +### The Problem + +Only people running a FIPS node can access `.fips` sites. This limits the audience. A gateway could let non-FIPS users access FIPS content through a public FIPS node. + +### What the Extension Could Do + +- **Gateway routing** — Route `.fips` requests through a public FIPS gateway node when no local FIPS node is detected +- **Gateway selection** — Let users choose a trusted gateway (or run their own) +- **Trust indicators** — Show whether you're accessing FIPS directly (full encryption) or through a gateway (gateway sees traffic) +- **Fallback mode** — Automatically fall back to gateway mode when local FIPS node is down + +Caveat: Gateway access breaks the end-to-end encryption promise — the gateway operator can see traffic. This should be clearly communicated. This is similar to how Tor has entry nodes vs. running your own relay. + +--- + +## Problem 10: Mixed Content and Resource Loading + +### The Problem + +A `.fips` web page might load resources (images, scripts, CSS) from the regular internet, creating a mixed content situation. Some resources are FIPS-encrypted, others are not. + +### What the Extension Could Do + +- **Mixed content indicator** — Show what percentage of page resources are FIPS vs. clearnet +- **Resource list** — Show which resources loaded over FIPS vs. regular internet +- **Upgrade to FIPS** — If a resource is available over FIPS (same npub), automatically upgrade the request +- **Block non-FIPS resources** — Option to block all non-FIPS resources on `.fips` pages for maximum privacy + +--- + +## Problem 11: FIPS Peer Management + +### The Problem + +Adding FIPS peers currently requires command-line scripts (`08-add-peer.sh`). The extension could provide a GUI for peer management. + +### What the Extension Could Do + +- **View peer list** — Show configured peers with their npub, alias, transport, and connection status +- **Add/remove peers** — GUI for adding new peers (npub + transport address) +- **Peer discovery** — Discover peers via Nostr (e.g., people publishing their FIPS node address in Nostr profiles) +- **Bootstrap peers** — One-click add of known bootstrap peers +- **Peer health** — Show latency, packet loss, and uptime per peer + +Implementation: Via native messaging to the FIPS daemon, or via the FIPS control API. + +--- + +## Problem 12: Qubes OS Integration + +### The Problem + +In Qubes OS, the browser runs in an AppVM while FIPS runs in `sys-fips` ProxyVM. The extension in the AppVM browser needs to communicate with the FIPS node in a different VM. + +### What the Extension Could Do + +- **Cross-VM status** — Show FIPS node status from `sys-fips` in the AppVM browser +- **Qubes-aware DNS** — Handle the Qubes DNS forwarding path (AppVM → sys-fips dnsmasq → FIPS DNS) +- **VM health** — Show if `sys-fips` is running and properly chained +- **Auto-configure** — Detect Qubes environment and configure DNS resolution accordingly + +--- + +## Problem 13: Nostr-FIPS Crossover Features + +### The Problem + +Since FIPS and Nostr share the same keypair, there are natural crossover features that neither system alone provides. + +### What the Extension Could Do + +- **Nostr profile → FIPS address** — When viewing a Nostr profile, show their FIPS `.fips` address and offer to visit it +- **FIPS site → Nostr identity** — When visiting a `.fips` site, look up the npub on Nostr and show their Nostr profile, posts, and relay list +- **Verified identity** — Cross-verify: the FIPS node you're connected to proves it owns the npub, and the Nostr profile proves the same npub — therefore the FIPS site is authentically operated by that Nostr identity +- **Nostr relay over FIPS** — Connect to Nostr relays hosted on FIPS nodes (e.g., `ws://npub1abc.fips`) with the extension handling both the FIPS connection and Nostr signing +- **FIPS service announcements via Nostr** — Publish and consume Nostr events that announce FIPS services (kind: FIPS service descriptor, content: service type, npub, port) + +--- + +## Architecture Considerations + +### Manifest V2 vs V3 + +- `webRequest` blocking is restricted in Manifest V3 (Chrome). Firefox still supports it with some changes. +- `declarativeNetRequest` is the MV3 replacement but is less flexible. +- For DNS interception, the `proxy` API may be more future-proof. +- Firefox supports both MV2 and MV3; Chrome is MV3-only. + +### Native Messaging + +Some features (writing to `/etc/fips/hosts`, communicating with FIPS daemon, managing system-level config) require native messaging — a companion native app that the extension communicates with via stdio. + +- Pros: Full system access; can talk to FIPS daemon directly +- Cons: Requires installing a native component; not portable to all platforms; more complex setup + +### Shared Keypair Architecture + +The keypair is the foundation. The extension should: + +1. Store the private key securely (browser extension storage, encrypted) +2. Derive everything from it: npub, .fips address, IPv6 address +3. Use it for both Nostr signing and FIPS identity +4. Optionally sync with a hardware signer (NIP-46, Nostr Connect) + +### Communication with FIPS Daemon + +Options for talking to the FIPS daemon from the extension: + +- **HTTP API** — If FIPS exposes a control API on localhost +- **WebSocket** — If FIPS exposes a WebSocket control interface +- **Native messaging** — Companion app that talks to FIPS daemon +- **File-based** — Read `/etc/fips/fips.yaml`, `/etc/fips/hosts`, `/etc/fips/fips.pub` directly (via native messaging) + +--- + +## Summary: Feature Priority Map + +| Feature | Problem Solved | Complexity | Impact | +|---|---|---|---| +| DNS resolution for `.fips` | Chromium can't resolve `.fips` | Medium | Critical — nothing works without this | +| Security indicator badge | "Not Secure" warning is misleading | Low | High — user trust | +| Nostr signing (nos2x parity) | Nostr app interaction | Medium | High — core feature for Nostr users | +| Identity management GUI | Key management is CLI-only | Medium | Medium — convenience | +| Node status monitoring | No visibility into FIPS state | Medium | Medium — operational | +| Hosts management GUI | Friendly hostnames are CLI-only | Low | Medium — convenience | +| npub linkification | npubs aren't clickable | Low | Medium — UX improvement | +| Content discovery | No way to find FIPS sites | High | Medium — ecosystem growth | +| Gateway for non-FIPS users | Only FIPS users can access | Very High | High — adoption, but breaks E2E | +| Mixed content handling | Partial encryption visibility | Medium | Low — edge case | +| Peer management GUI | Peer management is CLI-only | Medium | Low — operational | +| Qubes integration | Cross-VM communication | High | Low — Qubes-specific | +| Nostr-FIPS crossover | Identity verification, discovery | High | High — unique value proposition | + +--- + +## Open Questions for Discussion + +1. Which of these features feel most essential vs. nice-to-have? +2. Is the gateway/proxy approach (Problem 9) worth exploring, or should we focus on users who already run FIPS? +3. How much of this should be in the extension vs. a companion native app? +4. Should we target Firefox first (better webRequest support, system DNS) or Chrome/Chromium first (bigger user base, worse DNS behavior)? +5. Is the Nostr-FIPS crossover (Problem 13) a compelling enough unique feature to prioritize? +6. Should the extension try to replace the FIPS DNS resolver entirely (computing npub → IPv6 in the extension), or work alongside it? diff --git a/plans/fips-extension-no-daemon-access.md b/plans/fips-extension-no-daemon-access.md new file mode 100644 index 0000000..b64e455 --- /dev/null +++ b/plans/fips-extension-no-daemon-access.md @@ -0,0 +1,271 @@ +# FIPS Extension: Accessing .fips Content Without a Local Daemon + +## The Question + +> If a user came across a website with FIPS links embedded in it, could the browser extension resolve those links and make them usable without the user having the FIPS daemon installed and running? + +## Short Answer + +**Yes, but with important tradeoffs.** The extension can always resolve `.fips` names (the math is pure computation). But actually *connecting* to the destination requires either a local FIPS node or a gateway. There are three approaches, ranging from simple-but-less-private to ambitious-but-fully-encrypted. + +--- + +## Why Name Resolution Works Without a Daemon + +The npub → IPv6 derivation is **pure computation** — no network, no daemon, no DNS: + +``` +npub1qmc3... (bech32 string) + ↓ decode +32-byte public key + ↓ SHA-256, take first 16 bytes +node_addr (16 bytes) + ↓ prepend 0xfd +fd00::/8 IPv6 address +``` + +The extension can do this entirely in JavaScript. Bech32 decoding and SHA-256 are both trivial in JS (Web Crypto API has `crypto.subtle.digest('SHA-256', ...)`). So for any `npub1xxx.fips` URL, the extension can compute the target IPv6 address instantly, with zero network dependency. + +**Friendly hostnames** (`mysite.fips` instead of `npub1xxx.fips`) are different — those require a lookup against `/etc/fips/hosts` or the FIPS DNS resolver. Without a daemon, the extension would need its own host map (stored in extension storage) or a gateway that can do the lookup. + +--- + +## The Connectivity Problem + +Here's the catch: the resolved IPv6 address (e.g., `fdab:1234:...`) lives in the `fd00::/8` ULA range. This address only exists **inside the FIPS mesh**. Without a FIPS node running: + +- There's no `fips0` TUN interface +- There's no route for `fd00::/8` traffic +- The OS has nowhere to send packets to that IPv6 address +- The connection fails with "No route to host" + +So resolution works, but connectivity doesn't — unless we provide an alternative path. + +--- + +## Three Approaches to Daemon-Free Access + +### Approach 1: Gateway Proxy (Simple, Less Private) + +The extension routes `.fips` requests through a public FIPS gateway — a server that runs FIPS and acts as an HTTP proxy for non-FIPS users. + +**How it works:** + +``` +User browser (no FIPS) + ↓ extension intercepts request to npub1abc.fips + ↓ extension resolves npub → IPv6 (pure computation) + ↓ extension sends request to gateway: "fetch http://[fdab:...]/ for me" + ↓ +FIPS Gateway (runs FIPS daemon) + ↓ receives proxy request + ↓ connects to fdab:... through its local FIPS mesh + ↓ fetches the content + ↓ returns it to the extension + ↓ +User sees the FIPS-hosted page +``` + +**Implementation in the extension:** +- Use the `proxy` API to route `*.fips` requests through a configured gateway URL +- The gateway is an HTTP CONNECT proxy or a reverse proxy that accepts `.fips` hostnames +- Extension resolves the npub locally and passes the resolved address to the gateway (or just passes the hostname and lets the gateway resolve it) + +**Tradeoffs:** +- ✅ Simple to implement — just a proxy configuration +- ✅ Works for all `.fips` content (HTTP, WebSocket, etc.) +- ✅ Gateway can be self-hosted (your own FIPS node on a VPS) or public +- ❌ **Gateway operator can see all traffic** — breaks end-to-end encryption +- ❌ Trust required in the gateway operator +- ❌ Gateway becomes a centralization point (though users can choose their own) + +**This is analogous to Tor2Web gateways** — they let non-Tor users access `.onion` sites, but the gateway can see the traffic. It's a tradeoff between accessibility and privacy. + +**Mitigations:** +- Let users run their own gateway (their FIPS node on a VPS or home server) +- Support multiple gateways and let users choose +- Clearly indicate in the UI when you're on "gateway mode" vs "direct FIPS mode" +- Sites can use end-to-end app-level encryption (e.g., the site itself serves over HTTPS with a self-signed cert whose fingerprint is published via Nostr) on top of the FIPS transport + +--- + +### Approach 2: Lightweight Browser FIPS Client (Ambitious, Fully Encrypted) + +The extension implements enough of the FIPS protocol in JavaScript to participate in the mesh directly, without a full daemon. It connects to a FIPS bootstrap node via WebSocket and establishes an end-to-end encrypted session to the destination. + +**How it works:** + +``` +User browser (no FIPS daemon) + ↓ extension intercepts request to npub1abc.fips + ↓ extension resolves npub → IPv6 (pure computation) + ↓ extension connects to a FIPS bootstrap node via WebSocket + ↓ extension runs FSP (FIPS Session Protocol) — Noise XK handshake + ↓ extension establishes end-to-end encrypted session to npub1abc + ↓ extension sends HTTP request through the encrypted session + ↓ +FIPS Bootstrap Node (just a regular FIPS peer) + ↓ receives the session packet + ↓ routes it through the mesh toward npub1abc + ↓ (bootstrap node can't read the payload — it's end-to-end encrypted) + ↓ +Destination node (npub1abc) + ↓ receives session, decrypts payload + ↓ returns HTTP response through the encrypted session + ↓ +User sees the FIPS-hosted page — fully end-to-end encrypted +``` + +**What the extension would need to implement in JS:** +- Bech32 decoding (for npub parsing) — trivial +- SHA-256 (for node_addr derivation) — Web Crypto API +- secp256k1 (for keypair generation and signing) — existing JS libraries (e.g., @noble/secp256k1, nostr-tools) +- Noise XK handshake (for end-to-end session establishment) — JS Noise implementation or custom +- FSP session protocol (framing, port multiplexing) — port from Rust +- WebSocket transport (to connect to a FIPS bootstrap node) — native browser API +- HTTP-over-FSP (carry HTTP requests/responses over the FSP session) — custom layer + +**Tradeoffs:** +- ✅ **Full end-to-end encryption** — the bootstrap node can't read your traffic +- ✅ No trust required in the bootstrap node (it's just a router) +- ✅ True to FIPS's privacy model +- ✅ The extension IS a FIPS node (lightweight, browser-based) +- ❌ Significant implementation effort — Noise protocol, FSP, mesh routing in JS +- ❌ Browser limitations: WebSocket is the only viable transport (no raw UDP/TCP) +- ❌ Can't run a full mesh node in a browser (no TUN, no listening sockets, no UDP) +- ❌ Performance — JS crypto is slower than Rust, though WebCrypto helps +- ❌ The bootstrap node needs to support WebSocket inbound (FIPS does support TCP, and WebSocket is essentially TCP-over-HTTP) + +**This is the most exciting option.** It means the extension is a **first-class FIPS client** — not just a proxy consumer. The user gets real end-to-end encryption without installing anything. The browser extension becomes the FIPS node, using a bootstrap peer the same way a full FIPS node would. + +**Key insight:** The extension doesn't need to implement the full mesh protocol (FMP with spanning tree, bloom filters, routing). It only needs: +1. **FSP** (session protocol) — to establish end-to-end encrypted sessions +2. **A single transport** — WebSocket to a bootstrap node +3. **The bootstrap node handles mesh routing** — it receives the session packet and forwards it through the mesh using FMP + +The extension is a "FIPS client" not a "FIPS router." It doesn't relay traffic for others; it just creates sessions to destinations through a bootstrap peer. This is a much smaller scope than a full FIPS node. + +--- + +### Approach 3: Hybrid — Gateway Now, Browser Client Later + +Start with Approach 1 (gateway proxy) for initial release, then evolve to Approach 2 (browser FIPS client) as the project matures. + +**Phase 1:** Extension uses gateway proxy mode. Users can access `.fips` content immediately. The extension clearly shows "gateway mode" with a warning about reduced privacy. + +**Phase 2:** Extension implements FSP over WebSocket. Users who want full encryption can switch to "direct mode." Gateway mode remains as a fallback. + +**Phase 3:** Extension can optionally become a full FIPS node (with WebRTC for UDP-like transport, service worker for background operation, etc.) — but this is speculative and far future. + +--- + +## The User Experience + +Here's how it would look to a user who discovers a FIPS link: + +### Scenario: Alice publishes a blog post with a FIPS link + +1. **Bob visits Alice's regular blog** (clearnet, `https://alice.com/blog`) +2. The blog post contains a link: `My FIPS-hosted mirror` +3. **Bob has the extension installed** but no FIPS daemon +4. Bob clicks the link +5. The extension: + - Detects the `.fips` URL + - Resolves `npub1qmc3...98` → `fdab:1234:...` (pure computation, instant) + - Checks for local FIPS node → not found + - Falls back to gateway mode (or browser client mode) + - Fetches the content through the gateway/mesh + - Displays the page in the browser +6. **Bob sees Alice's FIPS-hosted mirror** — he didn't need to install FIPS + +### What the extension shows Bob: + +- **Toolbar badge:** "FIPS" with a color indicating mode: + - 🟢 Green: Direct FIPS (local daemon, full encryption) + - 🟡 Yellow: Browser client mode (end-to-end encrypted via WebSocket bootstrap) + - 🟠 Orange: Gateway mode (gateway can see traffic) +- **Popup on click:** Shows the destination npub, the resolved IPv6, the connection mode, and the encryption status +- **Page indicator:** Small banner on first visit explaining "This site is hosted on the FIPS mesh" + +### Linkification on regular pages: + +The extension can also **scan pages for npub addresses** and linkify them. If a page contains the text `npub1qmc3...98`, the extension converts it to a clickable link to `http://npub1qmc3...98.fips/`. This means FIPS addresses work anywhere — blog posts, Nostr notes, chat messages, forums. + +--- + +## What Makes This Compelling + +This is the feature that could drive FIPS adoption. Right now, FIPS has a chicken-and-egg problem: + +- Content creators won't host on FIPS if no one can access it +- Users won't install FIPS if there's no content to access + +The extension breaks this cycle: + +1. **Content creators** host on FIPS (they run a FIPS node — it's their server) +2. **Regular users** install the browser extension (one click, no daemon) +3. The extension makes `.fips` links work transparently +4. Users who want full privacy can upgrade to running a FIPS node later +5. The extension's Nostr signing features (nos2x parity) give users another reason to install it + +**The extension becomes the FIPS on-ramp.** You install it for Nostr signing (like nos2x), and as a side effect, `.fips` links just work. You don't even need to know what FIPS is — the extension handles it. + +--- + +## Technical Feasibility Assessment + +### Approach 1 (Gateway) — Feasible Now + +| Component | Status | +|---|---| +| npub → IPv6 in JS | Trivial — bech32 + SHA-256 | +| `proxy` API for routing | Standard browser extension API | +| Gateway server | Needs to be built — an HTTP proxy that connects through FIPS | +| Friendly hostname support | Extension can maintain its own host map, or gateway can resolve | + +The gateway server is the main thing that needs to be built. It's essentially: +- An HTTP proxy that accepts requests for `*.fips` hostnames +- Resolves the hostname (npub computation or FIPS DNS) +- Connects to the destination through its local FIPS mesh +- Returns the response + +This could be a simple Rust service built on top of the FIPS library, or even a configuration of an existing proxy (like a custom nginx module or a small Go/Rust HTTP proxy). + +### Approach 2 (Browser Client) — Feasible But Significant Work + +| Component | Status | +|---|---| +| npub → IPv6 in JS | Trivial | +| secp256k1 in JS | Existing libraries (@noble/secp256k1, nostr-tools) | +| Noise XK in JS | Needs implementation or port — no widely-used JS Noise library | +| FSP framing in JS | Needs port from Rust | +| WebSocket transport | Native browser API | +| HTTP-over-FSP | Needs design and implementation | +| Bootstrap node WebSocket support | FIPS supports TCP; WebSocket gateway needed (or FIPS adds WS transport) | + +The Noise XK implementation is the hardest part. There are Noise libraries in various languages but a production-quality JS implementation that matches FIPS's exact handshake would need work. The FSP framing is straightforward — it's a binary protocol with length-prefixed messages. + +The bootstrap node would need to accept WebSocket connections. FIPS currently supports TCP, and a WebSocket is essentially TCP with an HTTP upgrade handshake. Either: +- FIPS adds a WebSocket transport (small addition to the transport layer) +- A small bridge service converts WebSocket → TCP for the FIPS bootstrap node + +### Approach 3 (Hybrid) — Most Pragmatic + +Start with gateway, ship something that works, then build the browser client for users who want full encryption. This gives the project: +- Immediate value (gateway mode works day one) +- A path to full privacy (browser client mode) +- A reason for users to upgrade from gateway to direct mode + +--- + +## Open Questions + +1. **Gateway trust model:** Should the extension ship with default gateways, or require users to configure their own? Shipping defaults is more convenient but creates trust assumptions. + +2. **Browser client scope:** If we go with Approach 2, should the extension implement the full FSP protocol, or just enough for HTTP GET/POST? WebSocket support in FIPS would be needed either way. + +3. **Friendly hostnames without a daemon:** Should the extension maintain its own host map (synced via Nostr?) for friendly names, or should we focus on npub addresses for daemon-free users? + +4. **Nostr-based discovery:** Could we use Nostr events to publish FIPS service directories? E.g., a NIP where kind:XXXX events announce "I host service Y at npub Z on port P." The extension could index these and provide a FIPS service browser. + +5. **Gateway as a FIPS feature:** Should the gateway proxy be a first-class FIPS feature (built into `fipsctl` or as a separate `fips-gateway` binary), or a separate project? diff --git a/plans/fips-extension-running-fipsd.md b/plans/fips-extension-running-fipsd.md new file mode 100644 index 0000000..9dfd2e2 --- /dev/null +++ b/plans/fips-extension-running-fipsd.md @@ -0,0 +1,260 @@ +# Can the Extension Run the Existing FIPS Rust Code? + +## The Question + +> fipsd is a compiled Rust program. Could the extension run this program directly, saving us from rewriting FIPS in JavaScript? + +## Short Answer + +**A browser extension cannot run a compiled native binary directly** — browser extensions run in a sandboxed JavaScript/WebAssembly environment and cannot execute native programs. But there are three paths to reuse the existing Rust code with varying degrees of "no install required." + +--- + +## Why the Extension Can't Just Run fipsd + +Browser extensions are sandboxed. They cannot: +- Execute native binaries (`exec`, `spawn`, etc.) +- Create TUN interfaces +- Open raw UDP/TCP sockets +- Listen on ports +- Access the filesystem (except a small extension-specific storage area) + +The `fipsd` binary expects all of these — it creates a TUN interface, binds UDP/TCP ports, runs a DNS resolver, etc. None of that is possible inside a browser extension sandbox. + +--- + +## Path 1: Native Messaging (Requires Install — Defeats the Goal) + +Browser extensions can communicate with native applications via the **Native Messaging API**. The extension sends messages to a native messaging host (a script or binary installed on the user's system), which can run `fipsd` or talk to it. + +**How it works:** +``` +Browser extension + ↓ chrome.runtime.sendNativeMessage('com.fips.client', {...}) + ↓ +Native messaging host (installed separately) + ↓ communicates with fipsd via its control API + ↓ +fipsd (running as a system process) + ↓ connects to FIPS mesh + ↓ +Content returned through the chain +``` + +**The problem:** The user must install both the native messaging host AND `fipsd`. This is exactly the "install FIPS" step we're trying to eliminate. It's useful for the "existing FIPS user" case (Problem 1-5 in the possibilities doc) but doesn't solve the "no daemon" access question. + +**When this is useful:** For users who already run FIPS, native messaging lets the extension talk to their local `fipsd` for status, peer management, hosts file editing, etc. This is the right approach for the "FIPS power user" features. But it doesn't help with daemon-free access. + +--- + +## Path 2: Compile FIPS to WebAssembly (Reuse Protocol Code, Replace Transport) + +**This is the most promising path.** Rust compiles to WebAssembly (Wasm). The browser can run Wasm. The question is: which parts of FIPS can run in Wasm, and which need adaptation? + +### FIPS's Architecture Is Already Separated + +FIPS has clean layer separation (from [`plans/fips-overview.md`](plans/fips-overview.md:38)): + +``` +┌─────────────────────────────────────────────────┐ +│ Applications (SSH, curl, custom) │ +│ ↕ IPv6 TUN adapter (fd00::/8) + .fips DNS │ ← Can't run in browser +├─────────────────────────────────────────────────┤ +│ FSP — FIPS Session Protocol │ +│ End-to-end Noise XK encryption between npubs │ ← Pure computation — CAN run in Wasm +│ Port-based service multiplexing │ +├─────────────────────────────────────────────────┤ +│ FMP — FIPS Mesh Protocol │ +│ Peer auth (Noise IK), spanning tree, │ ← Pure computation — CAN run in Wasm +│ bloom filters, hop-by-hop forwarding │ +├─────────────────────────────────────────────────┤ +│ Transport Layer │ +│ UDP | TCP | Ethernet | Tor | BLE │ ← Can't run in browser — REPLACE with WebSocket +└─────────────────────────────────────────────────┘ +``` + +### What Can Run in Wasm + +| FIPS Component | Wasm Compatible? | Notes | +|---|---|---| +| **secp256k1 crypto** | ✅ Yes | Pure computation. `secp256k1` crate has Wasm support, or use `@noble/secp256k1` | +| **Noise IK / XK handshake** | ✅ Yes | Pure computation — key derivation, encryption, decryption | +| **FSP (session protocol)** | ✅ Yes | Framing, port multiplexing, session management — all pure computation | +| **FMP (mesh protocol)** | ✅ Yes | Spanning tree, bloom filters, routing logic — all pure computation | +| **npub → IPv6 derivation** | ✅ Yes | Bech32 + SHA-256 — trivial in Wasm | +| **Transport layer (UDP/TCP/BLE)** | ❌ No | Raw sockets not available in browser | +| **TUN interface** | ❌ No | Can't create network interfaces | +| **DNS resolver** | ❌ No | Can't listen on ports | +| **tokio async runtime** | ❌ No | `tokio` doesn't work in Wasm; need browser async (Promises) | + +### What Needs to Change + +The key insight: **FIPS's transport layer is already a trait/interface** with a minimal API (send bytes, receive bytes, report MTU). The protocol layers are transport-agnostic. So we need to: + +1. **Compile the protocol logic (FSP + FMP + crypto) to Wasm** — This is the valuable code we don't want to rewrite +2. **Implement a WebSocket transport adapter** — A new transport implementation that uses WebSocket instead of UDP/TCP. It implements the same trait interface. +3. **Replace tokio with browser async** — The async runtime needs to change from `tokio` to browser-native async (Promises, `setTimeout`, WebSocket events). This is the most invasive change. +4. **Skip TUN and DNS** — The extension doesn't need these. It intercepts HTTP requests at the browser API level instead of at the network layer. + +### How Much Code Reuse Is Realistic? + +This depends on how FIPS is structured. If FIPS is structured as: +- A **library crate** (`fips-core` or similar) containing the protocol logic +- A **binary crate** (`fipsd`) containing the daemon (TUN, DNS, systemd, CLI) + +Then compiling the library crate to Wasm is feasible. The library would need: +- `#![no_std]` or conditional compilation for Wasm-incompatible parts +- A WebSocket transport implementation (new code, but small — the transport interface is minimal) +- Async runtime abstraction (replace `tokio` with a trait that can use either `tokio` or browser async) + +If FIPS is a monolithic binary with protocol logic and system code intertwined, then more refactoring is needed to separate the Wasm-compatible parts. + +**The transport interface is the key.** From [`plans/fips-overview.md`](plans/fips-overview.md:117): + +> FIPS treats transports as dumb pipes. Each transport implements: send bytes, receive bytes, report MTU. The mesh layer handles everything above that. + +This means a WebSocket transport is just another implementation of that interface. The protocol layers don't know or care that the transport is WebSocket instead of UDP. + +### The Async Runtime Challenge + +The biggest obstacle is the async runtime. FIPS likely uses `tokio` throughout for async I/O. `tokio` doesn't compile to Wasm because it relies on OS-level I/O (epoll, kqueue, IOCP). + +Options: +- **`wasm-bindgen-futures`** — Bridge between Rust futures and JavaScript Promises. The Wasm module can expose async functions that return Promises. +- **Callback-based** — The Wasm module exposes synchronous functions and calls back into JavaScript when data arrives on the WebSocket. +- **`wasm-bindgen` + `js-sys`** — Use JavaScript APIs directly from Rust via `wasm-bindgen`. WebSocket operations become calls to `js-sys::WebSocket`. + +This is the most invasive change but it's mechanical — replacing `tokio::net::UdpSocket` with `js_sys::WebSocket` calls, replacing `tokio::spawn` with callback registration, etc. + +### What the Architecture Looks Like + +``` +┌──────────────────────────────────────────────────────┐ +│ Browser Extension (JavaScript) │ +│ │ +│ ┌─────────────┐ ┌─────────────────────────────┐ │ +│ │ Content │ │ FIPS Wasm Module │ │ +│ │ Script │───▶│ (compiled from Rust) │ │ +│ │ (intercepts │ │ │ │ +│ │ .fips HTTP │ │ ┌───────────────────────┐ │ │ +│ │ requests) │ │ │ FSP + Noise XK │ │ │ +│ └─────────────┘ │ │ (end-to-end encrypt) │ │ │ +│ │ └───────────────────────┘ │ │ +│ ┌─────────────┐ │ ┌───────────────────────┐ │ │ +│ │ WebSocket │◀──▶│ │ WebSocket Transport │ │ │ +│ │ (to boot- │ │ │ (new, implements the │ │ │ +│ │ strap node)│ │ │ transport trait) │ │ │ +│ └─────────────┘ │ └───────────────────────┘ │ │ +│ │ ┌───────────────────────┐ │ │ +│ │ │ npub → IPv6 │ │ │ +│ │ │ (bech32 + SHA-256) │ │ │ +│ │ └───────────────────────┘ │ │ +│ └─────────────────────────────┘ │ +└──────────────────────────────────────────────────────┘ + │ + │ WebSocket connection + ▼ +┌──────────────────────────────────────────────────────┐ +│ FIPS Bootstrap Node │ +│ (regular FIPS node with WebSocket transport support)│ +│ Receives session packets, routes through mesh │ +└──────────────────────────────────────────────────────┘ +``` + +### What the Bootstrap Node Needs + +The bootstrap node needs to accept WebSocket connections from browser clients. Options: + +**Option A: FIPS adds a WebSocket transport** +- Add a WebSocket implementation to FIPS's transport layer +- The bootstrap node listens for WebSocket connections and treats them as FIPS peers +- This is the cleanest approach — WebSocket is just another transport + +**Option B: Small bridge service** +- A tiny service (Rust, Go, or even Node.js) that accepts WebSocket connections and bridges them to FIPS's TCP transport +- The bridge does WebSocket ↔ TCP conversion +- FIPS itself doesn't need to change +- The bridge runs alongside `fipsd` on the bootstrap node + +Option A is better long-term (WebSocket is a natural transport for browser clients), but Option B works without modifying FIPS. + +--- + +## Path 3: Compile FIPS to Wasm + Run in a Service Worker (Full Browser Node) + +A service worker can run persistently in the background. If the FIPS Wasm module runs in a service worker, it can: +- Maintain persistent WebSocket connections to bootstrap nodes +- Handle `.fips` requests intercepted by the extension +- Cache responses +- Run even when no tab is open + +This is essentially **running a FIPS node inside the browser** — a lightweight node that doesn't participate in mesh routing (no relaying for others) but can create end-to-end encrypted sessions to any FIPS destination. + +The service worker + Wasm approach means: +- No native install required +- Full end-to-end encryption (Noise XK) +- The extension IS a FIPS client +- The protocol code is reused from the Rust implementation (compiled to Wasm) +- Only the transport layer is new (WebSocket instead of UDP/TCP) + +--- + +## Comparison of All Paths + +| Path | Requires Install? | Reuses Rust Code? | E2E Encryption? | Effort | +|---|---|---|---|---| +| Native Messaging | Yes (fipsd + host) | Yes (runs fipsd) | Yes (via fipsd) | Low | +| Wasm (protocol only) | No | Yes (protocol compiled to Wasm) | Yes | Medium-High | +| Wasm + Service Worker | No | Yes (protocol compiled to Wasm) | Yes | Medium-High | +| Rewrite in JS | No | No | Yes | High | +| Gateway proxy | No | N/A (gateway runs fipsd) | No (gateway sees traffic) | Low-Medium | + +--- + +## The Pragmatic Path + +Here's what I'd recommend, combining the approaches: + +### Phase 1: Gateway Proxy + Native Messaging +- **Gateway proxy** for daemon-free access (simple, works immediately) +- **Native messaging** for users who already run fipsd (full features, status, peer management) +- Ship something useful quickly + +### Phase 2: FIPS Wasm Module +- Compile FIPS protocol logic (FSP, Noise, crypto) to Wasm +- Implement WebSocket transport adapter +- Add WebSocket transport to FIPS (or bridge service) +- Extension uses Wasm module for end-to-end encrypted access without a daemon +- This is the "best of both worlds" — reuses Rust code, no install, full encryption + +### Phase 3: Service Worker Integration +- Run the Wasm module in a service worker for persistent background operation +- Full browser-based FIPS client + +--- + +## What We Need to Know About the FIPS Codebase + +To assess how feasible the Wasm approach is, we'd need to look at the FIPS Rust source code and answer: + +1. **Is FIPS structured as a library + binary, or a monolithic binary?** If there's a `fips-core` library crate, compiling it to Wasm is much easier. + +2. **How tightly coupled is the async runtime?** If `tokio` is used everywhere, the Wasm port needs an async runtime abstraction layer. If async is contained to the transport layer, it's easier. + +3. **Does the transport layer use a trait/interface?** If so, adding a WebSocket implementation is clean. If transports are hardcoded, refactoring is needed. + +4. **What crates does FIPS depend on?** Some crates don't compile to Wasm (anything using `std::net`, `tokio`, OS-specific APIs). We'd need to check the dependency tree. + +5. **How large is the protocol logic (FSP + FMP + crypto)?** This determines the Wasm binary size and whether it's practical to load in an extension. + +--- + +## Open Questions + +1. **Is the FIPS source code available to examine?** The plans reference `https://github.com/jmcorgan/fips.git` — is this the right repo, and can we look at its structure? + +2. **Does FIPS already have a WebSocket transport, or would we need to add one?** The overview mentions UDP, TCP, Ethernet, Tor, BLE — no WebSocket. + +3. **How is the FIPS control API structured?** If we go with native messaging for existing users, we need to know how `fipsctl` talks to `fipsd`. + +4. **Could the Wasm approach work for FMP (mesh routing) too, or should we limit it to FSP (sessions only)?** A browser client probably doesn't need to participate in mesh routing — it just needs to create sessions through a bootstrap node. This would mean only FSP + crypto needs to compile to Wasm, not FMP.