From 76e6f069951fbeb54baa3062f3b5975c34414653 Mon Sep 17 00:00:00 2001 From: archipelago Date: Wed, 12 Aug 2026 12:38:17 -0400 Subject: [PATCH] fix(phoenixd): dot-free datadir + data_uid; docs: iframe rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- apps/alby-hub/manifest.yml | 5 ++++- apps/phoenixd/manifest.yml | 13 ++++++++++++- docs/app-developer-guide.md | 33 ++++++++++++++++++++++++++++++++- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/apps/alby-hub/manifest.yml b/apps/alby-hub/manifest.yml index bbb1b6ed..d0a4a365 100644 --- a/apps/alby-hub/manifest.yml +++ b/apps/alby-hub/manifest.yml @@ -64,7 +64,10 @@ app: repo: https://github.com/getAlby/hub tier: optional launch: - open_in_new_tab: true + # Embedded: the gate neutralizes Alby Hub's X-Frame-Options: DENY on + # proxied responses. Nodes older than the gate fix show a blocked + # frame — flip to true only if targeting such nodes. + open_in_new_tab: false features: - Self-custodial Lightning node (LDK) with a friendly wallet UI - Connect wallets and apps via Nostr Wallet Connect (NWC) diff --git a/apps/phoenixd/manifest.yml b/apps/phoenixd/manifest.yml index 6ec924ee..7c71c1cb 100644 --- a/apps/phoenixd/manifest.yml +++ b/apps/phoenixd/manifest.yml @@ -10,6 +10,10 @@ app: # --http-bind-ip 0.0.0.0, as user "phoenix"; no custom args needed. image: source.archipelago-foundation.org/lfg2025/phoenixd:0.9.0 pull_policy: if-not-present + # The image runs as user phoenix (1000:1000); the datadir bind source + # must be chowned to that identity or phoenixd dies on + # "Failed to open /data/phoenix.conf with Permission denied". + data_uid: "1000:1000" dependencies: - storage: 500Mi @@ -41,11 +45,18 @@ app: # The wallet seed (seed.dat) and phoenix.conf live here. This directory # must survive reinstall/migration like any other app data dir — # losing it means losing funds. + # Target is /data (via PHOENIX_DATADIR below), NOT the image's default + # /phoenix/.phoenix: the orchestrator treats any bind path containing a + # dot as a file mount and skips creating its source directory, so a + # hidden-dir target never gets its host dir and the unit crash-loops. - type: bind source: /var/lib/archipelago/phoenixd - target: /phoenix/.phoenix + target: /data options: [rw] + environment: + - PHOENIX_DATADIR=/data + health_check: type: tcp endpoint: localhost:9740 diff --git a/docs/app-developer-guide.md b/docs/app-developer-guide.md index 8b3cfe5a..32128413 100644 --- a/docs/app-developer-guide.md +++ b/docs/app-developer-guide.md @@ -130,7 +130,38 @@ app: Additional extension keys may exist for current integrations, for example Bitcoin, Lightning, or app-specific launch/interface metadata. Treat extension keys as transitional unless they are documented as reusable platform primitives. -Use `metadata.launch.open_in_new_tab: true` when the app UI is known to reject iframe embedding with headers such as `X-Frame-Options` or restrictive CSP. The frontend app-session metadata is generated from this flag during release work. +### Iframe embedding — the rules + +The dashboard opens apps in an **embedded frame** (My Apps → app session) by +default. Whether that works is decided by HTTP headers, not by wishes, so +know the mechanics: + +- Browsers refuse to render a page in an iframe when the response carries + `X-Frame-Options: DENY`/`SAMEORIGIN` (the dashboard and the app are + different origins — different port at minimum) or a CSP `frame-ancestors` + directive that excludes the dashboard's origin. +- Many upstream apps ship exactly those headers (Alby Hub sends + `X-Frame-Options: DENY`). In a normal deployment that is correct hardening; + behind Archipelago's app gate the clickjacking threat those headers address + is already handled — every proxied request is authenticated by the gate + first. +- Therefore **the gate neutralizes frame blocking on proxied responses**: it + removes `X-Frame-Options` and strips only the `frame-ancestors` directive + from the app's CSP. The rest of the app's CSP (script-src, connect-src, …) + passes through untouched — the gate never weakens the app's own content + policy, only its framing policy. You do not need a bespoke reverse proxy, + header patches, or app config to be embeddable. + +Set `metadata.launch.open_in_new_tab: true` only when embedding is broken by +things headers can't fix — the app frame-busts in JavaScript, requires being +the top-level origin (OAuth redirect flows, WebAuthn), or sets +`SameSite=Strict` session cookies that never accompany framed requests. Test +in the real embedded app session, **not** a plain browser tab: tabs don't +enforce framing headers, so a tab proves nothing about the iframe. + +(Historical note: before the gate handled this, embeddable-but-blocking apps +each carried a hand-built nginx strip proxy — gitea's port-3000 proxy is the +surviving example. Do not copy that pattern for new apps.) ### Launch Interfaces