Bouquet for Android
June 3, 2026 · View on GitHub
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 │
└──────────────────────────────────────┘
Related: nsite-deck
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-deck | Bouquet | |
|---|---|---|
| Platform | macOS / Linux | Android |
| OS changes | DNS resolver + systemd / launchd daemons | none — single APK |
| URL entry | browser address bar via *.nsite DNS | paste npub / canonical label in-app |
| Local services | embedded relay + Blossom + gateway | per-session loopback HTTP |
| Cache | persistent (real Nostr relay) | TTL 5 min events + content-addressed blobs |
Demo
https://github.com/user-attachments/assets/6bd2a5c8-a129-4afe-83bf-15ee7fe2af26
Install
- Download the latest
bouquet-android-vX.Y.Z.apkfrom the Releases page. - Open the file on your Pixel / Android device (e.g. from the Files app).
- Allow installation from unknown sources when prompted.
- Install, then launch.
Android 8.0 (API 26) or newer is required.
Usage
- Paste an
npub1…(root site, kind 15128) or a NIP-5A canonical label<pubkeyB36><dTag>(named site, kind 35128) into the input field. - The decoded result (type, pubkey, and identifier when applicable) is shown below as you type.
- Tap Open in browser or Open in WebView.
- 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
| Layer | Library / Component |
|---|---|
| Language | Kotlin 2.2.10 |
| UI | Jetpack Compose Material3, dark-only |
| Nostr | Quartz (com.vitorpamplona.quartz:quartz) |
| Viewer | NanoHTTPD per-session loopback listener + WebView |
| Cache | JSON-backed event cache (TTL 5 min) + content-addressed blob cache |
| Build | AGP 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: