diff --git a/image-recipe/configs/nginx-archipelago.conf b/image-recipe/configs/nginx-archipelago.conf index 5bdf399e..8ba41d14 100644 --- a/image-recipe/configs/nginx-archipelago.conf +++ b/image-recipe/configs/nginx-archipelago.conf @@ -150,6 +150,9 @@ server { proxy_hide_header Content-Security-Policy; proxy_read_timeout 300s; proxy_send_timeout 300s; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/vaultwarden/ { proxy_pass http://127.0.0.1:8082/; @@ -160,6 +163,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/immich/ { proxy_pass http://127.0.0.1:2283/; @@ -172,6 +178,9 @@ server { proxy_hide_header Content-Security-Policy; proxy_read_timeout 300s; proxy_send_timeout 300s; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/penpot/ { proxy_pass http://127.0.0.1:9001/; @@ -184,6 +193,9 @@ server { proxy_hide_header Content-Security-Policy; proxy_read_timeout 300s; proxy_send_timeout 300s; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } # Block path traversal attempts before they reach FileBrowser location ~* /app/filebrowser/api/resources/.*/\.\. { @@ -203,6 +215,9 @@ server { proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; proxy_request_buffering off; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/grafana/ { proxy_pass http://127.0.0.1:3000/; @@ -213,6 +228,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/jellyfin/ { proxy_pass http://127.0.0.1:8096/; @@ -223,6 +241,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/uptime-kuma/ { proxy_pass http://127.0.0.1:3001/; @@ -233,6 +254,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/portainer/ { proxy_pass http://127.0.0.1:9000/; @@ -243,6 +267,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/onlyoffice/ { proxy_pass http://127.0.0.1:9980/; @@ -253,6 +280,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } # Remaining apps (also available on HTTPS via snippet include) location /app/searxng/ { @@ -264,6 +294,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/lnd/ { proxy_pass http://127.0.0.1:8081/; @@ -276,6 +309,9 @@ server { proxy_hide_header Content-Security-Policy; proxy_read_timeout 300s; proxy_send_timeout 300s; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/mempool/ { proxy_pass http://127.0.0.1:4080/; @@ -288,6 +324,9 @@ server { proxy_hide_header Content-Security-Policy; proxy_read_timeout 300s; proxy_send_timeout 300s; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/photoprism/ { proxy_pass http://127.0.0.1:2342/; @@ -298,6 +337,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/fedimint/ { proxy_pass http://127.0.0.1:8175/; @@ -310,6 +352,9 @@ server { proxy_hide_header Content-Security-Policy; proxy_read_timeout 300s; proxy_send_timeout 300s; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/fedimint-gateway/ { proxy_pass http://127.0.0.1:8176/; @@ -322,6 +367,9 @@ server { proxy_hide_header Content-Security-Policy; proxy_read_timeout 300s; proxy_send_timeout 300s; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/tailscale/ { proxy_pass http://127.0.0.1:8240/; @@ -332,6 +380,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/ollama/ { proxy_pass http://127.0.0.1:11434/; @@ -342,6 +393,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/bitcoin-ui/ { proxy_pass http://127.0.0.1:8334/; @@ -352,6 +406,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/electrs/ { proxy_pass http://127.0.0.1:50002/; @@ -362,6 +419,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/endurain/ { proxy_pass http://127.0.0.1:8080/; @@ -372,6 +432,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/nginx-proxy-manager/ { proxy_pass http://127.0.0.1:81/; @@ -382,6 +445,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/btcpay/ { proxy_pass http://127.0.0.1:23000/; @@ -392,6 +458,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/homeassistant/ { proxy_pass http://127.0.0.1:8123/; @@ -404,6 +473,9 @@ server { proxy_hide_header Content-Security-Policy; proxy_read_timeout 86400s; proxy_send_timeout 86400s; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } # External site proxies — strip X-Frame-Options so iframe embedding works. @@ -605,6 +677,9 @@ server { proxy_hide_header Content-Security-Policy; proxy_read_timeout 300s; proxy_send_timeout 300s; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/vaultwarden/ { proxy_pass http://127.0.0.1:8082/; @@ -615,6 +690,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/immich/ { proxy_pass http://127.0.0.1:2283/; @@ -627,6 +705,9 @@ server { proxy_hide_header Content-Security-Policy; proxy_read_timeout 300s; proxy_send_timeout 300s; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/penpot/ { proxy_pass http://127.0.0.1:9001/; @@ -639,6 +720,9 @@ server { proxy_hide_header Content-Security-Policy; proxy_read_timeout 300s; proxy_send_timeout 300s; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/btcpay/ { proxy_pass http://127.0.0.1:23000/; @@ -649,6 +733,9 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/homeassistant/ { proxy_pass http://127.0.0.1:8123/; @@ -661,6 +748,9 @@ server { proxy_hide_header Content-Security-Policy; proxy_read_timeout 86400s; proxy_send_timeout 86400s; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } # All remaining app proxies (mempool, fedimint, lnd, bitcoin-ui, etc.) include snippets/archipelago-https-app-proxies.conf; diff --git a/image-recipe/configs/snippets/archipelago-https-app-proxies.conf b/image-recipe/configs/snippets/archipelago-https-app-proxies.conf index 23f62c05..8a6cd40a 100644 --- a/image-recipe/configs/snippets/archipelago-https-app-proxies.conf +++ b/image-recipe/configs/snippets/archipelago-https-app-proxies.conf @@ -9,6 +9,9 @@ location /app/grafana/ { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/uptime-kuma/ { proxy_pass http://127.0.0.1:3001/; @@ -19,6 +22,9 @@ location /app/uptime-kuma/ { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/searxng/ { proxy_pass http://127.0.0.1:8888/; @@ -29,6 +35,9 @@ location /app/searxng/ { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/portainer/ { proxy_pass http://127.0.0.1:9000/; @@ -39,6 +48,9 @@ location /app/portainer/ { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/filebrowser/ { client_max_body_size 10G; @@ -51,6 +63,9 @@ location /app/filebrowser/ { proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; proxy_request_buffering off; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/endurain/ { proxy_pass http://127.0.0.1:8080/; @@ -61,6 +76,9 @@ location /app/endurain/ { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/lnd/ { proxy_pass http://127.0.0.1:8081/; @@ -73,6 +91,9 @@ location /app/lnd/ { proxy_hide_header Content-Security-Policy; proxy_read_timeout 300s; proxy_send_timeout 300s; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/onlyoffice/ { proxy_pass http://127.0.0.1:9980/; @@ -83,6 +104,9 @@ location /app/onlyoffice/ { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/jellyfin/ { proxy_pass http://127.0.0.1:8096/; @@ -93,6 +117,9 @@ location /app/jellyfin/ { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/photoprism/ { proxy_pass http://127.0.0.1:2342/; @@ -103,6 +130,9 @@ location /app/photoprism/ { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/mempool/ { proxy_pass http://127.0.0.1:4080/; @@ -115,6 +145,9 @@ location /app/mempool/ { proxy_hide_header Content-Security-Policy; proxy_read_timeout 300s; proxy_send_timeout 300s; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/fedimint/ { proxy_pass http://127.0.0.1:8175/; @@ -127,6 +160,9 @@ location /app/fedimint/ { proxy_hide_header Content-Security-Policy; proxy_read_timeout 300s; proxy_send_timeout 300s; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/fedimint-gateway/ { proxy_pass http://127.0.0.1:8176/; @@ -139,6 +175,9 @@ location /app/fedimint-gateway/ { proxy_hide_header Content-Security-Policy; proxy_read_timeout 300s; proxy_send_timeout 300s; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/tailscale/ { proxy_pass http://127.0.0.1:8240/; @@ -149,6 +188,9 @@ location /app/tailscale/ { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/ollama/ { proxy_pass http://127.0.0.1:11434/; @@ -159,6 +201,9 @@ location /app/ollama/ { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/bitcoin-ui/ { proxy_pass http://127.0.0.1:8334/; @@ -169,6 +214,9 @@ location /app/bitcoin-ui/ { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/electrs/ { proxy_pass http://127.0.0.1:50002/; @@ -179,6 +227,9 @@ location /app/electrs/ { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } location /app/nginx-proxy-manager/ { proxy_pass http://127.0.0.1:81/; @@ -189,4 +240,7 @@ location /app/nginx-proxy-manager/ { proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header X-Frame-Options; proxy_hide_header Content-Security-Policy; + proxy_set_header Accept-Encoding ""; + sub_filter_once on; + sub_filter '' ''; } diff --git a/loop/plan.md b/loop/plan.md index 121952ef..cfba341d 100644 --- a/loop/plan.md +++ b/loop/plan.md @@ -482,7 +482,7 @@ ### Sprint 41: NIP-07 Iframe Signing (April 2026 Week 3-4) -- [ ] **NIP07-01** — Configure nginx to inject nostr-provider.js into iframe apps. In `image-recipe/configs/nginx-archipelago.conf`, for every `/app/*` proxy location block, add `sub_filter '' '';` and `sub_filter_once on;`. Ensure `proxy_set_header Accept-Encoding "";` is set (required for sub_filter to work on compressed responses). Copy `neode-ui/public/nostr-provider.js` to `/opt/archipelago/web-ui/nostr-provider.js` in the deploy script. Also add this to the HTTPS snippets conf at `image-recipe/configs/snippets/archipelago-https-app-proxies.conf`. **Acceptance**: Open any iframe app (e.g., Mempool at `/app/mempool/`), open browser DevTools console, type `window.nostr` — should return the provider object with `getPublicKey` and `signEvent` methods. Deploy and verify. +- [x] **NIP07-01** — Configure nginx to inject nostr-provider.js into iframe apps. In `image-recipe/configs/nginx-archipelago.conf`, for every `/app/*` proxy location block, add `sub_filter '' '';` and `sub_filter_once on;`. Ensure `proxy_set_header Accept-Encoding "";` is set (required for sub_filter to work on compressed responses). Copy `neode-ui/public/nostr-provider.js` to `/opt/archipelago/web-ui/nostr-provider.js` in the deploy script. Also add this to the HTTPS snippets conf at `image-recipe/configs/snippets/archipelago-https-app-proxies.conf`. **Acceptance**: Open any iframe app (e.g., Mempool at `/app/mempool/`), open browser DevTools console, type `window.nostr` — should return the provider object with `getPublicKey` and `signEvent` methods. Deploy and verify. - [ ] **NIP07-02** — Add signing consent modal. In `neode-ui/src/components/`, create `NostrSignConsent.vue` — a modal that shows when an iframe app requests a Nostr signature. Display: requesting app name/origin, event kind number, event content preview (truncated to 200 chars), and Approve/Deny buttons. In `neode-ui/src/stores/appLauncher.ts` `handleNostrRequest()`, instead of immediately signing, emit an event that triggers this modal. Only call the backend RPC after user approves. Add a "Remember for this app" checkbox that stores approved origins in localStorage. **Acceptance**: Open a Nostr app in iframe, trigger a sign request — consent modal appears. Approve → signature returned. Deny → error returned to iframe. Deploy and verify.