Nostr sign-in (NIP-07)
Use this skill for authentication only — signing sign-in events that prove ownership of a Nostr identity. It never publishes notes, connects to relays beyond the configured callback, or requires a wallet or browser extension.
Inputs
- A sign-in challenge from a "Sign in with Nostr" page or API — either an
event template (
kind,tags,content) or a challenge string. - Optional
--domainfor the service host (used for deterministic key derivation),--callbackURL to submit the signed event to. - Optional
--dry-runwhen the signed event must be inspected before submission.
Do not invent or alter challenges. Ask for a fresh challenge when the service reports it was already used.
Run
The bundled helper uses only Node.js built-ins and lives at:
<skill_dir>/scripts/nostr_auth.js
Inspect the identity and signature without authenticating:
node <skill_dir>/scripts/nostr_auth.js pubkey --domain example.com
node <skill_dir>/scripts/nostr_auth.js --challenge "<hex>" --relay "wss://..." --dry-run --json
After confirming the service and callback are expected, submit:
node <skill_dir>/scripts/nostr_auth.js --challenge "<hex>" --relay "wss://..." --callback "https://example.com/verify" --json
Or sign a full event template:
node <skill_dir>/scripts/nostr_auth.js sign '{"kind":22242,"tags":[["challenge","<hex>"]],"content":""}' --domain example.com --callback https://example.com/verify --json
The helper requires Node.js 20.19 or newer. The only network request is the final callback POST.
Protocol
- Derive a per-service secp256k1 private key:
HMAC-SHA256(master, domain), from the persisted master secret (~/.config/nostr-auth/master.key, mode0600). - Build the event (kind 22242 for challenges by default), compute the NIP-01
id
sha256(JSON.stringify([0, pubkey, created_at, kind, tags, content])). - Sign the raw 32-byte id with BIP-340 schnorr; store the 64-byte signature
as hex in
event.sig. - POST
{"event": <signed>}to the callback. Interpret{"status":"OK"}as accepted; a fresh challenge is needed after{"status":"ERROR"}.
Identity and safety
- Keep the master secret local (
0600); expose the nsec to nobody. - Per-domain derivation keeps unrelated services from correlating identity.
- Inspect the callback URL and signed event in
--dry-runbefore submitting. - The
npubderived here is an agent identity — it is not the same key as a user's browser extension unless the master secret comes from it.
Failure handling
Invalid hex/odd length— malformed key or challenge input.Callback returned non-JSON or HTTP <n>— the service is down or the URL is wrong; verify--callback.{"status":"ERROR"}— challenge may already be used; request a fresh one.
Self-test (offline, cost-free)
cd <skill_dir>
npm test
The local mock service validates the full sign → submit → verify roundtrip.