Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nullclaw/nullclaw/llms.txt

Use this file to discover all available pages before exploring further.

NullClaw speaks Nostr natively via NIP-17 (gift-wrapped private DMs) and NIP-04 (legacy encrypted DMs), enabling decentralized, censorship-resistant AI assistant communication.

Prerequisites

  • nak CLI tool installed and in $PATH
  • Your Nostr public key (npub or hex format)
  • Access to Nostr relays

Installation

Install nak

go install github.com/fiatjaf/nak@latest
Verify installation:
nak --version

Quick Setup

1

Run Onboarding Wizard

nullclaw onboard --interactive
Select Nostr when prompted for channels.
2

Generate or Import Key

Wizard will offer to generate a new keypair or import existing private key.
3

Provide Owner Pubkey

Enter your personal Nostr pubkey (npub or hex) - this is always allowed through DM policy.
4

Configure Relays

Add relays for listening and publishing events.
The wizard encrypts your private key with ChaCha20-Poly1305 and stores it as enc2:... in config.

Configuration

Basic Setup

Edit ~/.nullclaw/config.json:
config.json
{
  "channels": {
    "nostr": {
      "private_key": "enc2:ChaCha20Poly1305-encrypted-hex-key",
      "owner_pubkey": "your-hex-pubkey-or-npub",
      "relays": [
        "wss://relay.damus.io",
        "wss://nos.lol",
        "wss://relay.nostr.band"
      ],
      "dm_allowed_pubkeys": ["*"]
    }
  }
}

Complete Configuration

config.json
{
  "channels": {
    "nostr": {
      "private_key": "enc2:ChaCha20Poly1305-encrypted-hex-key",
      "owner_pubkey": "npub1...",
      "relays": [
        "wss://relay.damus.io",
        "wss://nos.lol",
        "wss://relay.nostr.band"
      ],
      "dm_allowed_pubkeys": ["*"],
      "display_name": "NullClaw AI",
      "about": "AI assistant running on Nostr via NullClaw",
      "nip05": "nullclaw@yourdomain.com",
      "lnurl": "lnurl1..."
    }
  }
}

Configuration Options

private_key (required)

Your bot’s Nostr private key (nsec or hex), encrypted at rest:
"private_key": "enc2:ChaCha20Poly1305-encrypted-hex-key"
Private keys are encrypted with ChaCha20-Poly1305 and only decrypted into memory while the channel is running. Keys are zeroed on channel stop.
# Generate new key with nak
nak key generate

# Output format:
# nsec1... (private key)
# npub1... (public key)
Store the nsec1... value in config. NullClaw will encrypt it on first run if secrets.encrypt is enabled.

owner_pubkey (required)

Your personal Nostr public key (always allowed regardless of dm_allowed_pubkeys):
"owner_pubkey": "npub1qpq..." // or hex format
This is YOUR pubkey, not the bot’s. The bot will always accept DMs from this pubkey.

relays (required)

Nostr relays for publishing and listening:
"relays": [
  "wss://relay.damus.io",
  "wss://nos.lol",
  "wss://relay.nostr.band",
  "wss://nostr.wine",
  "wss://relay.snort.social"
]

dm_allowed_pubkeys

Pubkeys allowed to DM the bot:
"dm_allowed_pubkeys": ["*"]
Anyone can message the bot.
owner_pubkey is ALWAYS allowed, regardless of this setting.

Profile Fields (Optional)

Configure bot’s Nostr profile metadata:
"display_name": "NullClaw AI",
"about": "AI assistant powered by NullClaw - send me a DM!",
"nip05": "bot@yourdomain.com",
"lnurl": "lnurl1...",
"picture": "https://yourdomain.com/avatar.png"

How It Works

NIP-17 Gift Wrapped DMs

NullClaw’s primary DM protocol:
  1. Listening: Bot subscribes to kind:10050 inbox relays
  2. Receiving: Unwraps gift-wrapped DMs using nak gift unwrap
  3. Deduplication: Tracks rumor IDs to prevent duplicate deliveries across relays
  4. Sending: Wraps replies with nak gift wrap and publishes to sender’s relays

NIP-04 Legacy DMs

Backward compatibility for older clients:
  1. Receiving: Listens for kind:4 events
  2. Decrypting: Uses nak decrypt
  3. Sending: Mirrors sender’s protocol (NIP-04 reply to NIP-04 sender)

Protocol Mirroring

Bot automatically remembers which protocol each sender used and replies using the same protocol.

Startup Flow

1

Decrypt Private Key

Bot decrypts enc2:... private key into memory.
2

Announce Inbox

Publishes kind:10050 event announcing DM inbox relays.
3

Start Listener

Spawns nak req --stream subprocess for event listening.
4

Process Events

Reader thread processes incoming NIP-17 and NIP-04 events.

Running the Channel

Gateway Mode

nullclaw gateway
Starts all configured channels including Nostr.

Standalone Channel

nullclaw channel start nostr

Check Status

nullclaw channel status
Shows:
  • Connection status
  • Active relays
  • Message count
  • Last activity

Security

Private keys are encrypted with ChaCha20-Poly1305:
{
  "secrets": {
    "encrypt": true
  }
}
Keys are only decrypted into memory while channel is running.
Restrict who can message your bot:
"dm_allowed_pubkeys": ["npub1...", "npub2..."]
Empty array = owner only.
On channel stop, private keys are zeroed in memory before free:
@memset(sec, 0);
allocator.free(sec);
Prevents replay attacks by tracking seen rumor IDs with timestamps.

Troubleshooting

nak command not found

# Check if nak is installed
which nak

# Install with Go
go install github.com/fiatjaf/nak@latest

# Add Go bin to PATH
export PATH="$PATH:$(go env GOPATH)/bin"

Bot Not Receiving Messages

1

Check Relays

Verify sender and bot share at least one relay.
2

Check Allowlist

Ensure sender’s pubkey is in dm_allowed_pubkeys or use ["*"].
3

Check Logs

Run nullclaw gateway in foreground to see event processing.
4

Test with Owner

Send DM from owner_pubkey (always allowed).

Duplicate Messages

If you receive duplicate responses:
  • Check if same rumor is delivered via multiple relays
  • NullClaw should deduplicate automatically
  • Check logs for “duplicate rumor” messages

Private Key Decryption Failed

# Re-encrypt secrets
nullclaw onboard --channels-only

# Or manually edit config with plaintext nsec, NullClaw will encrypt on start

Relay Connection Issues

# Test relay connectivity with nak
nak req -k 1 --limit 1 wss://relay.damus.io

# Try different relays
# Remove slow/unreliable relays from config

Advanced Usage

External Signing (bunker://)

Use external signer instead of embedded private key:
config.json
{
  "channels": {
    "nostr": {
      "private_key": "bunker://pubkey?relay=wss://relay.example.com",
      "owner_pubkey": "npub1...",
      "relays": ["wss://relay.damus.io"]
    }
  }
}
NullClaw passes bunker:// URIs directly to nak --sec.

Multiple Nostr Accounts

Currently, Nostr channel doesn’t support multiple accounts (unlike Telegram/Discord). To run multiple bots:
  1. Run separate NullClaw instances
  2. Use different config directories
  3. Different ports for each instance

Kind:10050 Inbox Relays

Bot automatically publishes kind:10050 (inbox relays) on startup, announcing where it listens for gift-wrapped DMs. Clients following NIP-17 will look up your inbox relays and publish there.

Protocol Details

Event Kinds

KindProtocolDescription
4NIP-04Legacy encrypted DMs
14NIP-17Rumor (inner event)
1059NIP-17Gift wrap (outer event)
10050NIP-17Inbox relay list

Timestamps

Bot discards events with created_at before channel start time, preventing old messages from being processed on restart.

Session Keys

Session keys are derived from:
  • Sender pubkey
  • Channel account ID
  • Protocol type (nostr)
Format: nostr:main:<sender_pubkey>

Performance

  • Startup: <2ms (excluding nak subprocess spawn)
  • Message latency: Depends on relay response time (typically 100-500ms)
  • Memory: ~500KB per active session
  • Relay connections: Persistent WebSocket to each relay

Next Steps

Sandboxing

Configure security sandboxing

Memory Backends

Set up vector memory