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
| Surface | Role |
|---|---|
| relay-connect-web | UI path “Sign with browser extension”: detects window.nostr, calls getPublicKey(), optionally loads kind 0 metadata for the success screen |
| @bitmacro/relay-connect | Shared 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)
- User chooses the NIP-07 option (or equivalent entry point).
- App checks
window.nostrandtypeof nip.getPublicKey === "function". - If missing, the app can redirect to a signer installation help URL (e.g. Nostr apps / signers list).
await nip.getPublicKey()returns the user’s hex pubkey.- Optional:
fetchNip07ProfileBundleuses nostr-toolsSimplePoolto query kind 0 (metadata) from a configurable relay set, then stores a small JSON bundle in sessionStorage for the/successpage (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:
NEXT_PUBLIC_NIP07_METADATA_RELAYS— comma- or space-separatedwss://URLs (highest priority when set).- Else
NEXT_PUBLIC_RELAY_BRIDGE_WSS— if it looks likewss://…, it is prepended to the default list. - 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) holdsNip07ProfileBundle: 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_secretor 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
| Scenario | Prefer |
|---|---|
| User already has Alby / nos2x on desktop | NIP-07 |
| User signs on phone (Amber, etc.) while app is on desktop | NIP-46 + QR |
Need audited server-side session rows (nip46_sessions) | NIP-46 (and /signer/*) |
| Quick “who am I” pubkey + profile for onboarding | NIP-07 |