Relay Manager Docs
Protocol

NIP-07 — Browser extension signing

window.nostr, local signing with Alby/nos2x-style extensions, and metadata profile fetch in relay-connect.

NIP-07 — Browser extension signing

NIP-07 standardises how web pages ask a browser extension (Alby, nos2x, Flipper, etc.) to sign Nostr events or reveal the user’s pubkey. The private key never enters the page: the extension exposes window.nostr with methods such as getPublicKey() and signEvent().

In the Relay Manager stack, NIP-07 is the on-device counterpart to NIP-46: same high-level goal (sign without pasting nsec in the app), different transport (extension on the same browser vs remote signer over a bridge).


Where it is implemented

SurfaceRole
relay-connect-webUI path “Sign with browser extension”: detects window.nostr, calls getPublicKey(), optionally loads kind 0 metadata for the success screen
@bitmacro/relay-connectShared helpers (extension detection, signing utilities; boundary with the UI evolves with the SDK)

relay-panel authentication remains GitHub / NextAuth only. NIP-07 is for end-user Nostr identity in connect flows, not for operator login to the panel.


Flow (relay-connect-web)

  1. User chooses the NIP-07 option (or equivalent entry point).
  2. App checks window.nostr and typeof nip.getPublicKey === "function".
  3. If missing, the app can redirect to a signer installation help URL (e.g. Nostr apps / signers list).
  4. await nip.getPublicKey() returns the user’s hex pubkey.
  5. Optional: fetchNip07ProfileBundle uses nostr-tools SimplePool to query kind 0 (metadata) from a configurable relay set, then stores a small JSON bundle in sessionStorage for the /success page (no Supabase row for a pure NIP-07 profile-only flow).

Signing of specific events (signEvent) follows the same extension API when the product needs a signature; the exact calls depend on the screen (NIP-46 remains the path for long-lived remote signing sessions).


Metadata relays (NEXT_PUBLIC_NIP07_METADATA_RELAYS)

After getPublicKey(), loading a display name / avatar requires reading kind 0 from the network. relay-connect-web merges:

  1. NEXT_PUBLIC_NIP07_METADATA_RELAYS — comma- or space-separated wss:// URLs (highest priority when set).
  2. Else NEXT_PUBLIC_RELAY_BRIDGE_WSS — if it looks like wss://…, it is prepended to the default list.
  3. Else a stable default list (Damus, nos.lol, Primal, Nostr Band, etc.) so dev works out of the box.

Duplicates are deduped. Operators can force their own relays for privacy or reliability.


Storage and lifecycle

  • Session key: relay_connect_nip07_profile (STORAGE_NIP07_PROFILE_KEY) holds Nip07ProfileBundle: pubkey, parsed profile fields, optional raw metadata event ref, extension hints, list of relays queried, timestamp.
  • Starting a NIP-46 connect clears this storage so the two flows do not collide in one session.

Security notes

  • The web origin is trusted by the extension per the extension’s rules; users should only approve prompts they understand.
  • Unlike NIP-46, there is no pairing_secret or bridge relay in the path; trust boundary is browser + extension on one machine.
  • relay-api is not required for “pubkey + kind 0 display” only; any signing that must be tied to operator relays still goes through your app rules and optionally server-side checks.

When to use NIP-07 vs NIP-46

ScenarioPrefer
User already has Alby / nos2x on desktopNIP-07
User signs on phone (Amber, etc.) while app is on desktopNIP-46 + QR
Need audited server-side session rows (nip46_sessions)NIP-46 (and /signer/*)
Quick “who am I” pubkey + profile for onboardingNIP-07

See also