Files
adolf/docs/channels/nostr.md
alvis bedb527145
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Vendor OpenClaw source as Adolf fork baseline
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11),
free to diverge. Tree copied sans upstream .git; upstream remote added for
future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19.
Preserves docs/ARCHITECTURE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
2026-07-05 09:36:54 +00:00

6.9 KiB

summary, read_when, title
summary read_when title
Nostr DM channel via NIP-04 encrypted messages
You want OpenClaw to receive DMs via Nostr
You're setting up decentralized messaging
Nostr

Nostr is a downloadable channel plugin (@openclaw/nostr) that lets OpenClaw receive and answer NIP-04 encrypted direct messages over Nostr relays. One account per gateway; DMs only.

Install

openclaw plugins install @openclaw/nostr

Use the bare package spec to follow the current official release tag. Pin an exact version only when you need a reproducible install.

From a local checkout (dev workflows):

openclaw plugins install --link <path-to-local-nostr-plugin>

Restart the gateway after installing or enabling plugins. Onboarding (openclaw onboard) and openclaw channels add surface Nostr from the shared channel catalog once the plugin is installed.

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 instead of storing the key in config (default account only).

Quick setup

  1. Generate a Nostr keypair (if needed):
# Using nak
nak key generate
  1. Add to config:
{
  channels: {
    nostr: {
      privateKey: "${NOSTR_PRIVATE_KEY}",
    },
  },
}
  1. Export the key:
export NOSTR_PRIVATE_KEY="nsec1..."
  1. 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 is published as a NIP-01 kind:0 event. You can manage it from the Control UI (Channels -> Nostr -> Profile) or set 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 allowFrom can DM.
  • open: public inbound DMs (requires allowFrom: ["*"]).
  • disabled: ignore inbound DMs.

Enforcement notes:

  • Inbound event signatures are verified before sender policy and NIP-04 decryption, 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 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

  1. Note the bot pubkey from gateway logs or openclaw channels status (hex; convert to npub in your client if needed).
  2. Open a Nostr client (Amethyst, Damus, etc.).
  3. DM the bot pubkey.
  4. Verify the response.

Troubleshooting

Not receiving messages

  • Verify the private key is valid.
  • Ensure relay URLs are reachable and use wss:// (or ws:// for local).
  • Confirm enabled is not false.
  • Check gateway logs for relay connection errors.

Not sending responses

  • Check relay accepts writes.
  • Verify outbound connectivity.
  • Watch for relay rate limits.

Duplicate responses

  • Expected when using multiple relays.
  • Messages are deduplicated by event ID; only the first delivery triggers a response.

Security

  • Never commit private keys.
  • Use environment variables for keys.
  • Consider allowlist for production bots.
  • Signatures are verified before sender policy, and sender policy is enforced before decrypt, so forged events are rejected early and unknown senders cannot force full crypto work.

Limitations (MVP)

  • Direct messages only (no group chats).
  • No media attachments.
  • NIP-04 only (NIP-17 gift-wrap planned).