Commit Graph
3125 Commits
Author SHA1 Message Date
DorianandClaude Opus 5 4686e14706 feat(companion): native clipboard bridge, faster QR scanner, restart control — 0.5.27
- Clipboard: the node UI is plain HTTP, so the WebView withholds
  navigator.clipboard and the page's own polyfill can only fake a write —
  readText() resolved to '' and every Paste button in the wallet silently did
  nothing. New ArchipelagoClipboard JS bridge (copy/paste over the Android
  clipboard, answering through window.__archyClipboardResult) plus an injected
  shim that points navigator.clipboard at it, so existing pages get working
  copy AND paste with no web-side change. Registered on the kiosk WebView and
  the in-app browser (node apps are plain HTTP too).
- QR scanner: two-tier decode — a cheap centre-crop pass (no TRY_HARDER) at
  ~18/s catches the well-framed case immediately, while the old full-frame
  TRY_HARDER + inverted pass still runs at ~5/s for dense, off-centre and
  light-on-dark codes. Adds camera/decoder prewarm (on first node page load and
  via ArchipelagoQr.prewarm()), a torch toggle in both scanners, tap-to-focus,
  a 1x/1.5x zoom hunt after 3s of no decode, and a success haptic.
- Restart: the retained kiosk WebView is a process-scoped static and the FIPS
  service keeps the process alive after a recents swipe, so closing the app
  stopped restarting it. MainActivity releases it when the task is finishing,
  and the hub menu gains a Restart card (mesh service keeps running).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 17:49:41 +01:00
DorianandClaude Fable 5 92915921cd feat(companion): version chip at the hub menu's foot
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 17:49:41 +01:00
DorianandClaude Fable 5 6e1539e2cd perf(companion): cold-start and interaction overhaul from the launch audit
- Splash held until launch state resolves; introSeen+activeServer now one
  combined DataStore emission (no black frame, no Connect-screen flash).
- Application warmups: fips core dlopen, DataStore first-read, Chromium
  provider load — all off the first-paint window.
- Start-origin cache persisted across process death; cached/LAN/mesh
  probes race three-wide (cold relaunch starts in milliseconds).
- ServerConnect: tunnel warms at screen entry; LAN/mesh race in parallel
  (was 5s dead LAN wait, then tunnel start, then 15s probes); SSLContext
  built once, not per attempt.
- Party: QR encode off-main + debounced (was per keystroke), share-APK
  copy off-main + cached; Flare: sampled off-main photo decode,
  derived message filter; Intro: graphicsLayer alpha, parallel fade.
- Kiosk: safe-area JS injection deduped per insets value, inset observer
  disconnects once the page settles, BackHandler off live Chromium
  calls, loader icons LRU-cached.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 17:49:40 +01:00
DorianandClaude Fable 5 0cc8e9468e chore(android): update companion apk download
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 17:49:00 +01:00
DorianandClaude Fable 5 8ac20c0a1f feat(web): app-identity launch bridge + transactions scroll/pills/fee fixes
- openInAppEx(url, icon, name): companion's native loader gets the app's
  catalog icon + display name instead of a favicon.
- TransactionsModal: BaseModal's scroller owns scrolling (nested scroll
  container broke touch scroll on phones); filter pills move to a
  swipeable single-row rail (.pill-rail) that never wraps or squishes;
  outgoing rows show recipient-received amount with fee broken out.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 17:49:00 +01:00
DorianandClaude Fable 5 b3333649b0 fix(companion): status-bar safe-area contract, branded loader+error screens, parallel launch probes — 0.5.26
- Safe-area v3: backgrounds extend under the status bar, content never
  does. Body padding + effective top background sampling (fixes white
  strip on light-bodied apps), fixed/sticky headers pushed below the bar,
  full-height overlays padded instead of shifted (no see-through strip).
- Loader: pulsating app-icon card (catalog icon via openInAppEx bridge,
  pixel-logo fallback) — site favicons removed.
- Branded CloudOff error screen with Retry; Chromium's stock error page
  can no longer be seen in app tabs.
- pickStartUrl: LAN/mesh probes race in parallel and the last-good origin
  is cached — kills the 2.5s serial dead-LAN wait behind launch variance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 17:49:00 +01:00
archipelagoandClaude Fable 5 1c8515149e feat(origins): retire bare-IP origins — one named https server everywhere
Demo images / Build & push demo images (push) Successful in 5m16s
Update mirrors and the app registry both listed 146.59.87.168 beside its
own domain (source.archipelago-foundation.org — same machine), a leftover
from before the domain existed. The IP entries are gone from defaults and
stripped from saved lists on load (the established retire-on-load pattern;
the DNS/TLS-broken recovery the HTTP twin bought is deliberately given up —
the Ed25519 manifest signature was always the actual protection). The
companion APK QR now points at the https domain, where /packages/ is
proxied to the same package host that answered on the IP.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 10:43:37 -04:00
archipelagoandClaude Fable 5 5b499b45be docs(changelog): Unreleased — receive modal payment detection
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 06:30:37 -04:00
archipelagoandClaude Fable 5 35e2406a41 feat(wallet): receive modal sees the payment land — clock, txid, Done
Demo images / Build & push demo images (push) Successful in 5m30s
While the on-chain QR is showing, poll lnd.gettransactions (5s). The
address is fresh from lnd.newaddress, so any incoming wallet tx paying it
is this receive. First sighting swaps the QR for a success view — pulsing
clock at 0-conf with the amount and a middle-ellipsized txid + copy, a
single Done button — and emits 'received' so the balance refreshes. The
watcher keeps going until the first confirmation upgrades the clock to a
check, and dies with the modal (close, hide, unmount).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 05:42:15 -04:00
archipelagoandClaude Fable 5 8a55ae73a6 fix(release): create-release.sh carries its own build fallout
Stage Cargo.lock and the regenerated public catalog in the release commit,
and re-fold AIUI into web/dist after the frontend build wipes it. All three
were manual catches on every one of the last three releases: the dirty
files failed the ISO preflight, the missing AIUI failed verify-artifacts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 17:48:05 -04:00
archipelagoandClaude Fable 5 3b4b0e7cd1 chore(release): carry Cargo.lock + regenerated UI catalog for v1.7.129-alpha
Demo images / Build & push demo images (push) Successful in 3m51s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 16:44:06 -04:00
archipelago 339c2673c9 chore: release v1.7.129-alpha
Demo images / Build & push demo images (push) Successful in 3m36s
v1.7.129-alpha
2026-08-10 16:41:48 -04:00
archipelagoandClaude Fable 5 1dcfd7774b docs(changelog): fold the federation map into v1.7.129-alpha notes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 15:37:37 -04:00
archipelagoandClaude Fable 5 e53b3ac72c merge: ux-at-last — federation network map (3D orbital, 2D toggle, live peer requests)
Branch stays alive for continued UX work; fast-forwarded to main after this
merge so it continues from the current tip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 15:35:46 -04:00
archipelagoandClaude Fable 5 ebc38407e0 docs(changelog): fold the appgate retry-page fix into v1.7.129-alpha notes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 15:30:25 -04:00
archipelagoandClaude Fable 5 ec8af1ba50 chore: lockfile drift + fmt shape in crash_recovery test
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 15:30:13 -04:00
archipelagoandClaude Fable 5 ee15626127 fix(appgate): dead upstream serves a named, self-retrying page
Demo images / Build & push demo images (push) Successful in 3m45s
The gate answered a dead app with the bare string "app is not responding",
which on the app's own port reads as the node itself being broken — reported
against Gitea on a fleet node (the actual fault was a ghost container holding
Gitea's LevelDB lock, crash-looping the managed container). Serve the same
styled page as the login challenge instead: names the app, says it may be
restarting, and retries via a Refresh header (page CSP allows no script).
Status stays 502 so machine clients still see an upstream failure.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 15:29:19 -04:00
archipelagoandClaude Fable 5 2cee14dd47 docs(changelog): curate v1.7.129-alpha notes + What's New block
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 14:49:33 -04:00
archipelagoandClaude Fable 5 cd795c98ea feat(container): pasta apps join the Quadlet migration — units over scopes
The pasta exclusion paired with the transient-scope machinery: daemon-started
pasta/conmon inherit the starter's cgroup, so scopes kept app networking
alive across daemon restarts. A quadlet unit provides the same independence
(user.slice parentage) with supervision on top — Restart=always and
RestartSec=10, which also spaces restarts past pasta's port teardown (the
race observed live on strfry, 2026-08-10). Renderer needed nothing:
Network=pasta, ports and RestartSec were already in place.

Lifted in all four places: migrate_to_quadlet_if_needed, install_fresh, and
the missing-container unit-start path; the scoped start/restart helpers are
now unit-aware — quadlet-managed pasta routes through systemctl --user
(bare podman would fight systemd over an --rm container), legacy pasta
keeps its scope, so mixed fleets stay coherent during rollout.

Container suite 221/221.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 14:31:34 -04:00
archipelagoandClaude Fable 5 a0ffc5ca3d feat(container): re-wire the companion reaper behind the durable registry
The 2026-08-08 unwire set the bar: a DURABLE record of 'this app is
installed' must drive reaping, never runtime inference. installed-apps.json
is that record (written on install, cleared on uninstall, backfilled from
live containers, held through container absence). The reconciler's
companion loop now reaps against it — and only when the registry file
actually exists and parses: 'I could not look' and 'nothing is installed'
both surface as an empty set from the lossy loader, so a new
load_installed_apps_if_recorded keeps the distinction alive. The
runtime-derived set is deliberately not a fallback; it is the input class
that caused the incident. ORPHAN_GRACE still applies on top.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 14:05:26 -04:00
archipelagoandClaude Fable 5 dbe37f7ffe fix(grafana): manifest and curation move onto the fleet trust floor
The signed catalog has homed grafana at lfg2025/grafana:10.2.0 in the fleet
registry all along; the manifest still pulled Docker Hub's grafana/grafana,
so the repo-mismatch guard (correctly) discarded the catalog image on every
tick and warned every ~75s on every node. Registry verified to carry the
image; aligning the repo path makes the already-signed catalog entry
effective — no re-signing needed, no behavioural change beyond pull source.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 13:45:10 -04:00
archipelago 604722c96c chore: release v1.7.128-alpha
Demo images / Build & push demo images (push) Successful in 3m41s
v1.7.128-alpha
2026-08-10 13:42:39 -04:00
archipelagoandClaude Fable 5 c9a70f9d2e style: cargo fmt
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 11:56:32 -04:00
archipelagoandClaude Fable 5 7f258b508e docs(changelog): curate v1.7.128-alpha notes + regenerate What's New block
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 11:54:54 -04:00
archipelagoandClaude Fable 5 2786c0727f feat(nostr): optional display name in the presence event, asked at toggle-on
Turning discovery on prompts for a name; it rides the public announcement
(clean_display_name both directions: single line, control-stripped, 32-char
cap — it round-trips through untrusted relays). Blank lists as npub only;
off/on keeps the stored name; sending an empty name clears it. Discovery
lists show the name with the npub beneath. Own-npub display switches to
middle-ellipsis so the comparable tail stays visible.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 11:54:54 -04:00
archipelagoandClaude Fable 5 a2ff3502bd fix(onboarding): provisioned-guard refusal reads as operator guidance
'Re-keying requires the authenticated system.factory-reset' is precise and
useless to an operator standing at the seed screen. Map it to what they can
do: sign in, or factory-reset from Settings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 11:28:08 -04:00
archipelagoandClaude Fable 5 61b380d266 feat(web5): discoverability container shows the node's npub, not the onion
The presence event deliberately never contains the onion — the npub is
what's actually visible on the relays, and the UI previously showed neither
it nor any way to find it. Same container, same styling; the copy button
copies the npub.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 11:28:08 -04:00
archipelagoandClaude Fable 5 81858ab630 fix(nostr): discovery events expire, heartbeat, and tombstone — stale nodes age out
Presence gets a NIP-40 expiration (48h) and a 12h re-publish heartbeat that
honours the runtime toggle (UI-enabled nodes previously never re-published
at boot). discover() drops pre-TTL events client-side for relays that
ignore NIP-40. Switching discovery off publishes an empty tombstone, and
factory-reset tombstones BEFORE wiping identity — after the wipe the key is
gone and the stale event could never be replaced by anyone.

nostr.discovery-status now also returns the node's own npub (load-only).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 11:27:59 -04:00
archipelagoandClaude Fable 5 c3b532eaf4 fix(onboarding): seed screen's lock stays steady through silent retries
Demo images / Build & push demo images (push) Successful in 3m42s
The catch block dropped `loading` before branching, so every failed poll
unmounted the lock icon and status text for the 4s retry gap — the screen
flashed in and out on a live install test (zaza-optiplex, 2026-08-09).
`loading` now stays true across isServerStartingError retries; only success
or a genuine failure dismisses the waiting state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 08:53:15 -04:00
archipelagoandClaude Fable 5 5a0c1f06b1 fix(federation): anisotropic scene fill + centred key on mobile
- The fit now scales screen X and Y independently (capped at 1.75x
  anisotropy), so both 2D and 3D stretch to the container's aspect ratio —
  a portrait phone uses its full height instead of shrinking the orbit to
  the narrow width, and wide desktop panels spread horizontally.
- Legend/key centres at the top on mobile, mirroring the bottom-centre
  2D/3D toggle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 18:31:49 -04:00
archipelagoandClaude Fable 5 a5d81fd5ff fix(federation): mobile map readability — fill the space, taller 3D, toggle bottom-centre
- Compact containers (<480px) shrink the fit margins so the scene fills the
  phone screen instead of floating in padding.
- 3D mode steepens its tilt on portrait (-0.95 vs -0.5) so the orbit reads
  as a tall ellipse with depth, not a squashed horizontal band; projection
  params are resolved per-aspect via modeParams() and re-resolve on resize.
- The 2D/3D toggle moves to bottom-centre on mobile (thumb reach, frees the
  top edge); the hint line tucks above it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 18:29:54 -04:00
archipelagoandClaude Fable 5 4ffd632506 refactor(federation): keyed diff-reconciled map scene — stable intro, smooth revisits
Root causes of the flaky intro / janky revisits, all fixed structurally:

- Graph updates now RECONCILE against the live scene by identity (peer DID /
  request id) instead of wiping the SVG: in-place updates for label/online/
  trust changes, animated arrivals for new nodes, animated departures for
  removed ones, and short-way-around slot glides when the ring layout shifts.
  The old wipe-and-rebuild orphaned every in-flight intro tween whenever the
  5s poll or the async self-DID fetch landed mid-intro — that was the
  'sometimes parts don't show'.
- The intro is gated on readiness: first real container measurement (post-
  layout, via ResizeObserver) AND the self node being present, with a 1.2s
  fallback. Graph changes during the intro queue and apply on completion.
- Layout is deterministic per node: peers sort by DID for slot assignment and
  jitter/phase hash from the DID, so backend response order can't shuffle
  the map between polls.
- The long cinematic intro plays once per browser session; revisits get a
  shorter, lighter entrance that doesn't fight the route transition.
- selfDid seeds from the cached localStorage value (re-cached on fetch) so
  the centre node and links exist from the first frame.
- Requests' waiting-room orbit pushed clearly outside the peer rings (+1.0)
  with loose per-request hover jitter — outside until accepted, then the
  join animation glides them in.
- Full unmount hygiene: every tween killed by target (incl. the repeating
  sonar pulse), fallback timer cleared, window listeners removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 17:53:40 -04:00
archipelagoandClaude Fable 5 6ba24b0f4a feat(federation): inbound peer requests live on the map — blinking nodes + accept/reject
- Inbound pending requests render as blinking yellow point-globes on an
  orbit outside the peers, dotted-linked to self, with their own dashed
  guide ring and a Request legend chip.
- Tapping one opens a black-glass popover (name, request message,
  Accept/Reject) centred over the scene; tap-away or ✕ dismisses.
- Reject: the node swells and pops out of existence, taking its link.
- Accept: green burst ring, the point cloud/link morph to the trusted
  colour and the globe glides inward onto the peer orbit, then the data
  refresh replaces it with the real peer node.
- Wired to the same approve/reject RPCs as the pending panel; new
  motionTokens.color.pending (#facc15) token.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 17:37:39 -04:00
archipelagoandClaude Fable 5 e0aebf50f8 feat(federation): 2D/3D projection toggle + interaction fixes + mobile layout polish
- Small glass 2D/3D toggle top-right of the map: tweens tilt/perspective/fit
  (0.9s inOut) so the scene folds flat into the original radial 2D layout and
  back. Default: portrait/mobile opens 2D, desktop 3D; last choice saved to
  localStorage.
- Fix: setPointerCapture retargeted pointerup to the container, suppressing
  click synthesis on children — node taps and the toggle never fired. Drag
  now tracks via window listeners, no capture.
- Fix: stale post-drag distance made the click-suppressor swallow toggle taps
  indefinitely ('stuck' toggle). Distance resets on every pointerdown and the
  suppressor is one-shot.
- Map no longer slides under the floating mobile back pill: the
  mobile-scroll-pad-back panel keeps its full 64px clearance when filled.
- Mobile DID copy/rotate card moved out of the header to below the view tabs
  (new DidCardMobile.vue) and hidden on the Network Map tab.
- Node labels: dark stroke halo removed; 10px on mobile.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 17:31:07 -04:00
archipelagoandClaude Fable 5 aea90984d6 feat(federation): flat 2D-style top-down view on portrait/mobile
Portrait containers now render near top-down and near-orthographic (tilt
-1.35, persp 16) so the map reads like the previous 2D radial layout —
rings become circles with no perspective squash. Desktop keeps the 3D
depth camera. Globe point-spheres unchanged on both.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 17:22:31 -04:00
archipelagoandClaude Fable 5 abb4c95337 fix(federation): keep view tabs pill auto-width on desktop in map view
The map view turns the root into a flex column; default stretch alignment
was pulling the mode-switcher full-width on desktop. md:self-start restores
the content-fit pill (mobile stays full-width by design).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 17:21:35 -04:00
archipelagoandClaude Fable 5 8354db8e14 feat(federation): point-cloud globe nodes, auto-fit centering, calmer motion
- Nodes are now spheres made of points: fibonacci point-cloud globes with
  depth-shaded dots, limb outline, and a slow local spin (drag adds parallax).
  Self node is black — dark dots over a soft light backing disc with the
  brand-orange sonar pulse marking 'you'. Spheres and dots sized down.
- Scene auto-fits and centres to the container on every device: the outermost
  orbit is sampled through the real camera projection to get true bounds,
  then scaled/centred between the overlays. Portrait screens tilt the camera
  towards top-down so the orbit uses the full height (mobile readability).
- No idle orbiting: nodes hold position with a gentle side-to-side sway;
  drag inertia now settles to a stop. Intro dolly pushes in (0.82→1) instead
  of pulling back, so nothing clips during the intro.
- Labels get a dark paint-order halo and bump to 12.5px on mobile.
- Point clouds update at half frame rate to keep mobile/companion smooth.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 17:20:24 -04:00
archipelagoandClaude Fable 5 fa14f6ebaa feat(federation): GSAP-powered 3D orbital node map, fills viewport to bottom
- Add gsap 3.15 + design-system-aware motion module (src/utils/motion.ts):
  shared colour/duration/ease tokens mirrored from style.css, gsap.defaults,
  live prefers-reduced-motion check.
- Replace the d3 force NetworkMap with NetworkMap3D.vue: peers on projected
  3D orbital rings around the self node, cinematic intro (camera dolly +
  staggered fly-in + ring draw), idle rotation with drag-to-orbit inertia,
  depth-sorted painter's order, trust-colour palette, online/offline states,
  sonar pulse on self, tap-a-node opens the detail modal.
- Map view now fills the dashboard panel to the bottom edge on desktop,
  mobile and companion: .dashboard-scroll-panel:has(.node-map-stage) turns
  the panel into a column (tab-bar/safe-area/audio-player aware padding)
  instead of leaving the old dead bottom margin.
- Reduced motion: intro/idle skipped, scene renders static.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 17:09:09 -04:00
archipelagoandClaude Fable 5 7fbdabf136 chore(release): carry Cargo.lock version bump + regenerated UI catalog for v1.7.127-alpha
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 16:46:12 -04:00
archipelago 41fd231a36 chore: release v1.7.127-alpha
Demo images / Build & push demo images (push) Successful in 3m47s
v1.7.127-alpha
2026-08-09 16:25:53 -04:00
archipelagoandClaude Fable 5 76e6109aae chore(whats-new): sync v1.7.127-alpha block from CHANGELOG (generated)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 15:45:46 -04:00
archipelagoandClaude Fable 5 c5689b8ae4 fix(catalog): carry the Knots pin into app-catalog/catalog.json — drift gate
The signed release catalog was pinned in 6c41852c; the curation source still
said :latest, which ec8d88a6 established can stall the fleet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 15:45:20 -04:00
archipelagoandClaude Fable 5 e465563cbd style: cargo fmt over the phase-13 merge — mechanical, no behavior change
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 15:44:18 -04:00
archipelagoandClaude Fable 5 cb840fd36f chore(aiui): refresh shipped demo/aiui dist from post-merge source — verified, mock-free
The release tarball bakes demo/aiui in for fresh installs; the previous dist
predated the phase-13 security waves (S1-S7, W1.4) and would have shipped a
pre-hardening bundle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 15:43:19 -04:00
archipelagoandClaude Fable 5 7b82dfc779 docs(changelog): curate v1.7.127-alpha notes — AIUI ships, reboot gate recorded
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 15:16:42 -04:00
archipelagoandClaude Fable 5 89b03c47bd fix(state): installed apps are held, never dropped, while their container is gone
Quadlet renders --rm, so every stop DELETES the container: a scan during
the stop->start window legitimately sees nothing where an installed app
lives. The state layer treated that as the app not existing — after three
absent scans it removed the entry entirely, so dashboard tiles vanished
mid-restart, gate waits read 'absent' (grafana, at load ~2, ruling out
contention), and under daemon-restart churn the FIRST scan could publish
a completely empty My Apps map (observed twice on 2026-08-09).

Two guards, both keyed to the durable installed-apps.json registry:

- Eviction: an id the registry says is installed, and the user has not
  uninstalled, is held as Stopped (health/exit cleared) instead of being
  removed. The next scan that sees the container restores live state;
  desired-state recovery still recreates genuinely lost containers; a
  deliberate uninstall clears the registry first and still disappears.
- Empty scans: the first-scan exemption no longer applies when the
  registry is non-empty — better to keep saying "scanning" than "empty".

Verified: scan-merge/absence/registry test filters 34/34.

Also carries the v1.7.127-alpha changelog (user-benefit entries for the
Tor self-heal, trusted invites, the three-layer mempool fix, vanishing
apps, the Bitcoin version pin, and the smaller UI fixes).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 13:39:21 -04:00
archipelagoandClaude Fable 5 e898f138d1 fix(bootstrap): deliver the tor-helper and nginx ws fix to EXISTING nodes
An OTA updates the binary and web assets — not /etc/nginx, and not
/opt/archipelago/scripts. Auditing the 2026-08-09 fixes' delivery paths
found two that would silently reach nobody already installed:

- scripts/tor-helper.sh (reset-failed + truthful restart result) shipped
  only via ISO builds and manual deploys. Now embedded via include_str!
  like the doctor script, staged and installed at boot when the on-disk
  copy differs.
- The /app/mempool/ nginx Upgrade/Connection headers existed only in repo
  snippet sources consumed at image build time. A boot repair now
  idempotently patches any mempool location block missing them — in the
  live vhost (archipelago-http, the one sites-enabled actually links to),
  the legacy file, and the installed snippet — and reloads nginx once.
  Without this, every fleet node's mempool UI keeps loading-but-never-
  connecting after the OTA that supposedly fixed it.

Both are non-fatal boot repairs in the existing bootstrap chain, no-ops
when everything is already current.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 12:48:59 -04:00
archipelagoandClaude Fable 5 44fbdd381b fix(wallet): transactions modal tab strip is glass, not a black slab
Demo images / Build & push demo images (push) Successful in 4m14s
The sticky tab header painted bg-black/70 across the modal — reported as
a black container where the design is transparent glass. The backdrop
blur alone keeps pinned tabs legible over rows scrolling beneath; the
tint drops to the standard bg-white/5.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 12:36:26 -04:00
archipelagoandClaude Fable 5 47b563ec4f fix(appgate): splice websocket upgrades through to the app
The gate accepted a client's websocket upgrade (.with_upgrades() on the
serving side) but proxy_to_app forwarded every request with a plain
hyper::Client, which completes the app's 101 handshake and then drops the
upgraded connection. Result: any ws-driven app behind a gated port loads
its page fine and then dies with close code 1006 on every connect —
mempool's entire UI is such an app, and the operator's browser console
was the only place the failure was visible. This was the THIRD layer of
the same outage (electrumx sync, then the nginx proxy path, now the app
tile's gated-port path — each fix exposed the next).

On an Upgrade request the gate now takes the client's OnUpgrade handle
from the request extensions, hands the handshake to the app with the same
sanitized headers (cookie/authorization stripping applies unchanged), and
on a 101 bridges the two upgraded connections with copy_bidirectional.
Non-101 upstream answers pass through so auth failures stay visible.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 12:35:38 -04:00
archipelagoandClaude Fable 5 f10a0d5393 fix(nginx): forward the websocket upgrade to mempool — the UI is ws-driven
The dashboard's /app/mempool/ proxy forwarded plain HTTP only: no
Upgrade/Connection headers, so the browser's /api/v1/ws handshake reached
mempool-api as a bare GET and Express 404'd it. The mempool page loaded
fine and every REST probe was green while the user saw a UI that never
connects — the backend was fully healthy and completely unreachable in
the only way that matters to the page.

This hid behind the electrumx initial-sync outage: once sync finished and
the API answered, "mempool works" was declared from REST checks while the
websocket path stayed dead. Reported by the operator from the browser,
which is the only place it was visible.

Adds Upgrade/$http_upgrade + Connection "upgrade" to the /app/mempool/
location in both shipped sources (image-recipe snippet + scripts copy).
Live-verified on the dev node: ws through the proxy now answers 101; the
control probe without upgrade headers reproduces the 404 signature.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 12:27:47 -04:00