Commit Graph
11 Commits
Author SHA1 Message Date
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 03cf74696d docs(ecash): pin the seed-backup UX to the existing reveal pattern
Ecash gets its own BIP-39 mnemonic derived from the node master seed:
still covered by the node's recovery phrase, but portable into any NUT-13
wallet without exposing the master seed — so 'back up my ecash' is not the
same action as 'expose the key to everything'.

Surfaced exactly like the Lightning seed: the shared SeedRevealPanel, on
the app detail page and in Settings → Backup, behind the same
verify_reveal_auth password re-entry. Standard BIP-39, so the SeedQR tab
works (no aezeed flag).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 04:22:29 -04:00
archipelagoandClaude Fable 5 6e23b121ea docs(ecash): plan the move to the reference Cashu implementation
Scoped from the framework-pt keyset incident: adopt the `cashu` crate
(MIT, the crate CDK is built on) for the token codec, keyset ids, crypto,
DLEQ and NUT-13, while keeping ecash.rs's on-disk contract, MintClient's
Tor seam and error table, and our multi-mint routing. Records why the
full cdk WalletDatabase shim is the wrong trade over live funds, and how
ecash backup can derive from the node's existing seed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 04:07:19 -04:00
archipelagoandClaude Fable 5 58cdea5e79 feat(appgate): apps with their own login can skip the node login
Demo images / Build & push demo images (push) Successful in 3m33s
Some apps carry a complete account system and are broken by an upstream
challenge: git clients speak basic-auth (not browser cookies), and a
BTCPay checkout link handed to a customer must open for that customer.
Both were behind the gate's login page — the "non-browser clients need an
access token" gap disclosed in five consecutive releases.

- New manifest port policy `auth: open`: the daemon still fronts the port
  exactly like `gated` (loopback pin, external binds, frame-header fixes,
  app-down retry page, Tor upstream) but serves it without the login
  challenge. Requires auth_rationale, same burden of proof as `none`.
  Gitea 3001 and BTCPay 23000 declare it.
- Runtime operator override per app (security.set-app-gate → app-configs/
  <id>.json "gateEnabled"), surfaced as Settings → app → Access control.
  Wins over the manifest in both directions and applies on the next
  request — no restart, and it works today on catalog-covered apps whose
  signed manifest still says `gated`.
- The gate resolves policy per-request from the live port map, so a
  toggle takes effect without waiting for the 60s rebind sweep. "Off"
  never releases the port: gated apps are loopback-pinned, so releasing
  would strand them, not open them.
- security.app-gate-status now reports gate_enabled + any override.
- New guard test pins the `auth: open` set (both entries reviewed); the
  `auth: none` count moves 25 → 26, absorbing pre-existing drift from the
  phoenixd onboarding (loopback JSON API with its own generated password).
- Docs: the manifest spec's ports row documented only host/container/
  protocol — bind, auth, auth_rationale and session_passthrough were
  undocumented. Added a full "Ports & the app gate" section plus a
  developer-guide entry telling app authors to enforce their own auth
  regardless, since the operator can flip the gate either way.

Verified live on archi-dev-box from an external address: gated → 401 gate
page; override off → Gitea 200 own page, BTCPay 302 to its own login,
git-over-HTTP info/refs 200; override on → 401 again; clear → default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 11:40:07 -04:00
archipelagoandClaude Fable 5 3a3077529b feat(kiosk): companion remote drives app iframes via trusted CDP input
Demo images / Build & push demo images (push) Successful in 3m11s
Companion tap/scroll/type now works INSIDE cross-origin app iframes and
kiosk tabs. The web relay synthesizes untrusted DOM events in the top
document, which can never cross an origin boundary — so apps served
through the appgate were dead to the remote. The kiosk Chromium now
exposes a loopback-only CDP port (default origin check intact, no
--remote-allow-origins) and a backend bridge (api/handler/cdp.rs)
dispatches validated companion input as Input.dispatchKeyEvent /
dispatchMouseEvent / mouseWheel — trusted events that hit-test through
any frame, move real focus, and insert text like a physical device.

- Session keeper self-heals across kiosk Chromium restarts; inert on
  nodes without a kiosk unit (falls back to the existing relay path).
- The kiosk relay subscriber self-tags (?kiosk=1) and the backend mutes
  its key/click/scroll messages while the bridge is live, so input never
  applies twice; cursor moves still flow for the on-screen cursor.
- While companion input is active the native OS pointer is hidden
  (cursor:none, auto-restores 30s after the last event) so the dead
  physical-mouse cursor doesn't sit next to the virtual one.
- docs/tv-input-iframe-apps.md scope note updated: gamepad keys stay on
  uinput; CDP is for companion pointer/typing only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 05:28:53 -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
archipelagoandClaude Fable 5 2399eeac66 feat(ui): auto-tab fallback — embed-refusing apps become tab apps
Demo images / Build & push demo images (push) Successful in 3m49s
An app whose frame never loads while its backend reports Running (the
embed-refusal signature: frame-busting JS, top-level-origin apps,
SameSite=Strict logins — everything the gate's header stripping cannot
fix) is remembered in localStorage; every later launch opens a tab
straight from the click (user gesture, so no popup blocker), and
opensInTab() gives it the tab-launch icon. A successful iframe load
clears the memory and entries expire after 7 days, so nodes that gain
embedding (gate improvements) get re-probed instead of being remembered
broken forever. Dev guide updated; v1.8.2 changelog + What's New curated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 20:23:04 -04:00
archipelagoandClaude Fable 5 76e6f06995 fix(phoenixd): dot-free datadir + data_uid; docs: iframe rules
phoenixd: the orchestrator treats bind paths containing a dot as file
mounts and never creates their source dir, so the image's default
/phoenix/.phoenix target crash-looped the unit (statfs: no such file).
Datadir moved to /data via PHOENIX_DATADIR; data_uid 1000:1000 matches the
image's phoenix user — without it phoenixd dies on phoenix.conf
'Permission denied'. Both verified end-to-end on archi-dev-box: orch
install OK, seed.dat + db on the host, authenticated /getinfo answers.

alby-hub: launch flips to embedded — pairs with the gate change that
neutralizes upstream frame blocking.

Dev guide: iframe embedding rules (who blocks framing and why the gate
may strip it; when open_in_new_tab is legitimate; test in the embedded
session, never a tab).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 12:38:17 -04:00
archipelagoandClaude Fable 5 b7ba35477c docs(dev-guide): package.install needs dockerImage too; helper gotchas
Learned installing alby-hub/phoenixd for real: the id-only payload fails
with 'Missing dockerImage' (the store normally injects the image from the
catalog), the session helper silently reuses a stale cached session
without ARCHY_FORCE_LOGIN=1, needs jq, and its set -euo pipefail kills an
interactive shell chain without output — so the guide now wraps the flow
in a heredoc subshell.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 12:15:55 -04:00
archipelagoandClaude Fable 5 e87e8017bf docs(dev-guide): real pre-catalog testing flow on a live node
The old RPC example skipped login entirely and implied disk manifests show
up in the App Store. Documents: store lists signed-catalog + Nostr apps
only; the runtime-payload staging path (naive /opt/archipelago/apps copies
are deleted on every backend start); the rpc.bash session helper; and the
full lifecycle loop to run before submitting to the catalog.

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