Nostr DM Channel Plugin for OpenClaw via NIP-04 Encrypted Messages
This page covers the Nostr channel plugin for OpenClaw, enabling encrypted DM communication through Nostr relays. It provides installation, configuration, and setup instructions for developers integrating Nostr DMs.
Read this when
- You want OpenClaw to receive DMs via Nostr
- You're setting up decentralized messaging
Nostr is a downloadable channel plugin (@openclaw/nostr) that enables OpenClaw to receive and reply to NIP-04 encrypted direct messages through Nostr relays. One account per gateway; only DMs are supported.
Install
openclaw plugins install @openclaw/nostr
Use the bare package spec to track the latest official release tag. Pin a specific version only when you need reproducible builds.
From a local checkout (development workflows):
openclaw plugins install --link <path-to-local-nostr-plugin>
Restart the gateway after installing or enabling plugins. Once the plugin is installed, openclaw onboard and openclaw channels add expose Nostr in the shared channel catalog.
Non-interactive setup
openclaw channels add --channel nostr --private-key "$NOSTR_PRIVATE_KEY"
openclaw channels add --channel nostr --private-key "$NOSTR_PRIVATE_KEY" --relay-urls "wss://relay.damus.io,wss://relay.primal.net"
Use --use-env to keep NOSTR_PRIVATE_KEY in the environment rather than storing the key in configuration (applies to the default account only).
Quick setup
- Generate a Nostr keypair if you do not already have one:
# Using nak
nak key generate
- Add it to the configuration:
{
channels: {
nostr: {
privateKey: "${NOSTR_PRIVATE_KEY}",
},
},
}
- Export the key:
export NOSTR_PRIVATE_KEY="nsec1..."
- Restart the gateway.
Configuration reference
| Key | Type | Default | Description |
|---|---|---|---|
privateKey | string | required | Private key in nsec or hex format; secret references allowed |
relays | string[] | ['wss://relay.damus.io', 'wss://nos.lol'] | Relay WebSocket URLs |
dmPolicy | string | pairing | Policy for DM access |
allowFrom | string[] | [] | Public keys of allowed senders |
enabled | boolean | true | Turn the channel on or off |
name | string | - | Name shown in the interface |
profile | object | - | NIP-01 profile metadata |
Profile metadata
Profile data is published as a NIP-01 kind:0 event. You can manage it through the Control UI (Channels -> Nostr -> Profile) or define it directly in the config.
Example:
{
channels: {
nostr: {
privateKey: "${NOSTR_PRIVATE_KEY}",
profile: {
name: "openclaw",
displayName: "OpenClaw",
about: "Personal assistant DM bot",
picture: "https://example.com/avatar.png",
banner: "https://example.com/banner.png",
website: "https://example.com",
nip05: "openclaw@example.com",
lud16: "openclaw@example.com",
},
},
},
}
Notes:
- Profile URLs must use
https://. - Importing from relays merges fields and keeps local overrides intact.
Access control
DM policies
- pairing (default): unknown senders receive a pairing code.
- allowlist: only pubkeys in
allowFromcan send DMs. - open: anyone can send DMs (requires
allowFrom: ["*"]). - disabled: inbound DMs are ignored.
Enforcement notes:
- Inbound event signatures are verified before the sender policy and NIP-04 decryption are applied, so forged events are rejected early.
- Pairing replies are sent without decrypting or processing the original DM body.
- Inbound DMs are rate limited globally and per sender, and oversized payloads are discarded before decryption.
Allowlist example
{
channels: {
nostr: {
privateKey: "${NOSTR_PRIVATE_KEY}",
dmPolicy: "allowlist",
allowFrom: ["npub1abc...", "npub1xyz..."],
},
},
}
Key formats
Accepted formats:
- Private key:
nsec...or a 64-character hex string - Pubkeys (
allowFrom):npub...or hex
Relays
Defaults: relay.damus.io and nos.lol.
{
channels: {
nostr: {
privateKey: "${NOSTR_PRIVATE_KEY}",
relays: ["wss://relay.damus.io", "wss://relay.primal.net", "wss://nostr.wine"],
},
},
}
Tips:
- Use 2-3 relays for redundancy.
- Avoid too many relays to reduce latency and duplication.
- Paid relays can improve reliability.
- Local relays work fine for testing (
ws://localhost:7777).
Protocol support
| NIP | Status | Description |
|---|---|---|
| NIP-01 | Supported | Basic event format and profile metadata |
| NIP-04 | Supported | Encrypted DMs (kind:4) |
| NIP-17 | Planned | Gift-wrapped DMs |
| NIP-44 | Planned | Versioned encryption |
Testing
Local relay
# Start strfry
docker run -p 7777:7777 ghcr.io/hoytech/strfry
{
channels: {
nostr: {
privateKey: "${NOSTR_PRIVATE_KEY}",
relays: ["ws://localhost:7777"],
},
},
}
Manual test
- Note the bot pubkey from gateway logs or
openclaw channels status(hex format; convert to npub in your client if needed). - Open a Nostr client such as Amethyst or Damus.
- Send a DM to the bot pubkey.
- Check that you receive a response.
Troubleshooting
Not receiving messages
- Confirm the private key is valid.
- Make sure relay URLs are reachable and use
wss://(orws://for local). - Verify that
enabledis not set tofalse. - Review gateway logs for relay connection errors.
Not sending responses
- Confirm the relay allows writes.
- Ensure outbound connectivity is working.
- Keep an eye on relay rate limiting.
Duplicate responses
- Normal behavior when several relays are in use.
- Duplicate messages are removed based on event ID; only the initial delivery causes a response.
Security
- Private keys must never be committed.
- Store keys using environment variables.
- For production bots,
allowlistis recommended. - Signatures are validated before sender policy, and sender policy is checked before decryption, so fake events are turned away early and unknown senders cannot force full cryptographic processing.
Limitations (MVP)
- Only direct messages are supported, no group chats.
- Media attachments are not available.
- Only NIP-04 is used; NIP-17 gift-wrap is planned for the future.
Related
- Channels Overview, a summary of every supported channel
- Pairing, how DM authentication and pairing work
- Groups, group chat behavior and mention gating
- Channel Routing, session routing for messages
- Security, the access model and hardening measures