Compare commits

...

3 Commits

Author SHA1 Message Date
Dorian
30a26f94f7 release(v1.7.13-alpha): proxy app catalog server-side (CORS + CSP fix)
All checks were successful
Build Archipelago ISO (dev) / build-iso (push) Successful in 9m54s
The Discover / Marketplace page fetched the app catalog directly from
git.tx1138.com/lfg2025/app-catalog/raw/.../catalog.json in the
browser. Two blockers hit the fleet simultaneously: (1) tx1138's
Gitea doesn't emit Access-Control-Allow-Origin so the HTTPS fetch
got CORS-blocked; (2) the HTTP IP-port fallback
(http://23.182.128.160:3000/...) falls outside the node's
`connect-src` CSP. Users saw the hardcoded fallback instead of the
live catalog.

Backend: new authenticated GET /api/app-catalog handler uses reqwest
to pull catalog.json server-side (15s timeout) and returns it with
application/json + 1h Cache-Control. Tries the HTTPS URL first,
HTTP IP-port second.

Frontend: curatedApps.ts now calls /api/app-catalog (same-origin,
no CORS/CSP) with credentials included so the session cookie
authenticates the proxy. Baked /catalog.json stays as the last
resort.

Artefacts:
  archipelago                                      0aaf7262…b979f22c  40371192
  archipelago-frontend-1.7.13-alpha.tar.gz         27505811…efc6f4142 76982505

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 15:43:45 -04:00
Dorian
26d6eddb1c release(v1.7.12-alpha): bump on top of working-OTA 1.7.11
All checks were successful
Build Archipelago ISO (dev) / build-iso (push) Successful in 11m2s
Version-only bump. Sits above v1.7.11-alpha which user has verified
runs the full Install Update pipeline end-to-end (check → download
→ install → auto-restart). Freshly-installed nodes from the 1.7.11
ISO will see 1.7.12 as their first OTA target.

Frontend tarball byte-identical to v1.7.11 (same sha).

Artefacts:
  archipelago                                      247f65c2…54f40df9  40385472
  archipelago-frontend-1.7.12-alpha.tar.gz         0644a436…54f58    76983846 (reused)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 14:39:07 -04:00
Dorian
c9f6697f02 release(v1.7.11-alpha): OTA proof bump on top of namespace-escape apply
All checks were successful
Build Archipelago ISO (dev) / build-iso (push) Successful in 11m14s
Version-only bump. Frontend tarball byte-identical to v1.7.10. First
OTA-testable release where the running backend (v1.7.10) has the
host_sudo/systemd-run apply fix — clicking Install Update should
walk through check → download → install → auto-restart with no
manual intervention.

Artefacts:
  archipelago                                      cf003f62…65465f  40378752
  archipelago-frontend-1.7.11-alpha.tar.gz         0644a436…54f58   76983846 (reused)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 14:03:36 -04:00
11 changed files with 81 additions and 25 deletions

2
core/Cargo.lock generated
View File

@@ -80,7 +80,7 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "archipelago"
version = "1.7.10-alpha"
version = "1.7.13-alpha"
dependencies = [
"anyhow",
"archipelago-container",

View File

@@ -1,6 +1,6 @@
[package]
name = "archipelago"
version = "1.7.10-alpha"
version = "1.7.13-alpha"
edition = "2021"
description = "Archipelago Bitcoin Node OS - Native backend"
authors = ["Archipelago Team"]

View File

@@ -113,6 +113,53 @@ impl ApiHandler {
}
}
/// Server-side fetch of the upstream app catalog so the browser can
/// load it without fighting CORS (git.tx1138.com emits no ACAO) or
/// CSP (the fallback IP-port URL isn't in `connect-src`). Tries the
/// upstream URLs in the same order the frontend used, returns the
/// first 2xx response. 15s total timeout.
async fn handle_app_catalog_proxy() -> Result<Response<hyper::Body>> {
const UPSTREAMS: &[&str] = &[
"https://git.tx1138.com/lfg2025/app-catalog/raw/branch/main/catalog.json",
"http://23.182.128.160:3000/lfg2025/app-catalog/raw/branch/main/catalog.json",
];
let client = match reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(15))
.build()
{
Ok(c) => c,
Err(e) => {
return Ok(build_response(
hyper::StatusCode::INTERNAL_SERVER_ERROR,
"text/plain",
hyper::Body::from(format!("client build failed: {}", e)),
));
}
};
for url in UPSTREAMS {
match client.get(*url).send().await {
Ok(resp) if resp.status().is_success() => {
if let Ok(bytes) = resp.bytes().await {
return Ok(Response::builder()
.status(hyper::StatusCode::OK)
.header("Content-Type", "application/json")
.header("Cache-Control", "public, max-age=3600")
.body(hyper::Body::from(bytes))
.unwrap_or_else(|_| {
Response::new(hyper::Body::from("proxy response build failed"))
}));
}
}
_ => continue,
}
}
Ok(build_response(
hyper::StatusCode::BAD_GATEWAY,
"text/plain",
hyper::Body::from("all upstream catalog URLs failed"),
))
}
/// Build a 401 Unauthorized JSON response.
fn unauthorized() -> Response<hyper::Body> {
let body = serde_json::json!({ "error": "Unauthorized" });
@@ -352,6 +399,18 @@ impl ApiHandler {
// Electrs status — unauthenticated (read-only sync status)
(Method::GET, "/electrs-status") => Self::handle_electrs_status().await,
// App-catalog proxy — fetches catalog.json from the configured
// upstream URLs server-side so the browser doesn't hit CORS
// (git.tx1138.com has no ACAO header) or CSP (IP-port upstream
// falls outside `connect-src`). Session-authenticated so only
// the logged-in node owner can spin up fetches.
(Method::GET, "/api/app-catalog") => {
if !self.is_authenticated(&headers).await {
return Ok(Self::unauthorized());
}
Self::handle_app_catalog_proxy().await
}
// LND connect info — nginx validates session cookie (presence check),
// backend is bound to 127.0.0.1 so only nginx can reach it.
// No backend auth check here because the LND UI iframe fetches this

View File

@@ -22,13 +22,13 @@ let cachedCatalog: AppCatalog | null = null
let catalogFetchedAt = 0
const CATALOG_TTL = 60 * 60 * 1000 // 1 hour cache
/** Remote catalog URLs tried in order. First success wins. */
/** Catalog URLs tried in order. First success wins.
* Primary is the backend proxy (`/api/app-catalog`) — server-side fetch
* bypasses CORS on git.tx1138.com and CSP restrictions on the IP-port
* fallback. If the backend is offline (mid-restart etc.) we fall back
* to the static copy baked into the frontend build. */
const CATALOG_URLS = [
// Primary: git.tx1138.com raw file (HTTPS, dynamic, updated without frontend rebuild)
'https://git.tx1138.com/lfg2025/app-catalog/raw/branch/main/catalog.json',
// Fallback: direct IP (HTTP, only works if CSP allows http://$host:*)
'http://23.182.128.160:3000/lfg2025/app-catalog/raw/branch/main/catalog.json',
// Last resort: local static file (baked into frontend build)
'/api/app-catalog',
'/catalog.json',
]
@@ -40,7 +40,7 @@ export async function fetchAppCatalog(): Promise<AppCatalog | null> {
for (const url of CATALOG_URLS) {
try {
const res = await fetch(url, { signal: AbortSignal.timeout(5000) })
const res = await fetch(url, { credentials: 'include', signal: AbortSignal.timeout(20000) })
if (!res.ok) continue
const data = await res.json() as AppCatalog
if (!data.apps?.length) continue

View File

@@ -1,28 +1,25 @@
{
"version": "1.7.10-alpha",
"version": "1.7.13-alpha",
"release_date": "2026-04-20",
"changelog": [
"Install Update actually applies now. The installer had to write into system folders that the backend service was sandboxed out of — every earlier 'Failed to apply update' was a layer of that onion. Fixed by running the file swaps in a separate system context.",
"FIPS status on the Home and Server pages now reflects whether the public anchor is reachable. You'll see 'Active · N peers' (green) when healthy or 'No anchor' (orange) when the network is blocking the bootstrap — same signal as the full FIPS card.",
"Pasting an https://… URL into the profile picture or banner now previews correctly. Before, if the URL failed to load, the UI would silently blank out instead of showing your initial as a placeholder.",
"Uploaded profile pictures under 64 KB are now embedded directly in your Nostr profile (as a data URL), so any Nostr client can see them — not just ones routing over Tor. Larger uploads keep the onion URL for now, with a hint to paste a public URL for wider visibility."
"App catalog now loads reliably. Before, the Marketplace / Discover page couldn't fetch the catalog of apps because the upstream host wasn't sending the right CORS headers and the node's security policy didn't allow the fallback URL either. The node now fetches the catalog server-side and serves it same-origin to the browser — no more blank app lists."
],
"components": [
{
"name": "archipelago",
"current_version": "1.7.9-alpha",
"new_version": "1.7.10-alpha",
"download_url": "https://git.tx1138.com/lfg2025/archy/raw/branch/main/releases/v1.7.10-alpha/archipelago",
"sha256": "4a77c704b5c1ac0b424ccfc7ed123c50e2708764ac2b4916af534e80382aa6f8",
"size_bytes": 40379696
"current_version": "1.7.12-alpha",
"new_version": "1.7.13-alpha",
"download_url": "https://git.tx1138.com/lfg2025/archy/raw/branch/main/releases/v1.7.13-alpha/archipelago",
"sha256": "0aaf72625a6cb164b35e30e0dc6f6084cbc96fd8d9da9480b78e85f4b979f22c",
"size_bytes": 40371192
},
{
"name": "archipelago-frontend-1.7.10-alpha.tar.gz",
"current_version": "1.7.9-alpha",
"new_version": "1.7.10-alpha",
"download_url": "https://git.tx1138.com/lfg2025/archy/raw/branch/main/releases/v1.7.10-alpha/archipelago-frontend-1.7.10-alpha.tar.gz",
"sha256": "0644a43611309031efbb9b235a3602f0828f709fcaec0047543d96e1cbd54f58",
"size_bytes": 76983846
"name": "archipelago-frontend-1.7.13-alpha.tar.gz",
"current_version": "1.7.12-alpha",
"new_version": "1.7.13-alpha",
"download_url": "https://git.tx1138.com/lfg2025/archy/raw/branch/main/releases/v1.7.13-alpha/archipelago-frontend-1.7.13-alpha.tar.gz",
"sha256": "27505811ffcae22a33cc895e2dc630b3efef7d0682841eeeea517d5efc6f4142",
"size_bytes": 76982505
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.