All four content-over-peer handlers prefer FIPS when the peer is in
our federation and has advertised a FIPS npub; fall back to Tor
otherwise (unknown peers, FIPS daemon down, transient failure).
- content.handle_content_download_peer / _paid: DID-authenticated
fetch, payment token header threaded through both transports.
- content.handle_content_browse_peer / _preview: no DID header by
design (anonymous browse) — still benefits from FIPS when the
peer happens to be federated.
- federation::fips_npub_for_onion: storage helper that looks up a
peer's FIPS npub from the federation nodes file given their onion
address. Suffix-tolerant (`abc` matches `abc.onion`).
Preserves the Tor-only path for truly unknown peers: PeerRequest
returns Err from the Tor branch instead of silently succeeding,
matching the previous behavior when the peer was unreachable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Every federation peer-to-peer call now prefers FIPS (direct ULA dial
over `fips0`, ~LAN latency) and falls back to Tor only on network
failure. Per-method ed25519 signatures are preserved on both
transports so authenticity doesn't change.
- fips::dial::PeerRequest — fluent builder that owns transport
selection. Returns the Response plus the TransportKind that carried
it, so handlers can log or expose which path was used.
- fips::dial::is_service_active — free-standing async probe used by
migration sites (the transport::fips::is_available cache is keyed
to a `&self`, not usable from static contexts).
- federation/sync.rs: sync_with_peer + deploy_to_peer drop the
hand-rolled reqwest::Proxy dance, call PeerRequest instead.
- federation/invites.rs: notify_join takes the remote's fips_npub
(already parsed out of the invite code since v1.4) and dials over
FIPS when available. The "peer-joined" signature domain is
unchanged.
- api/rpc/federation/handlers.rs: DID rotation broadcast loops over
federated peers through PeerRequest; the per-peer result payload
gains a `transport` field so the UI can surface mesh vs. onion.
- api/rpc/tor/mod.rs: onion-address-change propagation is now the
most useful FIPS-first call — fips_npub is stable across onion
rotation, so peers get the new address even when the old onion
is already dead.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bakes the FIPS (Free Internetworking Peering System) mesh daemon into
the node stack, supervised by archipelago alongside Tor. Runs as a
system service, identity derives from the same BIP-39 master seed, and
user-triggered updates track upstream main.
Identity
seed.rs: new HKDF label archipelago/fips/secp256k1/v1 → dedicated
secp256k1 key, distinct from the Nostr-node key for crypto isolation
but still seed-recoverable
identity.rs: writes fips_key[.pub] to /data/identity on onboarding,
chmod 0600; fips_key_exists / load_fips_keys / fips_npub accessors
Transport
TransportKind::Fips=3 inserted between LAN and Tor (Tor bumps to 4)
→ router prefers FIPS over Tor for all peer traffic
PeerRecord gains fips_npub + last_fips fields (serde(default) for
backward-compat with older nodes)
transport/fips.rs: NodeTransport stub, reports unavailable until the
daemon is live so router falls through to Tor cleanly
Federation invites
FederatedNode and FederationInvite carry optional fips_npub
create_invite / accept_invite / peer-joined callback thread it end
to end; signature domain deliberately unchanged — FIPS Noise does
its own session auth, so the unsigned hint only affects path
selection
crate::fips
config.rs: renders /etc/fips/fips.yaml and sudo-installs key material
service.rs: systemctl status/activate/restart/mask wrappers
update.rs: GitHub API check against upstream main; apply stubbed
until per-commit .deb artefact source is decided
RPC + dashboard
fips.status / fips.check-update / fips.apply-update / fips.install /
fips.restart registered in dispatcher
HomeNetworkCard.vue shipped standalone (unmounted — place in Home.vue
when ready); shows state pill, version, FIPS npub, update button,
activate button when key is present but service is down
ISO + systemd
archipelago-fips.service: conditional on key presence, masked by
default — backend unmasks after onboarding writes the key
build-auto-installer-iso.sh: multi-stage Dockerfile builds the FIPS
.deb from jmcorgan/fips main (fail-loud), COPYs it into rootfs, apt
installs it so trixie resolves deps; unit copied + masked
Version bump: 1.3.5 → 1.4.0
Tests: 33 new/updated passing (seed, identity, transport, federation,
fips module, transport::fips).
Known gaps: fips.apply-update returns a clear stub error until
upstream publishes per-commit .deb artefacts; HomeNetworkCard is not
mounted in Home.vue by default.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The .github/workflows/ci.yml Rust job runs cargo fmt --check, clippy
with -D warnings, and tests. All three were failing. This commit:
- Applies rustfmt across the tree (the bulk of the diff — untouched
since the last toolchain bump, so a wide sweep was unavoidable).
- Fixes the correctness-level clippy errors:
container/bitcoin_simulator.rs wildcard-in-or-pattern
container/manifest.rs from_str rename to parse (reserved name)
container/podman_client.rs .get(0) -> .first()
container/runtime.rs manual += collapse
archipelago/src/constants.rs doc-comment → module-doc
api/rpc/package/install.rs stray /// comment above a non-item
container/docker_packages.rs redundant field init
streaming/advertisement.rs missing Metric import in tests
tests/orchestration_tests.rs `vec!` in non-Vec contexts
mesh/listener/dispatch.rs unused store_plain_message import
api/rpc/tor/mod.rs and mesh/steganography.rs: push-after-new → vec!
- Quiets wide legacy surfaces with crate-level allows in main.rs for
stylistic lints (too_many_arguments, type_complexity, doc indent,
enum variant prefix, wildcard-in-or, assertions-on-constants,
drop_non_drop, unused_io_amount, ptr_arg) — these fired in dozens
of places with no correctness payoff and have been churning every
toolchain bump.
- Tags intentional-dead-code helpers: wallet/ and streaming/ modules
are WIP, mesh::send_chunked_payload and DM_V1_MARKER are kept for
rollback compatibility, vpn::get_nostr_vpn_status is surface-area
for a not-yet-landed RPC.
cargo fmt --check, cargo clippy --all-targets --all-features
-- -D warnings, and cargo test --all-features now all pass locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add deploy_secondary() function for deploying to multiple LAN nodes
- --both now deploys to .198 and .253 (previously .198 only)
- Fleet deploy updated for 3 LAN nodes
- Mesh DM fixes: protocol frame format, DM-via-channel routing
- Federation pending requests, discover modal
- VPN status UI improvements
- Image versions and container specs updates
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a node was already known (via link-node) but had an empty onion
address, the peer-joined handler returned early without updating the
onion. Now it patches missing onion/pubkey fields on existing nodes.
Also adds update_node() to federation storage and updates the
architecture comparison doc with system resources, StartOS/umbrelOS
tabs, Web5 section, and comparison view.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>