12 KiB
nostr:// URI Scheme + JSON Viewer — Implementation Plan
Overview
Two complementary features that work together:
-
JSON Viewer (Phase 1) — A rich JSON viewer with syntax highlighting and collapsible tree view, injected into all
application/jsonresponses. Useful everywhere, not just for nostr://. -
nostr:// URL Handling (Phase 2) — Smart URL bar parsing for Nostr bech32 entities. The browser fetches the event(s) from relays and displays the raw JSON (using the Phase 1 viewer). At the top of the page, a toolbar shows clickable links to configured external Nostr clients (helper applications). The browser is NOT a Nostr client — it shows raw data and lets the user open it in a client of their choice.
Phase 1: JSON Viewer
Goal
When the browser loads any application/json content (from any URL
scheme — http, https, file, sovereign, nostr), instead of WebKit's
default plain <pre> display, inject a rich JSON viewer with:
- Syntax highlighting — colored keys, strings, numbers, booleans, null
- Collapsible tree view — click to expand/collapse objects and arrays
- Copy buttons — copy the raw JSON or a specific path
- Dark mode — respects
prefers-color-scheme - Search/filter — filter keys in large JSON documents
Architecture
WebKitGTK's built-in JSON display wraps raw JSON in a <pre> tag. We
intercept this by injecting a user script that:
- Detects when the page content type is
application/json - Parses the
<pre>content as JSON - Replaces the
<pre>with a styled tree view
Implementation approach: Use
webkit_web_view_add_user_content_filter or a WebKitUserScript injected
via WebKitUserContentManager. The script runs on all pages but only
activates when document.contentType === 'application/json'.
Alternatively, intercept at the resource-load-started or
load-changed level and inject the viewer script after load.
Files
www/
├── json-viewer/
│ ├── json-viewer.js # Tree view renderer + syntax highlighting
│ └── json-viewer.css # Tree view styles (light + dark)
src/
├── nostr_bridge.c # Extended: serve json-viewer.js/css via sovereign://
├── tab_manager.c # Extended: inject json-viewer script on JSON pages
JSON Viewer JS Design
// Pseudo-code for the viewer
(function() {
if (document.contentType !== 'application/json') return;
var raw = document.body.textContent;
var data;
try { data = JSON.parse(raw); } catch(e) { return; } // not valid JSON, leave as-is
var viewer = createTreeView(data);
document.body.innerHTML = '';
document.body.appendChild(viewer);
function createTreeView(obj, path) {
// Recursively create collapsible nodes:
// - Objects: { key: value, ... } with expand/collapse
// - Arrays: [ item, ... ] with expand/collapse
// - Strings: green, with quotes
// - Numbers: blue
// - Booleans: orange
// - Null: gray
// - Nested objects/arrays: collapsible with item count
}
})();
Styling
- Light mode: white background, dark text, colored values
- Dark mode: dark background, light text, adjusted colors
- Monospace font for all JSON content
- Indentation guides (vertical lines for nesting depth)
- Hover highlight on rows
- Expand/collapse arrows (▶ ▼)
Settings
json_viewer_enabled(default: true) — toggle in Settings dialog- Can be disabled per-page via a keyboard shortcut (e.g., Ctrl+Shift+J)
Phase 2: nostr:// URL Handling
Goal
The browser recognizes Nostr bech32 entities in the URL bar, fetches the event(s) from relays, and displays the raw JSON using the Phase 1 JSON viewer. At the top of the page, a toolbar shows clickable links to configured external Nostr clients (helper applications). The browser is NOT a Nostr client — it shows raw data and lets the user choose to open it in an external client.
User Flow
- User pastes
npub1abc...ornostr:npub1abc...in the URL bar - Browser fetches the relevant event(s) from relays
- Browser displays the raw JSON with the nice tree viewer (Phase 1)
- At the top of the page, a toolbar shows: "Open in: [Jumble] [Snort] [njump.me]"
- Clicking a helper app link opens that client's URL for the same entity in a new tab
URL Bar Smart Parsing
When the user types or pastes something in the URL bar, the browser should recognize these formats and normalize them:
| Input | Recognized as | Normalized to |
|---|---|---|
npub1abc... |
NIP-19 npub | nostr:npub1abc... |
nsec1abc... |
NIP-19 nsec | (rejected — private key, don't navigate) |
note1abc... |
NIP-19 note | nostr:note1abc... |
nevent1abc... |
NIP-19 nevent | nostr:nevent1abc... |
naddr1abc... |
NIP-19 naddr | nostr:naddr1abc... |
nprofile1abc... |
NIP-19 nprofile | nostr:nprofile1abc... |
nrelay1abc... |
NIP-19 nrelay | nostr:nrelay1abc... |
nostr:npub1abc... |
NIP-21 URI | (as-is) |
nostr://npub1abc... |
Our scheme | (as-is) |
NIP-21 defines nostr: as the standard prefix for bech32 entities
in links. We support both nostr:npub1... (NIP-21 standard) and
nostr://npub1... (our scheme variant).
nostr:// URI Scheme Handler
Register nostr:// (and handle nostr:) as a WebKit URI scheme. When
navigated to:
- Parse the bech32 entity from the URL
- Determine what events to fetch based on entity type:
npub/nprofile: kind 0 (metadata), recent kind 1 (notes), kind 3 (contacts), kind 10002 (relays)note/nevent: the specific event by IDnaddr: the event by kind + pubkey + d-tagnrelay: NIP-11 relay info (HTTP GET)
- Fetch events from relays (using relay hints from the entity, or user's configured relays)
- Return the event(s) as JSON with
Content-Type: application/json - The Phase 1 JSON viewer renders it with the tree view
- The JSON viewer also injects a helper-app toolbar at the top of the page
Helper App Toolbar
When the JSON viewer detects that the page is a nostr:// response, it
adds a toolbar above the JSON tree:
┌─────────────────────────────────────────────────────────────┐
│ Nostr entity: npub1abc... │ Open in: [Jumble] [Snort] [njump] │
└─────────────────────────────────────────────────────────────┘
Each button is a link that opens the helper app's URL for this entity in a new tab. The toolbar is styled to match the JSON viewer's theme.
Helper App Configuration
In the Settings dialog, users configure a list of helper applications:
Helper applications for Nostr entities:
Name: Jumble URL: https://jumble.social/{entity}
Name: Snort URL: https://snort.social/{entity}
Name: njump.me URL: https://njump.me/{entity}
Name: habla.news URL: https://habla.news/a/{naddr}
[+ Add helper app]
The {entity} placeholder is replaced with the full bech32 string
(e.g., npub1abc...). The {naddr} placeholder is replaced with the
naddr string (for article-specific clients). The {id} placeholder is
replaced with the hex event ID.
Default helper apps (if user hasn't configured):
- njump.me —
https://njump.me/{entity}(universal, handles all entity types) - Snort —
https://snort.social/{entity} - Jumble —
https://jumble.social/{entity}
Settings Storage
[nostr]
helper_apps=Jumble|https://jumble.social/{entity},Snort|https://snort.social/{entity},njump.me|https://njump.me/{entity}
Stored as a comma-separated list of Name|URL pairs. Parsed on startup
and passed to the JSON viewer via the sovereign:// bridge.
Files
src/
├── nostr_url.c # URL parsing + entity detection (new)
├── nostr_url.h # Public API (new)
├── nostr_scheme.c # nostr:// URI scheme handler (new)
├── nostr_scheme.h # Scheme handler API (new)
├── tab_manager.c # Extended: URL bar smart parsing
├── settings.c # Extended: helper app config
├── nostr_bridge.c # Extended: settings page + helper app API
└── relay_fetch.c # Extended: ad-hoc event fetch by entity
www/
├── json-viewer/
│ └── json-viewer.js # Extended: helper app toolbar for nostr:// pages
├── settings.html # Extended: helper app config UI
└── settings.js # Extended: save/load helper app config
URL Bar Integration
In tab_manager.c, the normalize_url() function (or the URL entry
activate handler) is extended:
/* Before trying to parse as a URL, check if it's a Nostr bech32 entity */
if (nostr_url_detect(input) != NOSTR_ENTITY_NONE) {
char *nostr_url = nostr_url_normalize(input);
/* Navigate to the nostr:// scheme — the scheme handler will
* fetch events and return JSON */
webkit_web_view_load_uri(webview, nostr_url);
g_free(nostr_url);
return;
}
Link Interception
When a page contains nostr:npub1... links (NIP-21 format), the
on_decide_policy handler intercepts the navigation:
if (strncmp(uri, "nostr:", 6) == 0) {
/* Normalize and navigate via our nostr:// scheme handler */
char *nostr_url = nostr_url_normalize(uri);
webkit_web_view_load_uri(webview, nostr_url);
g_free(nostr_url);
return TRUE; /* ignore the original navigation */
}
NIP-19 / NIP-21 Reference
NIP-19: bech32-encoded entities
| Prefix | Description | Data |
|---|---|---|
npub1 |
Public key | 32-byte pubkey |
nsec1 |
Private key | 32-byte privkey |
note1 |
Event ID | 32-byte event ID |
nprofile1 |
Profile | TLV: pubkey + relay hints |
nevent1 |
Event | TLV: event ID + relay hints + author |
naddr1 |
Address | TLV: kind + pubkey + d-tag + relay hints |
nrelay1 |
Relay | TLV: relay URL |
NIP-21: URI scheme
NIP-21 defines nostr: as the standard URI prefix:
nostr:npub1abc...— link to a profilenostr:note1abc...— link to a notenostr:nevent1abc...— link to an eventnostr:naddr1abc...— link to an addressable eventnostr:nprofile1abc...— link to a profile (with relay hints)nostr:nrelay1abc...— link to a relay
We support both nostr:entity (NIP-21) and nostr://entity (our
variant) — they're treated identically.
Implementation Order
Phase 1: JSON Viewer
- Create
www/json-viewer/json-viewer.js— tree view + syntax highlighting - Create
www/json-viewer/json-viewer.css— styling (light + dark) - Embed via
embed_web_files.sh - Inject into webviews via
WebKitUserScriptor content filter - Add
json_viewer_enabledsetting - Test with local .json files, API responses, and sovereign:// endpoints
Phase 2: nostr:// URL Handling
- Create
nostr_url.c— bech32 entity detection and parsing - Extend
nip019in nostr_core_lib to decode TLV entities (nprofile, nevent, naddr, nrelay) - Create
nostr_scheme.c— registernostr://URI scheme handler - Extend
relay_fetch.c— ad-hoc event fetch by entity type - Add helper app configuration to settings (list of Name|URL pairs)
- Extend URL bar
normalize_url()to detect bare bech32 entities - Extend
on_decide_policyto interceptnostr:links - Extend JSON viewer to show helper app toolbar on
nostr://pages - Add settings UI for configuring helper apps
- Test with various bech32 entities pasted in URL bar