Nostr WoT

Documentation

Everything you need to integrate Web of Trust into your application.

Extension API

The extension exposes window.nostr for identity, event signing, and NIP-04/NIP-44 message encryption.

Install the extension, select an account, and connect your site when prompted. Signing and encryption require a signing-capable account and may prompt you to unlock the vault. Reading a known public key does not require unlocking it.

Setup

Check for the method you need before calling it. An injected provider does not mean the site is connected or a request has been approved.

javascript
// Feature detection
function hasNostr() {
  return typeof window !== "undefined" &&
         typeof window.nostr?.getPublicKey === "function";
}

// Wait for the extension to load
async function waitForNostr(timeout = 3000) {
  const start = Date.now();
  while (!hasNostr() && Date.now() - start < timeout) {
    await new Promise(r => setTimeout(r, 100));
  }
  return hasNostr();
}

NIP-07 Signer API

The extension implements the NIP-07 signer API exposed through window.nostr.

getPublicKey()

Returns the active account's hex-encoded public key. Requires site connection and identity access.

Returns

Promise<string>

Example

javascript
const pubkey = await window.nostr.getPublicKey();
console.log(pubkey); // "3bf0c63f..."

signEvent(event)

Signs the event and adds id, pubkey and sig. Supply created_at yourself; the signer preserves your timestamp. If you supply pubkey, it must match the active account. Signing does not publish the event.

Parameters

NameTypeDescription
eventUnsignedEventEvent containing kind, content, tags and created_at (Unix time in seconds)

Returns

Promise<SignedEvent>

Example

javascript
const signed = await window.nostr.signEvent({
  kind: 1,
  content: "Hello Nostr!",
  tags: [],
  created_at: Math.floor(Date.now() / 1000),
});
console.log(signed.sig); // schnorr signature

nip04.encrypt(pubkey, plaintext)

Encrypts a message using NIP-04, the legacy direct-message encryption format.

Parameters

NameTypeDescription
pubkeystringRecipient's 64-character hexadecimal public key
plaintextstringMessage to encrypt

Returns

Promise<string>

Example

javascript
const encrypted = await window.nostr.nip04.encrypt(
  recipientPubkey,
  "Secret message"
);

nip04.decrypt(pubkey, ciphertext)

Decrypts a NIP-04 message.

Parameters

NameTypeDescription
pubkeystringSender's 64-character hexadecimal public key
ciphertextstringEncrypted message string

Returns

Promise<string>

Example

javascript
const plaintext = await window.nostr.nip04.decrypt(
  senderPubkey,
  ciphertext
);
console.log(plaintext); // "Secret message"

nip44.encrypt(pubkey, plaintext)

Encrypts a message using NIP-44. This example uses the standard two-argument call.

Parameters

NameTypeDescription
pubkeystringRecipient's 64-character hexadecimal public key
plaintextstringMessage to encrypt

Returns

Promise<string>

Example

javascript
const encrypted = await window.nostr.nip44.encrypt(
  recipientPubkey,
  "Secret message"
);

nip44.decrypt(pubkey, ciphertext)

Decrypts a NIP-44 message.

Parameters

NameTypeDescription
pubkeystringSender's 64-character hexadecimal public key
ciphertextstringEncrypted message string

Returns

Promise<string>

Example

javascript
const plaintext = await window.nostr.nip44.decrypt(
  senderPubkey,
  ciphertext
);
console.log(plaintext); // "Secret message"

getRelays()

Returns the relay URLs configured in the extension. Each entry has read: true and write: true. This method does not fetch the account's NIP-65 relay policies and may return an empty object.

Returns

Promise<Record<string, { read: boolean; write: boolean }>>

Example

javascript
const relays = await window.nostr.getRelays();

// {
//   "wss://relay.damus.io": { read: true, write: true },
//   "wss://nos.lol": { read: true, write: true }
// }

Connection, permissions and errors

Calls return Promises and can reject if the user declines, the site is disconnected, identity access is disabled, or the request times out. Signing and encryption are unavailable for read-only accounts.

An account switch can invalidate a pending request. Page calls time out after 120 seconds, including time spent waiting for connection, permission or unlock. Catch errors and let the user retry; error messages are not stable machine-readable codes.

javascript
async function signNote(content) {
  const provider = window.nostr;
  if (typeof provider?.getPublicKey !== "function" ||
      typeof provider?.signEvent !== "function") {
    return { ok: false, reason: "provider-unavailable" };
  }

  try {
    const pubkey = await provider.getPublicKey();
    if (!pubkey) return { ok: false, reason: "no-active-account" };

    const event = await provider.signEvent({
      pubkey,
      kind: 1,
      content,
      tags: [],
      created_at: Math.floor(Date.now() / 1000),
    });
    return { ok: true, event };
  } catch (error) {
    return { ok: false, reason: "request-failed", error };
  }
}

Use the SDK for follow-graph queries and the WoT Oracle API for public mute evidence. Mute evidence is separate from follow distance; it does not produce a combined trust score. The extension provides identity and signing.