diff --git a/image-recipe/configs/nginx-archipelago.conf b/image-recipe/configs/nginx-archipelago.conf
index 8ba41d14..eade7f46 100644
--- a/image-recipe/configs/nginx-archipelago.conf
+++ b/image-recipe/configs/nginx-archipelago.conf
@@ -537,6 +537,27 @@ server {
sub_filter "src='/" "src='/ext/arch-presentation/";
sub_filter '' '';
}
+ location /ext/nostrudel/ {
+ proxy_pass https://nostrudel.ninja/;
+ proxy_http_version 1.1;
+ proxy_set_header Host nostrudel.ninja;
+ proxy_set_header Accept-Encoding "";
+ proxy_ssl_server_name on;
+ proxy_hide_header X-Frame-Options;
+ proxy_hide_header Content-Security-Policy;
+ proxy_hide_header Cross-Origin-Embedder-Policy;
+ proxy_hide_header Cross-Origin-Opener-Policy;
+ proxy_hide_header Cross-Origin-Resource-Policy;
+ add_header X-Content-Type-Options "nosniff" always;
+ sub_filter_once off;
+ sub_filter_types text/css application/javascript;
+ sub_filter 'href="/' 'href="/ext/nostrudel/';
+ sub_filter 'src="/' 'src="/ext/nostrudel/';
+ sub_filter 'action="/' 'action="/ext/nostrudel/';
+ sub_filter "href='/" "href='/ext/nostrudel/";
+ sub_filter "src='/" "src='/ext/nostrudel/";
+ sub_filter '' '';
+ }
# Proxy WebSocket
location /ws {
@@ -814,6 +835,27 @@ server {
sub_filter "src='/" "src='/ext/arch-presentation/";
sub_filter '' '';
}
+ location /ext/nostrudel/ {
+ proxy_pass https://nostrudel.ninja/;
+ proxy_http_version 1.1;
+ proxy_set_header Host nostrudel.ninja;
+ proxy_set_header Accept-Encoding "";
+ proxy_ssl_server_name on;
+ proxy_hide_header X-Frame-Options;
+ proxy_hide_header Content-Security-Policy;
+ proxy_hide_header Cross-Origin-Embedder-Policy;
+ proxy_hide_header Cross-Origin-Opener-Policy;
+ proxy_hide_header Cross-Origin-Resource-Policy;
+ add_header X-Content-Type-Options "nosniff" always;
+ sub_filter_once off;
+ sub_filter_types text/css application/javascript;
+ sub_filter 'href="/' 'href="/ext/nostrudel/';
+ sub_filter 'src="/' 'src="/ext/nostrudel/';
+ sub_filter 'action="/' 'action="/ext/nostrudel/';
+ sub_filter "href='/" "href='/ext/nostrudel/";
+ sub_filter "src='/" "src='/ext/nostrudel/";
+ sub_filter '' '';
+ }
location /ws {
proxy_pass http://127.0.0.1:5678;
diff --git a/loop/plan.md b/loop/plan.md
index 5974099e..98cb04f2 100644
--- a/loop/plan.md
+++ b/loop/plan.md
@@ -486,7 +486,7 @@
- [x] **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.
-- [ ] **NIP07-03** — Test NIP-07 with a real Nostr web app. Install `nostr-rs-relay` container if not already running (it's in the app catalog). Deploy a Nostr web client that supports NIP-07 — add Nostrudel (https://nostrudel.ninja) as a web-only app entry in `Marketplace.vue` `getCuratedAppList()` (category: "Social", opens in iframe). Open Nostrudel, verify it detects `window.nostr`, can fetch the pubkey, and can sign events (post a note). **Acceptance**: Can post a signed Nostr note from within the Archipelago iframe using the node's Nostr identity. Verify the note appears on a public Nostr client.
+- [x] **NIP07-03** — Test NIP-07 with a real Nostr web app. Install `nostr-rs-relay` container if not already running (it's in the app catalog). Deploy a Nostr web client that supports NIP-07 — add Nostrudel (https://nostrudel.ninja) as a web-only app entry in `Marketplace.vue` `getCuratedAppList()` (category: "Social", opens in iframe). Open Nostrudel, verify it detects `window.nostr`, can fetch the pubkey, and can sign events (post a note). **Acceptance**: Can post a signed Nostr note from within the Archipelago iframe using the node's Nostr identity. Verify the note appears on a public Nostr client.
- [ ] **NIP07-04** — Support NIP-04 and NIP-44 encryption in iframe provider. The `nostr-provider.js` already has stubs for `nip04.encrypt`, `nip04.decrypt`, `nip44.encrypt`, `nip44.decrypt`. Add backend RPC endpoints: `identity.nostr-encrypt-nip04`, `identity.nostr-decrypt-nip04`, `identity.nostr-encrypt-nip44`, `identity.nostr-decrypt-nip44`. Each takes the identity ID, peer pubkey, and plaintext/ciphertext. Use `nostr_sdk` for the actual crypto. Register in RPC router. Wire the appLauncher `handleNostrRequest` to route `nip04.*` and `nip44.*` calls to these endpoints. **Acceptance**: From an iframe app, call `window.nostr.nip44.encrypt(peerPubkey, "hello")` — returns ciphertext. Call `nip44.decrypt` with same ciphertext — returns "hello". Deploy and verify.
diff --git a/neode-ui/public/assets/img/app-icons/nostrudel.svg b/neode-ui/public/assets/img/app-icons/nostrudel.svg
new file mode 100644
index 00000000..864fae1b
--- /dev/null
+++ b/neode-ui/public/assets/img/app-icons/nostrudel.svg
@@ -0,0 +1,7 @@
+
diff --git a/neode-ui/src/stores/appLauncher.ts b/neode-ui/src/stores/appLauncher.ts
index e5275f40..cc800a44 100644
--- a/neode-ui/src/stores/appLauncher.ts
+++ b/neode-ui/src/stores/appLauncher.ts
@@ -17,6 +17,7 @@ const EXTERNAL_PROXY_PATH: Record = {
'botfights.net': '/ext/botfights/',
'484.kitchen': '/ext/484-kitchen/',
'present.l484.com': '/ext/arch-presentation/',
+ 'nostrudel.ninja': '/ext/nostrudel/',
}
function mustOpenInNewTab(url: string): boolean {
diff --git a/neode-ui/src/views/Marketplace.vue b/neode-ui/src/views/Marketplace.vue
index 359ffd13..7968a623 100644
--- a/neode-ui/src/views/Marketplace.vue
+++ b/neode-ui/src/views/Marketplace.vue
@@ -943,6 +943,18 @@ function getCuratedAppList() {
manifestUrl: undefined,
repoUrl: 'https://github.com/TBD54566975/dwn-server'
},
+ {
+ id: 'nostrudel',
+ title: 'noStrudel',
+ version: '0.40.0',
+ description: 'A feature-rich Nostr web client with NIP-07 signer support. Browse your feed, post notes, manage relays, and interact with the Nostr network — all signed with your node\'s Nostr identity.',
+ icon: '/assets/img/app-icons/nostrudel.svg',
+ author: 'hzrd149',
+ dockerImage: '',
+ manifestUrl: undefined,
+ repoUrl: 'https://github.com/hzrd149/nostrudel',
+ webUrl: 'https://nostrudel.ninja'
+ },
{
id: 'nostr-rs-relay',
title: 'Nostr Relay',