39 Commits
Author SHA1 Message Date
archipelago 9c6580f5c0 fix: prevent stale catalog updates and redundant container recreation
Demo images / Build & push demo images (push) Failing after 40s
2026-09-15 03:40:21 -04:00
archipelago 83abb0485d fix: publish signed mempool DNS recovery catalog 2026-09-15 03:13:02 -04:00
archipelago b35409ca74 fix: recover mempool frontend after backend address changes 2026-09-15 02:49:06 -04:00
archipelago 700d39c425 fix: keep release credentials out of public remote URLs 2026-09-14 10:47:58 -04:00
ssmithxandarchipelago eacd74e1db feat(cuprate-ui): companion dashboard for the Cuprate Monero node
Same companion shape as bitcoin-ui/electrs-ui: host-networked nginx
bound to 127.0.0.1:18091 (auth: gated + session_passthrough), serving
a dark glass status page that polls the node's restricted RPC via a
session-gated /cuprate-rpc/ proxy — sync height/target with progress
bar, peers, mempool, chain size and free disk (from get_info), plus a
wallet 'remote node' endpoint. The offline state explains the disk gate
so a refused node says why.

No secret rendering: the restricted RPC is Monero's safe-for-public
subset, so nginx.conf is baked into the image (no pre_start hook, no
bind mount). companion.rs auto-provisions archy-cuprate-ui alongside
cuprate and reaps it when cuprate goes.

Catalog regenerated (cuprate-ui entry + manifest embed, 18091 into the
mesh launch-port list). NOTE: releases/app-catalog.json is UNSIGNED as
committed — run scripts/sign-catalog.sh before publishing.
2026-09-12 16:14:29 -04:00
archipelago 8f144c3038 chore: remove retired AdGuard app and refresh release docs 2026-09-12 05:05:33 -04:00
archipelago 564ffe1c47 fix(indeedhub): generate per-node encryption root 2026-09-11 11:25:44 -04:00
archipelago dac29baf97 fix(release): surface companion build and secure GitWorkshop deps 2026-09-11 06:10:59 -04:00
archipelago ef8c3a76be chore(release): define 1.8.12 publication gates 2026-09-11 05:37:21 -04:00
archipelago f5c0ba85cd feat(release): stage GitWorkshop and next node updates 2026-09-09 18:15:21 -04:00
archipelago b010471a4a chore(release): prep v1.8.11 notes and link checks 2026-09-07 03:26:01 -04:00
archipelago 2b4b60013c feat(lnd): build LND 0.21.2-beta in-house and sweep the pin
Demo images / Build & push demo images (push) Failing after 40s
Upstream publishes no docker images; our v0.18.4 image was built in-house.
This pass: official v0.21.2-beta release binaries (sha256-verified against
the signed release manifest), static, on alpine with the same entrypoint
shape as our existing image, pushed to our registry and smoke-run.
LND 0.21 auto-migrates the channel DB on first start (keeping a backup) —
the Update button is user-initiated, never auto-applied.
2026-09-01 05:14:50 -04:00
archipelago 19467e9b7c chore(apps): sweep pin bumps — gitea 1.27.3, vaultwarden 1.37.2, filebrowser 2.63.23, home-assistant 2026.8.3, adguardhome 0.107.79, portainer 2.45.0, pine-whisper 3.6.0
Demo images / Build & push demo images (push) Failing after 39s
First upstream sweep since v1.8.5: the safe patch/minor pins, mirrored
into our registry first (source.archipelago-foundation.org/lfg2025/*).
Held for their own careful passes: the majors (grafana 11, nextcloud,
uptime-kuma 2, bitcoin-core 29, the DBs) and consensus-sensitive apps
(fedimint, electrumx 2.0). LND 0.21.2 needs an in-house image build —
upstream publishes none.
2026-09-01 04:50:49 -04:00
archipelago bf6ef9644c chore(apps): retire morphos-server, did-wallet, lightning-stack, cryptpad
Store-listing components are filtered via the shared serviceNames canon;
these four never earn a tile: MorphOS server is old, the Web5 DID wallet
and CryptPad are untested, Lightning Stack is an untracked upstream
bundle (LND covers it).
2026-09-01 02:41:55 -04:00
archipelago fc68c5b680 feat(apps): complete the manifest platform — convert the last five stragglers
Demo images / Build & push demo images (push) Failing after 36s
Nginx Proxy Manager, Tailscale, Ollama, CryptPad, and AdGuard Home now
carry full manifests: the app gate fronts their web ports (TLS on the
same port, node login where appropriate), installs run through the
orchestrator, and pins live in the signed catalog. Tailscale mirrors its
legacy shape exactly (userspace networking, web console on 8240, plain
HTTP for the gate to front). Ollama stays loopback-only — the
assistant's local model backend, not a web app.

Retires the four already-removed apps for good (FIPS, Nostr VPN,
Routstr, Penpot pins dropped from image-versions.sh, the generator map,
and image_versions.rs), fixes Cuprate's duplicated metadata block that
strict YAML parsers reject, and updates the port-inventory review gates
for the new open (3 own-login consoles) and exempt (2 DNS) ports.
2026-08-31 18:40:39 -04:00
archipelago 9cf07e1eac fix(release): enforce the v1.8 What's New floor
Demo images / Build & push demo images (push) Failing after 39s
2026-08-31 15:44:48 -04:00
archipelago 7bc9f69b1f fix(settings): start What's New history at v1.8.0 2026-08-31 15:12:18 -04:00
archipelago 017505c431 fix(release): include every curated changelog item 2026-08-31 14:52:47 -04:00
archipelago 7a39d8fbd1 fix(settings): sort What's New history newest-first
Demo images / Build & push demo images (push) Failing after 36s
2026-08-31 14:50:55 -04:00
archipelago e3275353b9 fix(release): publish assets before exposing manifest 2026-08-31 14:45:29 -04:00
archipelago 3b3500a7dd test(image): gate installer crash-capture payload 2026-08-31 12:38:59 -04:00
archipelago 7b88ba59b2 chore(apps): bump the pins that need no mirroring; curate Cuprate's store entry
Demo images / Build & push demo images (push) Failing after 40s
Pin bumps (all verified pullable from their public registries before
editing, so none can become an image-not-found on a node):

  strfry           1.1.1 -> 1.1.2              (dockurr/strfry, direct pull)
  btcpay-server    2.4.2 -> 2.4.3             (docker.io/btcpayserver, direct pull)
  netbird (nginx)  1.31.3-alpine -> 1.31.4-alpine
  pine   (nginx)   1.31.3-alpine -> 1.31.4-alpine

image-versions.sh moved in lockstep for BTCPAY_IMAGE — it is the baseline
the update badge compares against. Held back deliberately, per the risk
policy from the Aug-17 pass: gitea (four minors of DB migrations),
portainer (six minors), filebrowser (2.27 -> 2.63), fedimint/gateway
(0.8 -> 0.12, real migrations), lnd (money-critical), netbird-server/
netbird-dashboard (0.x, must move in lockstep), and everything with a
major jump or a data migration.

Cuprate also gets its curated store entry (category money, tier optional,
icon, repo) — same shape as the Alby Hub / phoenixd entries — synced
through generate-app-catalog.py into both store catalogs and the
app-session config. The fips launch-port list is unchanged (Cuprate has
no UI port; the generated file round-trips to the committed bytes after
cargo fmt).

Three further bumps are prepared and parked on the
app-bumps-mirror-pending branch, blocked only on a registry-push token:
vaultwarden 1.37.2-alpine, archy-nbxplorer 2.6.11, home-assistant
2026.8.3 — all mirror-backed, and the push credential on record for the
lfg2025 namespace is dead.

Drift gate: check-app-catalog-drift.py --release --strict clean
(31 store entries, 0 drift, 0 missing). appSessionConfig tests 7/7.
2026-08-30 16:22:26 -04:00
archipelago b12d1d3826 feat(apps): track the last untracked apps' upstreams
Five apps had no app.upstream block, so nothing could ever tell us
when their pins fell behind upstream:

  barkd           gitlab ark-bitcoin/bark   (GitLab-only project)
  immich-postgres ghcr  immich-app/postgres (image exists only on ghcr.io)
  indeedhub-minio github minio/minio
  pine-whisper    dockerhub rhasspy/wyoming-whisper
  lightning-stack manual — no public listing exists for
                   lightninglabs/lightning-stack anywhere (docker.io,
                   ghcr.io, github.com all checked), so it is tracked by hand

This adds two fetchers to scripts/check-upstream-releases.py to reach the
first two: latest_gitlab (GitLab releases API; strips the project-name
tag prefix, e.g. bark-0.6.2 -> 0.6.2) and latest_ghcr (anonymous pull
token + tags/list, the same handshake a docker pull performs).

Live-verified after the change:
  barkd            0.3.0 -> 0.6.2   (bump gated on ark_client.rs REST compat)
  immich-postgres  14-vectorchord0.4.3-pgvectors0.2.0 -> 17-vectorchord0.4.3-pgvector0.8.0
  indeedhub-minio  RELEASE.2024-11-07T00-52-20Z -> latest (date-opaque: UNCOMPARABLE, shown for hand comparison)
  pine-whisper     3.4.1 -> 3.6.0   (tuned-args revision needs re-basing, not just a pin move)

Offline coverage check: 59 apps, 0 untracked.
2026-08-30 16:22:11 -04:00
archipelagoandClaude Opus 5 e38b148d8e fix(release): spell out how the mnemonic prompt actually submits
The signer reads stdin to EOF, so pressing Enter submits nothing and a
second paste simply appends to the first. Step [6b/8] said only "paste the
release master mnemonic when prompted", which gives no hint that Ctrl-D is
what ends the input — a 24-word phrase arrived today as "invalid word
count: 89", about four pastes concatenated by someone reasonably assuming
Enter had not worked.

sign-manifest.sh already explains this properly; create-release.sh now says
the same thing, including that pasting twice is itself a failure mode.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 06:27:42 -04:00
archipelagoandClaude Opus 5 e03a2fed89 fix(release): surface frontend build failures instead of hiding them
`npm run build 2>&1 | tail -3` threw away npm's exit status, so a failed
build was indistinguishable from a good one. The run continued and blamed
the next check instead — "the frontend build no-opped or its output is
stale" — which points at a stale dist rather than at the build error that
actually happened, and cost a diagnosis cycle today.

Success still prints the same quiet 3 lines; a failure now prints the real
error, keeps the full log, and aborts on the spot.

Verified both branches with a stubbed npm.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 05:24:37 -04:00
archipelagoandClaude Opus 5 3d7de3e902 fix(release): a dateless changelog header silently skipped the release
Demo images / Build & push demo images (push) Failing after 38s
create-release aborted at [4/8] with "web/dist/neode-ui does not contain
v1.8.4-alpha — the frontend build no-opped or its output is stale". The
build had not no-opped: it was fresh, and simply had no 1.8.4 string to
embed.

sync-whats-new.py only matches '## vX.Y.Z (YYYY-MM-DD)'. The entry read
'## v1.8.4-alpha (draft — date set at cut)', so the version was invisible
to it: the gate's whats-new-sync stage reported "87 versions, all present"
while the release being cut had no What's New block. That modal is the
only place a version string appears in the frontend, so the bundle carried
none and the freshness check — correctly — refused it, while naming the
wrong cause. Step [5/8] only greps for '^## v1.8.4-alpha (' so it passed
the draft too.

Three changes: date the v1.8.4-alpha entry, insert the modal block it was
owed, and make the sync tool refuse any version header without a real date
instead of skipping it. Skipping is what let a wrong "all present" through.

Verified: the draft header now fails the check with an explicit message,
the dated one passes (88 versions, up from 87), and a rebuilt bundle
contains 1.8.4-alpha where it did not before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 03:01:39 -04:00
archipelagoandClaude Opus 5 5ccef0ac2f feat(release): attach the installer ISO to the Gitea release automatically
Publishing the ISO was a manual step printed as a reminder at the end of
build-iso-release.sh: upload the ISO, its .sha256 and the signed checksum
JSON by hand. Only the OTA binary and frontend tarball were automated.

publish-release-assets.sh now uploads all three when an ISO for the
version exists in image-recipe/results/, with the same supply-chain rules
the OTA manifest already gets: the checksum JSON must be signed by the
pinned release root, the signature must cryptographically verify, and the
image must still match its own .sha256 (a truncated or half-copied ISO is
exactly what a signed checksum exists to expose). After upload it
confirms every asset landed at its exact local size.

The stage runs AFTER main is pushed, deliberately. The ISO is not
referenced by releases/manifest.json, so no node's OTA path depends on
it — running it last means a slow or failed multi-GB upload can never
delay or strand an OTA release that has already been verified. When no
ISO exists yet (the usual case, since the ISO build needs the tag this
script pushes) it explains how to build and attach one, and exits clean.

Uploads take a max-time argument: 4h and a progress bar for the ISO,
where the previous fixed 15-minute silent ceiling would have killed a
multi-GB transfer partway through.

Verified with a stubbed harness: no-ISO skip, missing .sha256, unsigned
checksum, wrong signing key, corrupted image, happy path, and a truncated
upload caught by the size check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 01:33:54 -04:00
archipelagoandClaude Opus 5 c19c411b91 chore(apps): mirror and bump the upgrades that carry no data migration
With registry push access, the 24 mirror-backed apps stopped being
blocked. Ten images are now mirrored (single-platform amd64, matching
the existing convention) and their pins moved:

  alby-hub          v1.23.0       -> v1.24.0
  mempool-frontend  v3.0.1        -> v3.3.1     (mempool, archy-mempool-web)
  mempool-backend   v3.0.0        -> v3.3.1
  fedimintd         v0.10.0       -> v0.10.1
  gatewayd          v0.10.0       -> v0.10.1
  nostr-rs-relay    0.9.0         -> 0.10.0
  portainer         2.39.1        -> 2.39.6
  vaultwarden       1.30.0-alpine -> 1.37.1-alpine
  jellyfin          10.8.13       -> 10.11.11
  home-assistant    2026.7.3      -> 2026.8.2

Every one verified pullable from our mirror after copying, so none can
become an image-not-found on a node. image-versions.sh moved in lockstep
— it is the baseline the update badge compares against when the catalog
does not cover an app, and leaving it behind would have kept advertising
an update that had already been applied.

Chosen by risk, not by count: these are patch/minor bumps with no data
migration. The ones held back are held for a reason each — Postgres
15->18 and 16->18 refuse to start on an older cluster, Redis 7->8,
Valkey 7->9, Nextcloud 29->32 must go one major at a time, plus
uptime-kuma 1->2, grafana 10->13, electrumx 1->2, photoprism, and
core-lightning's three years of schema migrations. Those are each a
migration plan, not a pin edit. LND (v0.18.4 -> v0.21.2) is held
separately: it is only a minor bump by version but it migrates its
channel database irreversibly, and this box holds real funds.

Note the checker still reports several of these as behind, and that is
correct: it reads the *catalog* pin, which is what nodes actually act on.
These land when the catalog is regenerated and re-signed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 11:22:42 -04:00
archipelagoandClaude Opus 5 c1a79fdd69 fix(apps): never recommend a release candidate, and flag major jumps
Two things the first run of this script got wrong, both found by reading
its own output rather than by a test.

It recommended MariaDB `13.0.1-ubi10-rc` — a release candidate — because
ordering strips the suffix, so an RC outranks every stable tag
numerically. Pre-releases are now excluded, with one exception that
matters here: a project whose stable line *is* suffixed. LND ships
`-beta` and always has, so a blanket exclusion would report it as
permanently current. The rule is therefore "no pre-release unless the pin
we are on is itself one", which keeps LND honest and MariaDB stable.

And "33 behind" is not an actionable list, because the entries are not
the same kind of work. A patch bump is a pin change; a major bump is
where the data migrations live — Postgres refuses to start on an older
cluster, Nextcloud requires one major at a time. Each row now says which
it is, and the summary names the majors separately.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 09:55:18 -04:00
archipelagoandClaude Opus 5 603291008b fix(test): restore the ecash network before bouncing the service
The restore proof's cleanup set the network back *after* restarting
archipelago, so the call landed on a socket that wasn't listening yet
and failed silently. A fully green run left the node parked on testnet —
the one outcome a cleanup path must never produce, and worse for being
invisible.

Network first, while the RPC is still up; then the wallet file, then the
restart, then wait for the service back so a check running straight
afterwards doesn't meet a dead socket.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 09:15:31 -04:00
archipelagoandClaude Opus 5 e30516316b fix(ecash): give every node a backup phrase, and prove restore works
Demo images / Build & push demo images (push) Failing after 2m11s
Running the route suite on this box surfaced that the backup was
unreachable here: `identity/master_seed.enc` is written during
onboarding, and any node onboarded before that step existed simply does
not have one. Reveal bailed with "this node has no encrypted seed
backup", and restore followed it down.

But the choice on such a node was never "derived phrase or independent
phrase" — it was "independent phrase or no backup at all", and a wallet
whose coins can be restored from words the operator holds beats one
whose coins die with a single file. So it now generates one, recorded as
`independent`, and every surface that shows it says plainly that
restoring the node will not bring the ecash back — only these words
will. `derivable_from_node_seed` lets the card say which kind you are
about to get *before* you write anything down.

Also: a mint that never implemented NUT-09 answered restore with a bare
404, which surfaced as "mint returned 404 with no further detail" —
true, and useless to someone trying to get their coins back. It now
names the limitation.

The route suite was reading `result.amount_sats` from mint-claim, which
answers with `minted_sats`. A working claim had been reporting as a
failure; that was one of the two reds carried over from yesterday.

The real gap, though, was that "recovered 0 sats" passes on a wallet
with nothing to find — exactly the shape of a backup that looks fine
until the day you need it. test-ecash-restore.sh does the test that
settles it: mint, **delete the wallet file**, restore, check the coins
came back. On this box: 87 sats before the wipe, 0 after, 61 recovered
from the phrase alone — every coin minted since the phrase existed, and
none of the 26 sats minted before it, which used random secrets and
never could come back. Testnet only, and it refuses to run otherwise.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 08:27:01 -04:00
archipelagoandClaude Opus 5 cbbd20e22e test(ecash): cover cashuB emission, the backup phrase, and restore
Four things the suite could not previously catch:

- The emitted token is cashuA. It is still valid, so nothing fails — the
  send succeeds and the receiver redeems it. The only symptom of cashuB
  encoding falling back is a warning in the journal nobody reads, which
  is exactly the kind of silent regression a route check exists for.
- The wallet has no backup phrase. Without one the coins live in exactly
  one file and nothing can bring them back.
- The phrase changes between reveals, which would orphan every coin
  minted under the previous one.
- Restore double-counts. It runs against a live wallet, so running it
  twice must leave the balance where it was.

Reveal is also asserted to refuse a wrong password: it is the one route
here that hands out key material, and a session alone must not be enough.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 08:06:27 -04:00
archipelagoandClaude Opus 5 eb48eab946 feat(apps): find out when an app has fallen behind upstream
Nodes offer an update when the signed catalog pins something newer than
what's running, and that machinery is fine. The missing step was the one
before it: nothing told *us* when upstream shipped. A pin could sit at
fedimintd v0.10.0 for months while every node in the fleet correctly and
confidently reported "up to date".

The reason nothing could tell us is that a manifest records only our
mirror — `source.archipelago-foundation.org/lfg2025/fedimintd:v0.10.0`
says nothing about the project it was mirrored from. So this adds an
optional `app.upstream` block naming the real source, and a script that
asks each one what it has released.

Running it answers the question that prompted this. Of 58 apps, 28 are
behind, including LND v0.18.4-beta against v0.21.2-beta, Bitcoin Core
28.4 against 31.1, and fedimintd/gatewayd v0.10.0 against v0.10.1.

Two choices worth stating. An app with no `upstream` block is reported
as UNTRACKED rather than skipped — a silent skip is how this stayed
invisible, and before this commit all 58 were silently skipped. And a
suggestion prefers our own tag variant: telling someone pinned to
`postgres:16.13-alpine` that the newest tag is `18.6-trixie` is true and
useless, because swapping the base image is a different decision from
bumping a version.

Five apps are deliberately left untracked (barkd, immich-postgres,
indeedhub-minio, lightning-stack, pine-whisper): I could not establish
their upstream with confidence, and a wrong `repo` produces a confident
wrong verdict, which is worse than an honest gap.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 08:04:33 -04:00
archipelagoandClaude Fable 5 26638aa621 fix(ecash): sign with the mint's SAT keyset, not whichever came first
`get_active_sat_keyset` picked the first keyset with a non-empty key map,
and `MintKeyset` had no `unit` field to filter on — so on a multi-unit mint
the wallet signed sat-denominated mint/swap requests against a usd or eur
keyset. The mint refuses that with `11013 Unit unsupported`, which is
exactly what claiming minted coins hit against testnut.cashu.space (it
serves usd, eur, msat and sat keysets). Minibits is sat-only, so this
latent bug never surfaced in production — the test-mint switch found it on
its first run.

MintKeyset now carries `unit` and `active`, both defaulted so a sat-only
mint that omits them still parses, and selection filters to sat and prefers
an active keyset.

Also: pin BIP-39 seed derivation to the specification's own test vectors.
The node's entire identity hangs off `Mnemonic::to_seed("")`, and the
`bip39` crate is no longer version-pinned (the exact pin had to be relaxed
so `cashu` could resolve). A bump that changed derivation would silently
re-key every node on the fleet and orphan every backup; both vectors —
empty passphrase and the NFKD-exercising passphrase arm — now fail the
suite instead. Verified byte-identical under the newly resolved 2.2.2.

And the route script polls the mint's quote state before claiming: the test
mint settles its own invoices, but not instantly, so claiming immediately
raced the settlement and reported a spurious "Quote not paid".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 06:07:27 -04:00
archipelagoandClaude Fable 5 be2cfb8293 feat(ecash): adopt the reference NUT-02 resolver + real/test network switch
Demo images / Build & push demo images (push) Failing after 2m26s
Executes steps 1-3 of docs/cashu-cdk-migration-plan.md, plus the test-coin
switch needed to exercise these routes without spending real sats.

Protocol layer: depend on `cashu` 0.17.5 (MIT, the crate CDK is built on,
default-features off, `wallet` only). Keyset ids now go through upstream's
`Id::from_short_keyset_id` / `ShortKeysetId` instead of the prefix match
hand-rolled in 2277fc46 — same repair, but implemented by the reference
code that defines the rule, so the next spec turn is a version bump rather
than another incident. `MintClient` feeds it the mint's `/v1/keysets` in
upstream's own `KeySetInfo` shape, parsing entries individually so one
keyset in an unmodelled unit can't block resolving the id we need.

Adding the crate required relaxing `bip39 = "=2.1.0"` to `"2.1"` (resolves
2.2.2): the exact pin held `unicode-normalization` at 0.1.22 and no
resolution existed otherwise. The pin carried no recorded rationale; seed
tests cover the bump.

Network switch: `wallet.ecash-network` / `wallet.ecash-set-network`, with a
Test mode toggle in Wallet Settings → Cashu. Cashu has no testnet, so this
points the wallet at the public `testnut` mint — but crucially each network
gets its OWN wallet and accepted-mints file, because test and real proofs
in one purse would be spendable interchangeably and the balance would be a
lie. Mainnet keeps the original filenames, so existing funds files are
untouched and switching is reversible: tests assert a real balance survives
a round trip through test mode.

Headless coverage: scripts/test-ecash-routes.sh drives every ecash RPC over
the real HTTP path (network get/set, balance, history, mint quote + claim,
send, receive, double-redeem refusal, garbage input, melt quote), restores
the node's original network on exit, and exits non-zero with the failure
count.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 05:04:25 -04:00
archipelagoandClaude Fable 5 1587853ce2 fix(banner): console banner shows the reachable LAN address, not the WG tunnel IP
The welcome banner picked its address with 'hostname -I | awk {print $1}',
so a node with WireGuard up advertised 10.44.0.1 — its own tunnel address,
present on EVERY node — as its web ui / ssh address. Off-tunnel that is
unreachable, and after a headless box moves to a new network it is exactly
the wrong thing to trust (framework-pt, 2026-08-15).

- Pick the default route's source address; fall back to the first address
  that is not WireGuard 10.44/16, CGNAT 100.64/10, or loopback.
- Also print http://<hostname>.local when avahi is up — the one address
  that survives any DHCP change, which is the real answer for headless
  boxes that move between networks.
- scripts/welcome-banner.sh is the new canonical copy, embedded in the
  binary (tor-helper pattern): bootstrap::run_welcome_banner_sync rewrites
  /etc/profile.d/archipelago.sh on ISO-installed nodes at startup, so the
  fix reaches the deployed fleet with the next OTA instead of only fresh
  ISOs. Machines without an installer-baked banner are left untouched.
- Same fix inlined in the live ISO builder's PROFILE heredoc
  (image-recipe/_archived/build-auto-installer-iso.sh).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 08:37:13 -04:00
archipelagoandClaude Fable 5 b5e33784e6 fix(ui): kiosk map animates again + paints on resize; gloss scoped; icons get intrinsic size
Demo images / Build & push demo images (push) Successful in 3m42s
- NetworkMap3D: kiosks keep static PLACEMENT (the rAF-fragile intro was
  the blank-screen cause) but re-attach the half-rate ticker — the calm
  orbit is back; and measure() now renders explicitly when no ticker runs,
  so resizes repaint instead of leaving a stale/blank/mis-scaled
  projection (also fixes reduced-motion users on any screen).
- Gloss v3 scoped to .logo-gloss opt-in (screensaver, intro, login,
  splash tap-logo) — it had leaked onto every logo-gradient-border user,
  including the dashboard header, via AnimatedLogo's default border.
- normalize-app-icon.py output now carries intrinsic 512x512 dimensions:
  a viewBox-only SVG collapses to nothing in auto-sized tiles (the
  'transparent icon in My Apps' report); both app icons regenerated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 05:11:54 -04:00
archipelagoandClaude Fable 5 7c34df36cd feat(ui): app icons on the house canvas; detail page gets the tile treatment
Demo images / Build & push demo images (push) Successful in 3m33s
- alby-hub + phoenixd icons re-set with the standard 12% inner margin
  (they shipped edge-to-edge; every other icon carries whitespace).
- scripts/normalize-app-icon.py: wraps any third-party SVG mark onto the
  house canvas — the system applies the tile plate (archy-app-icon)
  automatically but deliberately no runtime inset, so the margin must be
  baked; the guide now says exactly that.
- MarketplaceAppDetails: the icon now carries archy-app-icon like the
  store tiles — the treatment no longer stops at the detail page.
- v1.8.2 changelog: third curated bullet (the ceremony gate requires 3).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 04:22:28 -04:00
Archipelago b67e1527a2 Archipelago — open-source initial import 2026-08-12 10:55:50 +00:00