feat(fips): integrate jmcorgan/fips as preferred non-Tor transport + v1.4.0
Some checks failed
Build Archipelago ISO (dev) / build-iso (push) Failing after 2s
Some checks failed
Build Archipelago ISO (dev) / build-iso (push) Failing after 2s
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>
This commit is contained in:
160
neode-ui/src/views/home/HomeNetworkCard.vue
Normal file
160
neode-ui/src/views/home/HomeNetworkCard.vue
Normal file
@@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<div
|
||||
data-controller-container
|
||||
tabindex="0"
|
||||
class="home-card controller-focusable"
|
||||
:class="{ 'home-card-animate': animate }"
|
||||
style="--card-stagger: 5"
|
||||
>
|
||||
<div class="home-card-shell">
|
||||
<div class="home-card-inner p-6 flex flex-col h-full min-h-0">
|
||||
<div class="home-card-header flex items-start justify-between mb-4 shrink-0">
|
||||
<div class="home-card-text">
|
||||
<h2 class="text-xl font-semibold text-white mb-1">Network</h2>
|
||||
<p class="text-sm text-white/70">FIPS mesh — preferred over Tor for peer traffic</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-2" :title="statusLabel">
|
||||
<span class="w-2 h-2 rounded-full" :class="statusDotColor"></span>
|
||||
<span class="text-sm font-medium" :class="statusTextColor">{{ statusLabel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="home-card-stats grid grid-cols-1 sm:grid-cols-2 gap-4 mb-4 flex-1 min-h-0">
|
||||
<div class="p-4 bg-white/5 rounded-lg">
|
||||
<p class="text-xs text-white/60 mb-1">Daemon version</p>
|
||||
<p class="text-sm font-medium text-white break-all">{{ status.version || '—' }}</p>
|
||||
<p v-if="!status.installed" class="text-xs text-white/40 mt-1">Package not installed</p>
|
||||
</div>
|
||||
<div class="p-4 bg-white/5 rounded-lg">
|
||||
<p class="text-xs text-white/60 mb-1">FIPS npub</p>
|
||||
<p class="text-sm font-mono text-white break-all">{{ npubDisplay }}</p>
|
||||
<p v-if="!status.key_present" class="text-xs text-white/40 mt-1">Unlock your seed to derive the FIPS key</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="updateInfo" class="mb-3 p-3 bg-white/5 rounded-lg border-l-2 border-orange-400">
|
||||
<p class="text-xs text-orange-400 font-medium mb-1">{{ updateInfo.update_available ? 'Update available' : 'Up to date' }}</p>
|
||||
<p class="text-xs text-white/70 break-all">{{ updateInfo.notes }}</p>
|
||||
</div>
|
||||
<div v-if="statusMessage" class="mb-3 p-3 rounded-lg text-xs" :class="statusIsError ? 'bg-red-400/10 text-red-300' : 'bg-green-400/10 text-green-300'">{{ statusMessage }}</div>
|
||||
|
||||
<div class="home-card-buttons flex gap-2 mt-auto pt-4 shrink-0">
|
||||
<button
|
||||
class="home-card-btn flex-1 px-4 py-2 glass-button rounded-lg text-sm font-medium transition-colors"
|
||||
:disabled="checking"
|
||||
@click="checkForUpdate"
|
||||
>{{ checking ? 'Checking…' : 'Check for update' }}</button>
|
||||
<button
|
||||
v-if="status.key_present && !status.service_active"
|
||||
class="home-card-btn flex-1 px-4 py-2 glass-button rounded-lg text-sm font-medium transition-colors"
|
||||
:disabled="installing"
|
||||
@click="installAndActivate"
|
||||
>{{ installing ? 'Installing…' : 'Activate' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { rpcClient } from '@/api/rpc-client'
|
||||
|
||||
defineProps<{ animate: boolean }>()
|
||||
|
||||
interface FipsStatus {
|
||||
installed: boolean
|
||||
version: string | null
|
||||
service_state: string
|
||||
service_active: boolean
|
||||
key_present: boolean
|
||||
npub: string | null
|
||||
}
|
||||
|
||||
interface UpdateCheck {
|
||||
current: string | null
|
||||
latest_commit: string
|
||||
update_available: boolean
|
||||
notes: string
|
||||
}
|
||||
|
||||
const status = ref<FipsStatus>({
|
||||
installed: false,
|
||||
version: null,
|
||||
service_state: 'unknown',
|
||||
service_active: false,
|
||||
key_present: false,
|
||||
npub: null,
|
||||
})
|
||||
const updateInfo = ref<UpdateCheck | null>(null)
|
||||
const checking = ref(false)
|
||||
const installing = ref(false)
|
||||
const statusMessage = ref('')
|
||||
const statusIsError = ref(false)
|
||||
|
||||
const statusLabel = computed(() => {
|
||||
if (!status.value.installed) return 'not installed'
|
||||
if (!status.value.key_present) return 'awaiting seed'
|
||||
if (status.value.service_active) return 'active'
|
||||
return status.value.service_state
|
||||
})
|
||||
|
||||
const statusDotColor = computed(() => {
|
||||
if (status.value.service_active) return 'bg-green-400'
|
||||
if (!status.value.installed || !status.value.key_present) return 'bg-white/30'
|
||||
return 'bg-orange-400'
|
||||
})
|
||||
|
||||
const statusTextColor = computed(() => {
|
||||
if (status.value.service_active) return 'text-green-400'
|
||||
if (!status.value.installed || !status.value.key_present) return 'text-white/50'
|
||||
return 'text-orange-400'
|
||||
})
|
||||
|
||||
const npubDisplay = computed(() => {
|
||||
const n = status.value.npub
|
||||
if (!n) return '—'
|
||||
return n.length > 20 ? `${n.slice(0, 12)}…${n.slice(-6)}` : n
|
||||
})
|
||||
|
||||
function flash(msg: string, isError = false) {
|
||||
statusMessage.value = msg
|
||||
statusIsError.value = isError
|
||||
setTimeout(() => { statusMessage.value = '' }, 6000)
|
||||
}
|
||||
|
||||
async function loadStatus() {
|
||||
try {
|
||||
status.value = await rpcClient.call<FipsStatus>({ method: 'fips.status' })
|
||||
} catch (e) {
|
||||
if (import.meta.env.DEV) console.warn('fips.status failed', e)
|
||||
}
|
||||
}
|
||||
|
||||
async function checkForUpdate() {
|
||||
checking.value = true
|
||||
try {
|
||||
updateInfo.value = await rpcClient.call<UpdateCheck>({ method: 'fips.check-update' })
|
||||
} catch (e: unknown) {
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
flash(`Update check failed: ${msg}`, true)
|
||||
} finally {
|
||||
checking.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function installAndActivate() {
|
||||
installing.value = true
|
||||
try {
|
||||
status.value = await rpcClient.call<FipsStatus>({ method: 'fips.install' })
|
||||
flash('FIPS installed and activated')
|
||||
} catch (e: unknown) {
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
flash(`Install failed: ${msg}`, true)
|
||||
} finally {
|
||||
installing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadStatus)
|
||||
</script>
|
||||
Reference in New Issue
Block a user