Nostr DM Channel Plugin for OpenClaw
Install and configure the @openclaw/nostr plugin so OpenClaw can receive and reply to NIP-04 encrypted direct messages through Nostr relays. Covers setup, config keys, and non-interactive installs for one account per gateway.
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; DMs only.
Install
openclaw plugins install @openclaw/nostr
To follow the current official release tag, use the bare package spec. Only pin an exact version when a reproducible install is needed.
From a local checkout (dev workflows):
openclaw plugins install --link <path-to-local-nostr-plugin>
Installing or enabling the plugin applies it to a running Gateway; check the application result. Once the plugin is installed, onboarding (openclaw onboard) and openclaw channels add surface Nostr from 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"
With --use-env, keep NOSTR_PRIVATE_KEY in the environment rather than storing the key in config (default account only).
Quick setup
- Generate a Nostr keypair (if needed):
# Using nak
nak key generate
- Add to config:
{
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 refs allowed |
relays | string[] | ['wss://relay.damus.io', 'wss://nos.lol'] | Relay URLs (WebSocket) |
dmPolicy | string | pairing | DM access policy |
allowFrom | string[] | [] | Allowed sender pubkeys |
enabled | boolean | true | Enable/disable channel |
name | string | - | Display name |
profile | object | - | NIP-01 profile metadata |
Profile metadata
Profile data gets published as a NIP-01 kind:0 event. You can manage it either from the Control UI (Channels -> Nostr -> Profile) or by setting it directly in 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 preserves local overrides.
Access control
DM policies
- pairing (default): unknown senders get a pairing code.
- allowlist: only pubkeys in
allowFromcan DM. - open: public inbound DMs (requires
allowFrom: ["*"]). - disabled: ignore inbound DMs.
Enforcement notes:
- Inbound event signatures are checked before sender policy and NIP-04 decryption, so forged events get rejected early.
- Pairing replies go out without decrypting or processing the original DM body.
- Inbound DMs are rate-limited (globally and per sender), and oversized payloads are dropped before decrypt.
Allowlist example
{
channels: {
nostr: {
privateKey: "${NOSTR_PRIVATE_KEY}",
dmPolicy: "allowlist",
allowFrom: ["npub1abc...", "npub1xyz..."],
},
},
}
Key formats
Accepted formats:
- Private key:
nsec...or 64-char hex - 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 (latency, duplication).
- Paid relays can improve reliability.
- Local relays are fine for testing (
ws://localhost:7777).
Protocol support
| NIP | Status | Description |
|---|---|---|
| NIP-01 | Supported | Basic event format + 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; convert to npub in your client if needed). - Open a Nostr client (Amethyst, Damus, etc.).
- DM the bot pubkey.
- Verify the response.
Troubleshooting
Not receiving messages
- Make sure the private key is valid.
- Relay URLs must be reachable and should use
wss://(ws://for local). - Verify that
enableddiffers fromfalse. - Inspect gateway logs for errors connecting to relays.
Not sending responses
- Confirm the relay permits writes.
- Check that outbound connectivity works.
- Keep an eye out for relay rate limits.
Duplicate responses
- This is expected when several relays are in use.
- Event ID deduplication means only the first delivery triggers a response.
Security
- Private keys must never be committed.
- Store keys in environment variables.
- For production bots, consider
allowlist. - Signature verification happens before sender policy, and sender policy runs before decryption, so forged events get rejected early and unknown senders cannot force full crypto work.
Limitations (MVP)
- Only direct messages are supported (no group chats).
- Media attachments are not supported.
- NIP-04 only (NIP-17 gift-wrap is planned).
Related
- Channels Overview, every supported channel
- Pairing, DM authentication and the pairing flow
- Groups, group chat behavior and mention gating
- Channel routing, how messages are routed to sessions
- Security, access model and hardening