From 2ebcd8f9a8450b99f5b39a2d53db07ac6c30f5a6 Mon Sep 17 00:00:00 2001 From: archipelago Date: Fri, 26 Jun 2026 03:47:25 -0400 Subject: [PATCH] =?UTF-8?q?docs(master-plan):=20backlog=20=E2=80=94=20smar?= =?UTF-8?q?t=20launch-port=20selection=20+=20manifest-driven=20archival-no?= =?UTF-8?q?de=20blocker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §10b: replace per-app static launch-port map with a manifest-first + non-HTTP-port-skipping heuristic (the gitea :2222 class). §10c: generalize the un-pruned/archival Bitcoin install blocker from a hardcoded requires_unpruned_bitcoin() match to a manifest-declared dependency, with a clear pre-install UX. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/PRODUCTION-MASTER-PLAN.md | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/docs/PRODUCTION-MASTER-PLAN.md b/docs/PRODUCTION-MASTER-PLAN.md index e4aa3ae0..5f971f7d 100644 --- a/docs/PRODUCTION-MASTER-PLAN.md +++ b/docs/PRODUCTION-MASTER-PLAN.md @@ -787,3 +787,53 @@ handling) would make these classes of bug structurally hard. - Deliverable: a short design note + a recommendation, then a scoped migration of the package-lifecycle surfaces (My Apps / install / uninstall / update progress) as the proof case — sequence AFTER workstream F (it informs F's progress-UI fix and vice-versa). + +## 10b. Backlog — intelligent launch-port selection (2026-06-26) + +**Replace the per-app static launch-port map with a smart, manifest-first heuristic.** Gitea +launched at **:2222 (SSH)** instead of **:3001 (web)** on a node missing the gitea manifest on +disk: `manifest_lan_address_for` returned None → the code fell through to `extract_lan_address`, +which returns podman's **first-listed** published port, and podman lists `2222->22` before +`3001->3000`. Patched 2026-06-26 (`670ebb06`) with a static `"gitea" => 3001` entry in +`lan_address_for` (`core/container/src/podman_client.rs`) — but that's a per-app band-aid (the +anti-pattern CLAUDE.md warns against; the map already carries bitcoin/lnd/mempool/immich/… by hand). + +**Real fix (do this, then delete the static entries):** +- **Primary** is already correct — derive the launch URL from the manifest's declared + `interfaces.main` port. The failure was only the *fallback*. The north-star cure is + registry-distributed manifests (workstream B) so the manifest is always present and we never + guess. +- **Smart fallback** — make `extract_lan_address` stop returning the blind first port: **skip + container-side ports that are known non-HTTP (22/SSH, etc.) and prefer the published port whose + container side matches the manifest `health_check` endpoint / a known web port.** Fixes the whole + multi-port-app class generically (no per-app hardcoding), and lets us drop the static map. +- ~20-line change to one function + unit tests; rides the next fleet roll. NOT a free-port + remap (that's `port_allocator.rs`, which already resolves host-port *collisions* — a different + problem; gitea's web UI was never in conflict). + +## 10c. Backlog — generalize the archival/full-node install blocker (2026-06-26) + +**Make "this app needs an un-pruned (archival, txindex) Bitcoin node" a manifest-declared +dependency, applied to every app that needs it — using the electrumX/mempool blocker as the +reference behavior.** Today the gate works but is **hardcoded**: `requires_unpruned_bitcoin()` in +`core/archipelago/src/api/rpc/package/dependencies.rs` is a literal `matches!(package_id, "electrumx" +| "electrs" | "mempool-electrs" | "mempool" | "mempool-web")`, and install `bail!`s with +`archival_bitcoin_required_message` when `bitcoin.pruned` is true or disk < `ARCHIVAL_BITCOIN_DISK_GB` +(1 TB). That's the same per-app-hardcoding anti-pattern as the gitea static map (§10b) and the +`install_*_stack` Rust — any new app needing a full node is silently *un*-gated until someone edits +this match. + +**Do:** +- **Declare it in the manifest** — e.g. `requires: { bitcoin: archival }` (or a + `dependencies.bitcoin.pruned: false` constraint) so the install pre-flight reads the requirement + from the manifest set instead of a hardcoded list. Covers future apps automatically (manifest-driven + north star). +- **Audit coverage** — confirm EVERY archival-dependent app is gated (electrumX, electrs, + mempool + its electrs, and any BTC-indexer/explorer added later); add a unit test asserting the + manifest constraint ⇒ blocker fires. +- **UX** — the blocker must be a clear, surfaced **pre-install** state in the UI (not just an RPC + `bail!` string): explain *why* (pruned node / insufficient disk), what to do (add ~1 TB, resync + un-pruned with txindex), and keep the app visibly "requires archival node" rather than a confusing + generic failure. Pairs with workstream F's honest-progress/blocker UX. +- Reference: the existing `package-install-prune-check` dependency descriptor (dependencies.rs:208) + is the seam to make data-driven.