apply_update was extracting the frontend tarball with
`tar -xzf -C /opt/archipelago`, but the tar contents are the *inside*
of web-ui/ (root entries are ./test-aiui.html, ./assets/, etc.). So
the files landed directly in /opt/archipelago instead of under web-ui/,
and tar bailed on nginx-owned paths mid-extraction. First end-to-end
OTA test (.198) found it: "tar: ./assets/SystemUpdate-…js: Cannot
open: No such file or directory".
Now extracts into web-ui.new, chowns, then atomically swaps: move
existing web-ui → web-ui.bak, then web-ui.new → web-ui. Same pattern
as the manual sideload that's been working.
Frontend: SystemUpdate.vue fake download progress was capped at "<90"
with a Math.random()*15 increment — the last tick could push to
~104.99%. Capped at 95% with a smaller step so it stops at 95 and the
real RPC completion jumps it to 100.
Artefacts:
archipelago a14ad7e4…2a2be3 40361984
archipelago-frontend-1.7.4-alpha.tar.gz 4fb79664…0172e9 76984615
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sidebar version
detect_build_version() no longer reads /opt/archipelago/build-info.txt
first. That file was written by the ISO installer at flash time and
never rewritten by OTA or sideload, so after any binary swap the
sidebar kept advertising whatever the ISO shipped with. Now just
returns env!("CARGO_PKG_VERSION") unconditionally — always matches the
running binary.
FIPS card
The two-column grid in FipsNetworkCard.vue placed version/npub boxes
side-by-side on mobile but the anchor-status panel forced col-span-2,
creating an unbalanced empty column at every desktop width. Anchor
status moves to its own full-width row below the grid. When the
anchor is not reached, a "Reconnect" button appears next to the
status line; it calls fips.restart (45s timeout), waits 5s for the
daemon to come back, then reloads fips.status. Surfaces whether the
restart actually recovered the anchor in a status flash.
Profile picture render
Uploaded profile pictures are stored with an onion-rooted URL so
external Nostr clients can fetch them. The local browser isn't
Tor-routed though, so the <img src> silently 404'd and the UI fell
back to showing initials. Added a displayableUrl() helper on
Web5Identities.vue that rewrites http://<onion>/blob/<cid>[?...] to
same-origin /blob/<cid> for rendering, while the stored URL keeps
its onion prefix so publishing to Nostr still works for external
viewers. Pass-through for data: URLs and already-relative paths.
Identity row title
The identity list header now renders profile.display_name (when set)
and keeps identity.name as a muted parenthetical. Before, only the
internal name was shown and a user who'd customised their Nostr
display_name saw a mismatch between their own UI and what peers
rendered.
Artefacts:
archipelago 99184b95…22dc1b 40350664
archipelago-frontend-1.7.3-alpha.tar.gz 7b933cf4…74a8bc 76987031
Changelog layman-style per the saved feedback.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three user-visible fixes shipped together.
1. update.apply permission-denied
apply_update() was doing fs::copy into /usr/local/bin/archipelago and
tar xzf into /opt/archipelago as the archipelago user — both root-owned.
The backup step succeeded (it wrote to data_dir) but the swap failed
with a silent permission denied, wrapped as "Failed to apply archipelago".
Now uses `sudo install -m 0755` for the binary and `sudo tar -xzf` for
the frontend, plus a post-apply `sudo systemctl --no-block restart
archipelago` scheduled 2s after the RPC reply so the UI sees success.
2. Apply → Install label
en/es locale strings: applyUpdate / applyTitle / applyNow changed from
"Apply" to "Install". Matches the user's mental model and distinguishes
the user-facing verb from the internal apply_update() function.
3. Identity avatar backfill
Identities created before df83163f had profile=None on disk and so
rendered as initials. load_record() now synthesizes an IdentityProfile
with a default picture (identicon for regular identities, the hex node
SVG for derivation_index=0) when profile is missing. The synthetic
profile lives only in the returned record; the file stays untouched so
a later explicit Save persists whatever the user actually chose.
Artefacts:
archipelago 70e5444e…67c589 40381960
archipelago-frontend-1.7.2-alpha.tar.gz 806b027b…358a824 76983699
Changelog rewritten layman-style per saved feedback.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Trivial bump on top of df83163f. No code changes — this exists purely so
the fleet nodes now sitting on 1.7.0-alpha have a real target to exercise
the OTA pipeline against: check → download → apply → restart → state
reconciliation. The binary content differs only in the embedded
CARGO_PKG_VERSION string.
Frontend tarball reused from v1.7.0-alpha (same bytes, copied to a new
filename to match the manifest component name convention).
Artefacts:
archipelago 7f7981bd…56eef0 40391760
archipelago-frontend-1.7.1-alpha.tar.gz (dup of 1.7.0) dc3b63af…e9a8370 76984288
Manifest changelog is a single plain-language sentence explaining that
this is the test release — per the saved feedback about keeping
fleet-facing strings readable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two fixes bundled into the OTA:
1. update.download hard-fail on git-path nodes. handle_update_check's git
branch reported update_available=true + update_method="git" but never
populated state.available_update, so update.download returned "No update
available to download" even though the UI showed one. SystemUpdate.vue
now routes update_method=="git" through update.git-apply (pull+rebuild+
restart via self-update.sh); manifest-path nodes keep the download→apply
flow. i18n strings + confirm modal added for the git path.
2. Reconciler creating containers behind the user's back. On fresh
unbundled installs (.198, .253) archy-mempool-db and archy-btcpay-db
materialised ~10 min after first boot because reconcile-containers.sh
walked container-specs.sh's canonical tier list and created any
"missing" container. reset_spec() now defaults SPEC_OPTIONAL="true",
so reconcile is strictly a repair tool — baseline comes from
first-boot-containers.sh (filebrowser on unbundled), everything else
from the install RPC.
Also forces OTA trigger for nodes on 1.6.0-alpha that otherwise saw
"I'm at manifest.version, nothing to do" and skipped the refreshed 1.6
artifacts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
No functional changes from v1.5.0-alpha — this release exists only to
validate the in-app update pipeline end-to-end (manifest check → staged
download → apply → restart → version bump in UI sidebar).
Dropping just the manifest + artifacts; no manual deploy to the fleet.
.116/.228/.253 should notice within 30 min (periodic update-check
interval) and surface the update in the dashboard.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Versioning was drifting on three axes — fixed all of them:
1. Cargo.toml → 1.5.0-alpha (was 1.5.0). User wants `-alpha` suffix
on every pre-stable release; this is the current state of main.
2. neode-ui/package.json was still 1.3.5 — brought in line.
3. /opt/archipelago/build-info.txt was stale on .198 (1.3.4) and
.253 (1.3.5), absent on .116/.228. That file OVERRIDES the
binary's CARGO_PKG_VERSION for the UI sidebar, which is why
.198/.253 kept showing old versions even with fresh binaries.
scripts/deploy-to-target.sh now writes build-info.txt on every
deploy, reading the version straight from Cargo.toml — so the
sidebar can never drift from the binary again.
Release artifacts + manifest:
- releases/v1.5.0-alpha/archipelago (40M, sha in manifest)
- releases/v1.5.0-alpha/archipelago-frontend-1.5.0-alpha.tar.gz (51M)
- releases/manifest.json bumped with full 7-line changelog covering
FIPS-first routing, Settings toggle, transitive federation, cancel
button, transport badges, peer listener, and the build-info fix.
- scripts/check-release-manifest.sh — new pre-publish guard. Refuses
to pass if: Cargo.toml ≠ manifest version, changelog is empty
(release notes are mandatory), or any component's sha256/size
doesn't match the file on disk. Run locally or from CI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Every federated node card now shows a colored badge indicating how
archipelago actually reached the peer on the most recent successful
call — FIPS / TOR / LAN / MESH — not a prediction based on available
addresses. The badge is hidden when we've never reached the peer.
Backend:
- Cargo.toml: 1.4.0 → 1.5.0 (visible in the sidebar health endpoint).
- FederatedNode gains last_transport + last_transport_at (serde
default for back-compat with v1.4 nodes.json files).
- federation::storage::record_peer_transport(did, onion, transport)
— writes both fields plus last_seen after each successful peer
call. Matches by DID first, falls back to onion.
- federation::sync::sync_with_peer now calls record_peer_transport
immediately after a successful PeerRequest return, so the badge
on the sync'ing peer's card reflects the transport the call
actually rode (fips vs tor).
Frontend:
- types.ts FederatedNode gains last_transport / last_transport_at
(union-typed to the four known kinds).
- NodeList.vue: new transportBadge(node) returns {label, cls, title}
tuned per transport. Hidden when last_transport is absent so we
never lie. Tooltip shows "Last reached via <x> · <time ago>" so
stale data is self-evident. Removed the predictive icon from the
transport store — badge is now 100% ground-truth.
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 nvpn daemon config at /var/lib/archipelago/nostr-vpn/ is owned by
root, but the backend runs as archipelago. Direct write silently failed,
so adding a second phone's npub never reached the daemon — service
restarted with stale config. Now falls back to sudo cp for root-owned
paths, and first-boot sets ownership to archipelago.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace fragmented random key generation with a single 24-word BIP-39
mnemonic that deterministically derives all node keys: Ed25519 (DID),
secp256k1 (Nostr/Bitcoin), BIP-84 xprv (Bitcoin Core), and LND aezeed
entropy. New onboarding flow: seed generate → word verification → identity
naming. Restore path enabled via 24-word entry. Includes seed RPC handlers,
mock backend support, LND/Bitcoin Core wallet-from-seed integration, and
UI polish across settings and discover views.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- BUG-33: CPU load alert threshold increased from 2x to 4x core count
(8→16 on 4-core machine) to reduce false alerts during container ops
- TASK-27: Launch buttons for new-tab apps now show external link icon
(BTCPay, Grafana, PhotoPrism, Portainer, OnlyOffice, etc.)
- TASK-36: Iframe error screen now distinguishes between X-Frame-Options
blocked vs container not reachable, with appropriate messaging
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Added new dependencies: `adler2`, `crc32fast`, `flate2`, `miniz_oxide`, and `libredox`.
- Updated existing dependencies: `tokio-rustls` to version 0.26.4 and `filetime` to version 0.2.27.
- Removed the `backup.rs` file as it is no longer needed.
- Introduced tests for configuration and credential management.
- Enhanced the `identity` module to generate W3C compliant DID documents.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add electrs to marketplace as standalone installable app
- Add dependency check: refuse install if no bitcoin node is running
- Use container DNS (bitcoin-knots:8332) on archy-net instead of host IP
- Auto-create bitcoin.conf with txindex + RPC on bitcoin-knots install
- Auto-build and start electrs-ui container post-install
- Show index size and estimated progress during initial sync
- Add /electrs-status and /health nginx proxy routes
- Remove Tailwind CDN from electrs-ui, use inline styles
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Added a new method to reset the onboarding state, allowing users to re-initiate the onboarding process.
- Integrated backup creation functionality, enabling users to create encrypted backups of their node identity.
- Updated API endpoints to handle onboarding reset and backup creation requests.
- Enhanced UI components to support the new onboarding reset and backup features, including error handling and user feedback.
- Introduced new dependencies for cryptographic operations and data encoding.
- Upgraded Fedimint version to v0.10.0 in docker-compose.yml and manifest.yml, adding support for the built-in Guardian UI.
- Modified .gitignore to exclude deploy-config.sh script.
- Enhanced onboarding process in AuthManager to persist onboarding state and validate password strength during user setup.
- Updated API to handle onboarding completion and password change requests, ensuring a smoother user experience.
- Improved configuration management to support Nostr discovery and Tor proxy settings, enhancing node identity features.
- Updated BUILD-GUIDE.md to clarify instructions for building the Archipelago Auto-Installer ISO, emphasizing the recommended method of building directly on the target server.
- Added auto-installation of missing dependencies (xorriso, podman) when running the build script with sudo.
- Enhanced the build-auto-installer-iso.sh script to capture container images from the live server, ensuring the ISO includes the same set of applications as the dev server.
- Revised deployment documentation to stress the importance of building the Rust backend on the Linux dev server and included new instructions for capturing system-level changes for ISO builds.
- Improved UI components and added new bundled applications (BTCPay Server, Mempool Explorer, Nostr Relay, Strfry Relay, Tailscale) to enhance user experience.
- Revamped GETTING_STARTED.md for clarity and completeness, detailing the Docker development environment and installation steps.
- Updated Cargo.lock and Cargo.toml to replace deprecated dependencies and add new ones, including hyper-ws-listener and env_logger.
- Improved WebSocket handling in the API to support upgrades and error management.
- Enhanced Neode UI scripts to manage Docker containers during development.
- Adjusted dummy app configurations for accurate LAN addresses.
- Sorted app entries in the UI for better organization and accessibility.