NIP-07 Integration

Browser extension integration for Nostr signing.
What is NIP-07?
NIP-07 defines a standard for web browsers to provide Nostr signing capabilities through the window.nostr object. This allows web applications to request signatures without accessing the user's private key.
The window.nostr Object
When a NIP-07 compatible extension is installed, it exposes:
javascript
window.nostr = {
// Required methods
getPublicKey(): Promise<string>,
signEvent(event: UnsignedEvent): Promise<Event>,
// Optional methods
nip04: {
encrypt(pubkey: string, plaintext: string): Promise<string>,
decrypt(pubkey: string, ciphertext: string): Promise<string>
},
nip44: {
encrypt(pubkey: string, plaintext: string): Promise<string>,
decrypt(pubkey: string, ciphertext: string): Promise<string>
}
}Required Methods
getPublicKey
Returns the user's public key as a hex string.
javascript
const pubkey = await window.nostr.getPublicKey()
// Returns: "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"signEvent
Signs a Nostr event, adding id, pubkey, and sig fields.
javascript
const unsignedEvent = {
created_at: Math.floor(Date.now() / 1000),
kind: 1,
tags: [],
content: 'Hello Nostr!'
}
const signedEvent = await window.nostr.signEvent(unsignedEvent)
// Returns complete event with id, pubkey, and sigOptional Methods
NIP-04 Encryption (Deprecated)
Direct messages using older encryption standard:
javascript
// Encrypt
const ciphertext = await window.nostr.nip04.encrypt(recipientPubkey, 'secret message')
// Decrypt
const plaintext = await window.nostr.nip04.decrypt(senderPubkey, ciphertext)NIP-44 Encryption
Newer, more secure encryption standard:
javascript
// Encrypt
const ciphertext = await window.nostr.nip44.encrypt(recipientPubkey, 'secret message')
// Decrypt
const plaintext = await window.nostr.nip44.decrypt(senderPubkey, ciphertext)Detection
Check if a NIP-07 extension is available:
javascript
function hasNostrExtension() {
return typeof window.nostr !== 'undefined'
}
// With timeout for extensions that load slowly
async function waitForNostr(timeout = 1000) {
return new Promise((resolve) => {
if (window.nostr) {
resolve(true)
return
}
const interval = setInterval(() => {
if (window.nostr) {
clearInterval(interval)
resolve(true)
}
}, 100)
setTimeout(() => {
clearInterval(interval)
resolve(false)
}, timeout)
})
}Usage in ZapTracker
Connection Flow
javascript
async function connect() {
// Check for extension
if (!window.nostr) {
throw new Error('No NIP-07 extension found')
}
// Get public key
const pubkey = await window.nostr.getPublicKey()
// Store and use
return pubkey
}Signing Events
javascript
async function publishNote(content) {
const event = {
created_at: Math.floor(Date.now() / 1000),
kind: 1,
tags: [],
content
}
// Sign via extension
const signedEvent = await window.nostr.signEvent(event)
// Publish to relays
await publishToRelays(signedEvent)
return signedEvent
}Compatible Extensions
Alby
- Full NIP-07 support
- Built-in Lightning wallet
- getalby.com
nos2x
- Lightweight signer
- Chrome/Firefox
- github.com/fiatjaf/nos2x
Flamingo
- Feature-rich
- Key management
- getflamingo.org
Extension Recommendations
For extension authors (from NIP-07 spec):
Load Timing
Ensure window.nostr is available when pages load:
json
// manifest.json
{
"content_scripts": [{
"run_at": "document_end"
}]
}User Experience
- Clear permission prompts
- Remember user choices
- Allow revoking permissions
Error Handling
javascript
async function safeSign(event) {
try {
return await window.nostr.signEvent(event)
} catch (error) {
if (error.message?.includes('rejected')) {
// User rejected the signature
throw new Error('Signature rejected by user')
}
if (error.message?.includes('locked')) {
// Extension is locked
throw new Error('Please unlock your Nostr extension')
}
throw error
}
}Security Considerations
Extension Trust
- Only install extensions from trusted sources
- Review permissions requested
- Keep extensions updated
Website Trust
- Extensions should warn about suspicious sites
- Users should verify site authenticity
- Be cautious with signature requests
Key Management
- Private keys stay in extension
- Websites never see nsec
- User controls all signing