v1.7.120-alpha is shipped and verified; do not re-cut it. Two fixes landed after it: federation trust escalation (c0cfc72a) and the lnd-ui OTA pin + host networking (5088aef5). The task file now carries a RESUME HERE block with the groundwork already located for the next item (the password gate on granting Trusted) — the exact helper, both entry points with line numbers, and the rule that demotion stays ungated — so the next session does not repeat the search. Paused here deliberately rather than starting the app-port auth work at low context: it is the largest item, the operator asked for umbrelOS and StartOS research first, and it is the same bug class as the leaks fixed in v1.7.120 but across every app port and transport. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
10 KiB
Release 1.7.121 — task list
Opened 2026-08-03, immediately after v1.7.120-alpha shipped. Everything the operator has asked for since, plus the items v1.7.120 deliberately left open. Ordered by severity.
Status key: DONE (committed) · READY (written, not yet committed/tested) · OPEN (not started) · BLOCKED (needs an operator decision)
P0 — Security
1. App ports are reachable with no login, on every transport — OPEN
"if I'm logged out I can reach every app port on tailscale and LAN, this can not be allowed… it must present the login to access the app with an app icon of what you're accessing to confirm, and 2FA if present" — operator, 2026-08-03
- Applies to Tailscale, LAN, Tor, FIPS alike, and to "ssh access to that port or whatever".
- Required behaviour: an unauthenticated request to any app port serves a login page naming and showing the icon of the app being accessed, then honours 2FA when set.
- Research first: how umbrelOS and StartOS gate app access (operator asked explicitly).
Both are open source —
getumbrel/umbrelandStart9Labs/start-os. Do not guess at their model; read it. - This is the same class as the v1.7.120
/lnd-connect-info+/bitcoin-rpc/leaks, but fleet-wide across every app port rather than two endpoints. Those two were closed by moving authorisation to the resource; this needs a general gate. - Scope note:
fips/app_ports.rsholds the mesh allowlist;is_peer_allowed_pathinserver.rsholds the peer HTTP allowlist. Neither currently authenticates app ports.
2. Filebrowser ships an insecure default login — OPEN
- Change the default credential without breaking the dashboard's Cloud view, which authenticates to filebrowser on the user's behalf.
- Related prior art: FED-07 rotated the shipped Fedimint gateway credential and had to
recreate the running container for it to take effect (
06e0e695) — the same trap applies here.
3. Federation trust escalation — DONE (c0cfc72a, pushed)
Two independent fail-open paths granted Trusted without any operator decision:
federation.peer-joinedis unauthenticated (middleware no-session list) and peer-reachable on/rpc/v1. Its ed25519 check verifies the caller against the pubkey the caller supplied, so it proves key possession, never authorisation. A join with noinvite_tokenfell through toTrustLevel::Trusted.min(claimed_trust), andclaimed_trustdefaults toTrusted— so anyone able to reach the node could self-grant Trusted. Now capped atObserver.merge_transitive_peersadded every peer advertised by a Trusted source asTrusted, making trust viral across the whole federation graph. NowObserver— which is whatNodeStateSnapshot.federated_peers' own doc comment always said it should be ("adds them as Observers on her side… doesn't auto-promote to Trusted"). The code contradicted its own spec.- Added
FederatedNode.trust_source(invite|uninvited-join|transitive-merge|manual,None= pre-existing/unknown) so existing grants are auditable. Per operator decision: existing peers are left alone, not auto-demoted. - Still to do: surface
trust_sourceinfederation.list-nodes+ the UI so the operator can actually review theNone/uninvited-joinpopulation.
3b. Granting Trusted must require the node password — OPEN
"to make someone trusted must require the node password to generate the code or change in the modal dropdown when you click a node" — operator, 2026-08-03
Re-authentication on privilege escalation. Two entry points, both must be covered:
- Minting a Trusted invite (
federation.invitewithtrust_level: "trusted") — "Link Your Nodes" mints Trusted today with no re-auth. - Changing a node's level in the UI dropdown (
federation.set-trust-level/handlers.rs:342) — promoting Observer → Trusted.
Demotion must NOT require the password: making something less privileged should never be
harder than leaving it. Grant TrustSource::Manual on the operator path so the audit
trail distinguishes it from the capped automatic ones.
P1 — Correctness the operator hit directly
4. LND UI never updates over OTA — DONE (5088aef5, pushed)
LND_UI_IMAGEwaslnd-ui:latestwhileBITCOIN_UI_IMAGEwas pinned to1.7.119-alpha. Podman will not re-pull a tag it already holds locally, so nodes kept a stale lnd-ui forever. Now pinned to1.7.119-alpha.scripts/first-boot-containers.shdeclared lnd-ui as bridge-p 18083:80. That is the third copy of the declaration the UI agent already corrected inscripts/container-specs.shandapps/lnd-ui/manifest.yml— so fresh installs still produced the reproducedHTTP 000. Now--network host, ports empty.- Root cause worth fixing separately: the same container spec is declared in three places.
5. Federated/peered nodes must message without a LoRa hop first — OPEN
"make it so federated/peered nodes can message without needing to connect on Lora first once connected"
- Investigate the split contact model (radio contact vs federation peer) — there is prior
art in memory:
project_archy_lora_e2e_rootcause("split contact model; don't touch federation") andmesh::seed_federation_peers_into_mesh/upsert_federation_peer, which already mirror federation peers into the mesh table. - Likely the gap is addressing/route selection rather than transport availability.
6. In-app app updates, independent of OTA — OPEN
"we need app update to see updates in the registry, whether UI or not… show the update mechanism in the app… a modal and update now / cancel… same in the detail page… the update button should show 'see update' and a different graphic for just ui, app, or both together. All pushed through the signed-catalog flow." … "This has to show independent of OTA updates as a separate pipeline, I think we've done a lot of work on it."
- Operator says much of this already exists — research the codebase before building.
Known groundwork: the signed catalog (
releases/app-catalog.json,sign-catalog.sh), catalog→manifest runtime reload,package.updateRPC,check-app-catalog-drift.py, andscripts/image-versions.shpinning. - Needs: registry-version awareness per app, a diff of what changed (UI vs app vs both), the modal + detail-page affordance, and distinct iconography for the three cases.
P2 — Carried over from v1.7.120
7. create-release.sh commits the manifest BEFORE signing — OPEN
Release commit always carries an unsigned manifest; nodes fetch it from branch main
and refuse to auto-apply. Caught manually this cycle. Fix the ordering so it cannot ship.
8. gitea-vps2 remote is dead, and is the same server as gitea-ai — OPEN
Stored token fails auth. source.archipelago-foundation.org == 146.59.87.168, so
git push gitea-ai already publishes to the "primary" OTA host. Ties into the existing
"migrate VPS2 IP to domain" todo.
9. Fleet SSH host-key rotation — BLOCKED (operator decision)
archipelago-1, archy-x250-beta, archipelago share all three SSH host keys; two also
share a TLS private key. Detection shipped; rotation deliberately not performed.
10. 5× lifecycle gate — OPEN
Not run for v1.7.120 (disclosed in its changelog). Needs repeated reboots of a live node.
11. prod_orchestrator.rs:3181 unreachable code — OPEN
bitcoin_host() returns unconditionally at :3171, so the podman container-name lookup
below is dead on every path. Pre-existing; spotted in the v1.7.120 build warnings.
Notes for whoever picks this up
- A separate agent is doing AIUI planning with GSD — do not touch AIUI.
- AIUI must always be built
VITE_BASE_PATH=/aiui/(see the memory note); a hand-built bundle renders a black page. - Verify security claims on the node, not from the source. v1.7.120's headline bug was a fix that shipped in the binary and silently never reached the running container.
RESUME HERE — next session
Landed this session (both pushed):
c0cfc72afederation trust escalation (items 3) — 42/42 federation tests green5088aef5lnd-ui OTA pin + host networking (item 4), and this task file
v1.7.120-alpha is SHIPPED — signed, published, assets verified live. Do not re-cut it.
Start with item 3b (password gate) — groundwork already located
Everything needed to implement it, so the next session does not re-search:
- The helper to use:
self.auth_manager.verify_password(password).await?— returnsbool. Existing callers to copy the shape from:api/rpc/node.rs:176,api/rpc/totp.rs:18/:66/:121. - Entry point A — minting a Trusted invite:
handle_federation_invite,api/rpc/federation/handlers.rs:58. It readstrust_levelfrom params and defaults toTrustLevel::Trustedat :72. Gate only when the resolved level isTrusted; leave Observer invites unchanged. - Entry point B — the UI dropdown:
handle_federation_set_trust,api/rpc/federation/handlers.rs:326, dispatched as"federation.set-trust"(api/rpc/dispatcher.rs:353). Its parse is at:342. - Rule: gate PROMOTION to Trusted only. Demotion must stay ungated — making something less privileged must never be harder than leaving it.
- Set
TrustSource::Manualon the operator path so the audit trail distinguishes a deliberate grant from the capped automatic ones. - Frontend will need the password prompt in both places (invite modal, node dropdown).
Then item 1 (app ports unauthenticated) — the big one
Start with the research the operator explicitly asked for: how umbrelOS
(getumbrel/umbrel) and StartOS (Start9Labs/start-os) gate app access. Read their
model rather than inventing one. Only then design the gate.
Give this a fresh session with real context — it is the largest item here and is the same
bug class as the /lnd-connect-info + /bitcoin-rpc/ leaks fixed in v1.7.120, but across
every app port and every transport.
Working notes
- A separate agent is doing AIUI planning with GSD — do not touch AIUI.
- The shared tree has concurrent agents: stage by explicit path, never
git add -A. - Verify security claims on the node, not from source. v1.7.120's headline bug was a fix that shipped in the binary and silently never reached the running container.
- A piped command's exit code is the pipe's, not the script's — redirect to a log file and read the content.