Skip to main content
Glama

nostr-auth-agents

Nostr sign-in for LLM coding agents — auth-only, no wallet, no browser extension, no relay account.

An agent (Claude Code, OpenCode, OpenClaw, Codex, Cursor, ...) is handed a "Sign in with Nostr" challenge or event template. This repo signs it with a secp256k1 (BIP-340 schnorr) key derived from a local master secret, and optionally submits the signature to the service callback.

Zero npm runtime dependencies: pure BigInt secp256k1/schnorr + node:crypto. Boots from a clean clone.

Methods

Method

Status

Notes

NIP-07 sign-in (challenge event signing)

✅ v1.0.0

Mirrors the LNURL-auth UX

NIP-98 HTTP Auth (Authorization: Nostr <event>)

🔜 planned

next

NIP-42 relay AUTH (websocket)

🔜 planned

NIP-05 identifier resolution

🔜 planned

Usage

# Sign a classic kind-22242 sign-in challenge and submit it
node nostr_auth.js nip07 --challenge "<hex>" \
  --relay "wss://relay.example.com" \
  --domain example.com \
  --callback "https://site.example.com/verify"

# Dry-run: sign but don't submit (inspect first)
node nostr_auth.js nip07 --challenge "<hex>" --dry-run --json

# Sign an arbitrary event template (what window.nostr.signEvent would do)
node nostr_auth.js nip07 '{"kind":22242,"tags":[["challenge","<hex>"]],"content":""}'

# Print the derived identity (hex pubkey + npub)
node nostr_auth.js nip07 pubkey --domain example.com

Progress logs go to stderr; results (JSON) go to stdout. Exit codes: 0 accepted, 1 client error, 2 usage, 3 server ERROR, 4 non-JSON callback response.

Key management

  • First run generates a 32-byte master secret at ~/.config/nostr-auth/master.key (mode 0600).

  • Per-service identity: linkingPriv = HMAC-SHA256(master, serviceDomain). Same domain → same npub; different domains → different npubs (privacy).

  • --single-key shares one identity across all services.

  • --generate overwrites the master secret. Override path with --keyfile/--keyout or the NOSTR_AUTH_KEYFILE env var.

Protocol (NIP-07 style)

  1. Challenge → event (kind 22242 by default) with challenge and optional relay tags.

  2. NIP-01 id: sha256(JSON.stringify([0, pubkey, created_at, kind, tags, content])).

  3. BIP-340 schnorr sign the raw 32-byte id → 64-byte hex sig.

  4. POST {"event": {...}} to the callback → {"status":"OK"} / {"status":"ERROR","reason":"..."}.

See SKILL.md for the full agent-facing docs.

MCP server

Zero-dependency stdio MCP server (mcp/server.js) exposing nostr_nip07_sign / nostr_nip07_pubkey tools:

{
  "mcpServers": {
    "nostr-auth": { "command": "node", "args": ["mcp/server.js"] }
  }
}

Plugin manifests for Claude Code (.claude-plugin/), Codex, Cursor, and skills.sh discoverability (skills.sh.json) are included. See PUBLISHING.md for distribution status per platform.

Install as an agent skill (per platform)

  • Claude Code / any MCP client: drop the repo in your skills dir, use SKILL.md + .mcp.json.

  • OpenClaw / ClawHub: skills/nostr-auth/ is an autonomous bundle (SKILL.md + scripts/nostr_auth.js, zero npm deps).

  • skills.sh (Vercel): npx skills add dyegolara/nostr-auth-agents

  • npm: npm i -g nostr-authnostr-auth nip07 ...

Self-test (offline, cost-free)

npm ci
npm test

A local mock "Sign in with Nostr" service (mock_server.js) validates the challenge → sign → submit → verify roundtrip, replay rejection, dry-run safety, per-domain identity stability, and the portable skill bundle.

Project layout

nostr_auth.js        CLI (bin "nostr-auth")
lib/                 key mgmt, BIP-340 schnorr, NIP-01 events, NIP-07 flow
mcp/server.js        zero-dep MCP server (stdio)
mock_server.js       local sign-in mock for tests
skills/nostr-auth/   portable OpenClaw/ClawHub skill bundle
contrib/anthropics/  educational skill variant for anthropics/skills
test/                vitest suite

License

MIT — see LICENSE. Auth-only: this project never holds funds, never publishes notes, and never connects to a relay on its own.