Skip to content

NIP-07 Integration

Nostr

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 sig

Optional 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

Flamingo

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