feat(fips): integrate jmcorgan/fips as preferred non-Tor transport + v1.4.0
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>
2026-04-18 22:57:51 -04:00
//! RPC handlers for the FIPS mesh transport subsystem.
//!
//! Surface is deliberately thin: a read-only `fips.status`, a user-gated
//! `fips.check-update`, a stubbed `fips.apply-update`, and a
//! `fips.install` that (re-)materialises the daemon config + key and
//! activates the service. All writes go through `sudo` helpers in
//! `crate::fips`.
use super ::RpcHandler ;
use crate ::fips ;
use anyhow ::Result ;
impl RpcHandler {
pub ( super ) async fn handle_fips_status ( & self ) -> Result < serde_json ::Value > {
2026-04-21 07:08:26 -04:00
let status = fips ::FipsStatus ::query ( & self . config . data_dir ) . await ;
feat(fips): integrate jmcorgan/fips as preferred non-Tor transport + v1.4.0
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>
2026-04-18 22:57:51 -04:00
Ok ( serde_json ::to_value ( status ) ? )
}
pub ( super ) async fn handle_fips_check_update ( & self ) -> Result < serde_json ::Value > {
let check = fips ::update ::check ( ) . await ? ;
Ok ( serde_json ::to_value ( check ) ? )
}
pub ( super ) async fn handle_fips_apply_update ( & self ) -> Result < serde_json ::Value > {
fips ::update ::apply ( ) . await ? ;
Ok ( serde_json ::json! ( { " applied " : true } ) )
}
/// Install config + key into /etc/fips and activate the service.
/// Intended to be called:
/// - once by the seed-onboarding flow, right after the FIPS key
/// is written to /data/identity/fips_key, and
/// - on user demand from the dashboard if something drifted.
pub ( super ) async fn handle_fips_install ( & self ) -> Result < serde_json ::Value > {
let identity_dir = fips ::identity_dir_from ( & self . config . data_dir ) ;
fips ::config ::install ( & identity_dir ) . await ? ;
fips ::service ::activate ( fips ::SERVICE_UNIT ) . await ? ;
2026-04-21 07:08:26 -04:00
let status = fips ::FipsStatus ::query ( & self . config . data_dir ) . await ;
feat(fips): integrate jmcorgan/fips as preferred non-Tor transport + v1.4.0
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>
2026-04-18 22:57:51 -04:00
Ok ( serde_json ::to_value ( status ) ? )
}
2026-04-21 07:08:26 -04:00
/// Restart whichever fips unit is supervising the daemon on this host.
/// Nodes installed from the archipelago ISO use `archipelago-fips.service`;
/// nodes that had the upstream debian package set up first may only have
/// `fips.service`. We resolve the active one via `service::active_unit()`
/// so the UI button is never a no-op.
feat(fips): integrate jmcorgan/fips as preferred non-Tor transport + v1.4.0
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>
2026-04-18 22:57:51 -04:00
pub ( super ) async fn handle_fips_restart ( & self ) -> Result < serde_json ::Value > {
2026-04-21 07:08:26 -04:00
let unit = fips ::service ::active_unit ( ) . await ;
fips ::service ::restart ( unit ) . await ? ;
Ok ( serde_json ::json! ( { " restarted " : true , " unit " : unit } ) )
feat(fips): integrate jmcorgan/fips as preferred non-Tor transport + v1.4.0
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>
2026-04-18 22:57:51 -04:00
}
2026-04-20 16:40:25 -04:00
/// Full reconnect: stop the daemon, bring it back, wait for the DHT
/// bootstrap window, poll the identity-cache + peer list, and
/// classify what recovered (or didn't) so the UI can explain it to
/// the user instead of showing a generic failure.
///
/// Runtime: ~20s. Needs an RPC timeout ≥ 45s on the client.
pub ( super ) async fn handle_fips_reconnect ( & self ) -> Result < serde_json ::Value > {
let identity_dir = fips ::identity_dir_from ( & self . config . data_dir ) ;
2026-04-21 07:08:26 -04:00
let before = fips ::FipsStatus ::query ( & self . config . data_dir ) . await ;
2026-04-20 16:40:25 -04:00
// Heal the pre-fix bech32-text fips_key.pub → 32-raw-bytes
// mismatch. The daemon silently authenticates with a garbage
// pubkey when the .pub file is 63-char text, which looks like
// "anchor unreachable" to the user even though the real fault
// was an identity malformed on the node itself. Re-install the
// config + keys so /etc/fips gets the healed .pub.
let key_src = identity_dir . join ( " fips_key " ) ;
let pub_src = identity_dir . join ( " fips_key.pub " ) ;
if key_src . exists ( ) {
let _ = fips ::config ::normalize_pub_file ( & key_src , & pub_src ) . await ;
// Re-install refreshes /etc/fips/fips.pub from the healed
// source. No-op if nothing changed.
let _ = fips ::config ::install ( & identity_dir ) . await ;
}
2026-04-21 07:08:26 -04:00
// Operate on whichever fips unit is actually up — nodes that
// have the upstream `fips.service` rather than the
// archipelago-managed `archipelago-fips.service` used to see
// Reconnect silently fail because we stopped a unit that
// didn't exist. Clean stop+start rather than `restart` so a
// daemon that fails to come back up surfaces as
// service_active=false instead of quietly sticking with the
// old process.
let unit = fips ::service ::active_unit ( ) . await ;
let _ = fips ::service ::stop ( unit ) . await ;
2026-04-20 16:40:25 -04:00
tokio ::time ::sleep ( std ::time ::Duration ::from_millis ( 800 ) ) . await ;
2026-04-21 07:08:26 -04:00
fips ::service ::activate ( unit ) . await ? ;
// Re-push seed anchors after restart so freshly-bound daemons
// don't have to wait 5 min for the periodic apply loop.
if let Ok ( list ) = fips ::anchors ::load ( & self . config . data_dir ) . await {
if ! list . is_empty ( ) {
let _ = fips ::anchors ::apply ( & list ) . await ;
}
}
2026-04-20 16:40:25 -04:00
// Anchor bootstrap window: poll the status every ~3s for up to
// 20s. Bail as soon as the anchor is connected.
let mut last_status : Option < fips ::FipsStatus > = None ;
let deadline = std ::time ::Instant ::now ( ) + std ::time ::Duration ::from_secs ( 20 ) ;
loop {
tokio ::time ::sleep ( std ::time ::Duration ::from_secs ( 3 ) ) . await ;
2026-04-21 07:08:26 -04:00
let s = fips ::FipsStatus ::query ( & self . config . data_dir ) . await ;
2026-04-20 16:40:25 -04:00
if s . anchor_connected {
last_status = Some ( s ) ;
break ;
}
last_status = Some ( s ) ;
if std ::time ::Instant ::now ( ) > = deadline {
break ;
}
}
let after = last_status . unwrap_or_else ( | | before . clone ( ) ) ;
let recovered = after . anchor_connected & & ! before . anchor_connected ;
let likely_cause = if after . anchor_connected {
" connected "
} else if ! after . service_active {
" daemon_down "
} else if ! after . key_present {
" no_seed_key "
} else if after . authenticated_peer_count = = 0 {
// Daemon is up with a key but hasn't authenticated any
// peers — almost always outbound UDP/8668 dropped by the
// local firewall/router, or the anchor itself being down.
" no_outbound_udp_or_anchor_down "
} else {
" peers_but_no_anchor "
} ;
let hint = match likely_cause {
2026-04-21 07:08:26 -04:00
" connected " = > " An anchor is reachable. " ,
" daemon_down " = > " The FIPS daemon didn't come back up — check the FIPS service on this host. " ,
2026-04-20 16:40:25 -04:00
" no_seed_key " = > " No seed-derived FIPS key on disk. Re-run the onboarding unlock step. " ,
" no_outbound_udp_or_anchor_down " = >
2026-04-21 07:08:26 -04:00
" Daemon is running but no peers handshook. Your router / ISP might be blocking outbound UDP 8668, or every configured anchor could be down. Add a reachable peer in Seed Anchors. " ,
2026-04-20 16:40:25 -04:00
" peers_but_no_anchor " = >
2026-04-21 07:08:26 -04:00
" Mesh has peers but none of them are anchors we recognise. Add your cluster's anchor in Seed Anchors. " ,
2026-04-20 16:40:25 -04:00
_ = > " " ,
} ;
Ok ( serde_json ::json! ( {
" recovered " : recovered ,
" likely_cause " : likely_cause ,
" hint " : hint ,
" before " : before ,
" after " : after ,
} ) )
}
2026-04-21 06:21:37 -04:00
/// List the seed-anchor entries configured on this node.
pub ( super ) async fn handle_fips_list_seed_anchors ( & self ) -> Result < serde_json ::Value > {
let list = fips ::anchors ::load ( & self . config . data_dir ) . await ? ;
Ok ( serde_json ::json! ( { " seed_anchors " : list } ) )
}
/// Add (or update) a seed anchor and immediately push it into the
/// running daemon. Params: `{ npub, address, transport?, label? }`.
pub ( super ) async fn handle_fips_add_seed_anchor (
& self ,
params : & serde_json ::Value ,
) -> Result < serde_json ::Value > {
let anchor : fips ::anchors ::SeedAnchor = serde_json ::from_value ( params . clone ( ) )
. map_err ( | e | anyhow ::anyhow! ( " bad seed anchor payload: {} " , e ) ) ? ;
if ! anchor . npub . starts_with ( " npub1 " ) {
anyhow ::bail! ( " npub must be bech32 (npub1...) " ) ;
}
if ! anchor . address . contains ( ':' ) {
anyhow ::bail! ( " address must be host:port (e.g. 192.168.1.116:8668) " ) ;
}
2026-04-28 15:00:58 -04:00
let list = fips ::anchors ::add ( & self . config . data_dir , anchor . clone ( ) ) . await ? ;
2026-04-21 06:21:37 -04:00
// Push just the newly-added anchor into the running daemon so
// the user sees effect without waiting for the periodic apply.
let results = fips ::anchors ::apply ( & [ anchor ] ) . await ;
Ok ( serde_json ::json! ( {
" seed_anchors " : list ,
" apply " : results . iter ( ) . map ( | r | {
serde_json ::json! ( { " npub " : r . npub , " ok " : r . ok , " message " : r . message } )
} ) . collect ::< Vec < _ > > ( ) ,
} ) )
}
/// Remove a seed anchor by npub. Params: `{ npub }`. Does NOT tear
/// down an already-authenticated peer connection — it only stops
/// us from re-dialing the anchor on the next apply cycle.
pub ( super ) async fn handle_fips_remove_seed_anchor (
& self ,
params : & serde_json ::Value ,
) -> Result < serde_json ::Value > {
let npub = params
. get ( " npub " )
. and_then ( | v | v . as_str ( ) )
. ok_or_else ( | | anyhow ::anyhow! ( " missing npub " ) ) ? ;
let list = fips ::anchors ::remove ( & self . config . data_dir , npub ) . await ? ;
Ok ( serde_json ::json! ( { " seed_anchors " : list } ) )
}
/// Re-apply all seed anchors to the running daemon. Useful after a
/// FIPS restart or when the user wants to force a reconnection
/// attempt without waiting for the periodic apply loop.
pub ( super ) async fn handle_fips_apply_seed_anchors ( & self ) -> Result < serde_json ::Value > {
let list = fips ::anchors ::load ( & self . config . data_dir ) . await ? ;
let results = fips ::anchors ::apply ( & list ) . await ;
Ok ( serde_json ::json! ( {
" applied " : results . len ( ) ,
" results " : results . iter ( ) . map ( | r | {
serde_json ::json! ( { " npub " : r . npub , " ok " : r . ok , " message " : r . message } )
} ) . collect ::< Vec < _ > > ( ) ,
} ) )
}
feat(fips): integrate jmcorgan/fips as preferred non-Tor transport + v1.4.0
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>
2026-04-18 22:57:51 -04:00
}