Bouquet for Android

June 3, 2026 · View on GitHub

Build

English | 日本語

Nostr static-website gateway for Android. Paste an npub1… or a NIP-5A canonical label, fetch the manifest event from relays, fetch blobs from Blossom servers, and render the site in a hardened in-app WebView or in the system browser.

Abstract

NIP-5A publishes static sites as a Nostr manifest event (kind 15128 / 35128) plus file blobs on Blossom servers. The spec ships a smart-server reference: an HTTPS host (e.g. nsite-host.com) resolves the manifest, fetches blobs, and re-serves the site. Anyone can run such a host, but whichever one the browser connects to becomes a trusted intermediary that terminates TLS, can observe requests, and can take a pubkey offline by dropping a subdomain.

Bouquet inverts that. The Android app subscribes to relays directly, fetches the manifest, verifies each blob's SHA-256, and serves the result over http://127.0.0.1 to a hardened in-app WebView. No third party sits between the user and the Nostr / Blossom network.

NIP-5A approach (smart server):

  ┌──────────────────────────────────────┐
  │  Relays + Blossom servers   (dumb)   │
  └────────────────────┬─────────────────┘
                       │ site manifest + blobs

  ┌──────────────────────────────────────┐
  │  nsite-host.com             (smart)  │
  │  resolves manifest · fetches blobs · │
  │  serves HTTPS                        │
  └────────────────────┬─────────────────┘
                       │ HTTPS

  ┌──────────────────────────────────────┐
  │  User device                (dumb)   │
  │  browser only — trusts the host      │
  └──────────────────────────────────────┘

Bouquet approach (smart client):

  ┌──────────────────────────────────────┐
  │  Relays + Blossom servers   (dumb)   │
  └────────────────────┬─────────────────┘
                       │ site manifest + blobs

  ┌──────────────────────────────────────┐
  │  User device                (smart)  │
  │  Bouquet resolves manifest ·         │
  │  fetches blobs · verifies SHA-256 ·  │
  │  serves on 127.0.0.1 → WebView       │
  └──────────────────────────────────────┘

nsite-deck is a desktop (macOS / Linux) smart-client: it installs a local DNS resolver to intercept *.nsite, runs an embedded Khatru relay + Blossom server as a persistent cache, and ships a management UI so any browser can open https://npub1….nsite directly. Bouquet covers the opposite case — viewing nsites on a phone with no system-level changes. No DNS changes, no browser configuration: a single APK gives you a hardened in-app WebView talking to a per-session loopback server, and the only persistent state is a small on-disk cache.

nsite-deckBouquet
PlatformmacOS / LinuxAndroid
OS changesDNS resolver + systemd / launchd daemonsnone — single APK
URL entrybrowser address bar via *.nsite DNSpaste npub / canonical label in-app
Local servicesembedded relay + Blossom + gatewayper-session loopback HTTP
Cachepersistent (real Nostr relay)TTL 5 min events + content-addressed blobs

Demo

https://github.com/user-attachments/assets/6bd2a5c8-a129-4afe-83bf-15ee7fe2af26

Install

  1. Download the latest bouquet-android-vX.Y.Z.apk from the Releases page.
  2. Open the file on your Pixel / Android device (e.g. from the Files app).
  3. Allow installation from unknown sources when prompted.
  4. Install, then launch.

Android 8.0 (API 26) or newer is required.

Usage

  1. Paste an npub1… (root site, kind 15128) or a NIP-5A canonical label <pubkeyB36><dTag> (named site, kind 35128) into the input field.
  2. The decoded result (type, pubkey, and identifier when applicable) is shown below as you type.
  3. Tap Open in browser or Open in WebView.
  4. The app fetches write relays, retrieves the manifest, fetches the Blossom server list, and then renders the site in your browser or the in-app WebView.

Build from source

./gradlew :app:assembleDebug
adb install app/build/outputs/apk/debug/app-debug.apk

JDK 21 is required (either the JBR bundled with Android Studio or Homebrew's openjdk@21 works).

Stack

LayerLibrary / Component
LanguageKotlin 2.2.10
UIJetpack Compose Material3, dark-only
NostrQuartz (com.vitorpamplona.quartz:quartz)
ViewerNanoHTTPD per-session loopback listener + WebView
CacheJSON-backed event cache (TTL 5 min) + content-addressed blob cache
BuildAGP 9.1.1, JVM 21, R8 minified

Development

Run the unit tests:

./gradlew :app:testDebugUnitTest

Build a release APK locally (falls back to debug signing if no release keystore is configured):

./gradlew :app:assembleRelease

Releasing (for maintainers)

When a v* tag is pushed, GitHub Actions builds the APK, signs it with the release keystore, and attaches it to the Releases page.

git tag v0.1.0
git push origin v0.1.0

Acknowledgements

This project was inspired by and references the following prior work:

License

MIT