Skip to content

Add Nostr verify identity component - #85

Open
alokdangre wants to merge 4 commits into
saiy2k:mainfrom
alokdangre:relay-crawler
Open

Add Nostr verify identity component#85
alokdangre wants to merge 4 commits into
saiy2k:mainfrom
alokdangre:relay-crawler

Conversation

@alokdangre

@alokdangre alokdangre commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

This PR adds a new <nostr-verify-identity> web component for NIP-39 identity verification, plus a relay directory crawler for collecting and scoring identity claims from Nostr relays.

The new component guides a user through:

  • connecting a Nostr signer
  • generating the canonical proof text
  • posting the proof on X/Twitter
  • verifying the proof tweet client-side
  • publishing the verified i tag to kind:10011
  • mirroring the identity tag into kind:0 when a profile already exists

It also adds Storybook coverage, unit tests, and package/export wiring so the component can be consumed as part of the library.

What changed

  • Added src/nostr-verify-identity/ component implementation
  • Added verify helper utilities and unit tests
  • Added Storybook stories for the new component
  • Added component exports and Vite build entry wiring

Validation

  • Added unit tests for verification helpers
  • Added crawler helper tests
  • Added Storybook story for manual review

Notes

  • Proof verification is client-side and uses Twitter/X oEmbed JSONP, so no backend or paid X API is required.
  • kind:10011 is treated as the canonical NIP-39 home, while kind:0 is mirrored only when a profile event already exists.

Summary by CodeRabbit

  • New Features
    • Added identity verification component enabling NIP-39 identity proofs. Users can now link their Nostr accounts to their X/Twitter handles through a streamlined workflow that generates proof text, verifies tweet authenticity, and publishes verified identities to Nostr relays.

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a <nostr-verify-identity> Shadow DOM web component that guides a user through NIP-39 Twitter/X identity proof creation and publishing (JSONP oEmbed verification → kind:10011/kind:0 Nostr event publishing). It also adds a standalone Node.js CLI script that crawls Nostr relays to build a verified X-handle–to–pubkey directory with WoT scoring and NIP-57 zap-support checks.

Changes

nostr-verify-identity Web Component

Layer / File(s) Summary
NIP-39 utility contracts and pure helpers
src/nostr-verify-identity/verify-utils.ts
Exports Platform type, OEmbedResult and VerifyResult interfaces, and pure functions: buildProofText, buildTweetIntentUrl, extractTweetId, buildIdentityTag.
JSONP tweet fetching, verification, and Nostr publishing
src/nostr-verify-identity/verify-utils.ts
Implements fetchProofTweet (JSONP script injection to publish.x.com/oembed with timeout/cleanup), verifyTwitterProof, getUserPubkey (NIP-07 wrapper), mergeIdentityTag, publishIdentity (kind:10011 + optional kind:0 mirror), and internal signAndPublish/HTML-strip helpers.
Shadow DOM rendering and styles
src/nostr-verify-identity/render.ts, src/nostr-verify-identity/style.ts
renderVerifyIdentity produces HTML strings for connect, proof/verify, and done steps with error banner, spinner, shortened npub, and published-kinds list; getVerifyIdentityStyles() returns a full CSS template for all UI states.
NostrVerifyIdentity custom element class
src/nostr-verify-identity/nostr-verify.ts
Implements the component class with platform/handle observed attributes, async handleConnect/handleVerify flows, nc:verified event dispatch, clipboard copy, reset, delegated click wiring, and renderContent() updating shadowRoot.innerHTML. Registers the custom element.
Build config, package exports, and library re-exports
vite.config.esm.ts, vite.config.umd.ts, package.json, src/index.ts
Adds ESM Rollup entry for nostr-verify-identity, includes it in UMD default export reconstruction, exposes ./components/nostr-verify-identity in package exports, and re-exports NostrVerifyIdentity from src/index.ts.
Utility tests, Storybook stories, and spec
src/nostr-verify-identity/__tests__/verify-utils.test.ts, stories/nostr-verify-identity/NostrVerifyIdentity.stories.tsx, src/nostr-verify-identity/spec/spec.md
Vitest tests for all pure utility functions and mergeIdentityTag; three Storybook stories (Default, PinnedHandle, DarkTheme); full NIP-39 component specification with security model and out-of-scope items.

Relay Directory Crawler Script

Layer / File(s) Summary
CLI scaffolding and WebSocket relay querying
scripts/relay-directory-crawler.mjs
CLI argument parsing, default constants, Twitter regex constants/reserved paths, help printer, queryRelay with subscription/EOSE/timeout termination, and queryPool deduplicating events across relays.
Event normalization and directory input extraction
scripts/relay-directory-crawler.mjs
Latest-replaceable-event selection, signed-event validation, normalizeTwitterHandle, extractTweetId, safe JSON parsing, hex-to-npub, extractDirectoryInputs (kind:10011 i tags → proof candidates; kind:0 metadata fields → claimed entries), metadata merging, and sort comparators.
Tweet fetching, proof verification, zap checks, and WoT scoring
scripts/relay-directory-crawler.mjs
Syndication token generation and multi-strategy fetchTweet (X API → syndication → oEmbed), stripHtml, verifyCandidate, checkZapSupport via lnurlp discovery, lightningAddressToLnurlp, hex-pubkey validation, and computeWotScores (kind:3/1984/30382 → bounded 0–100 score).
End-to-end crawler pipeline and output writing
scripts/relay-directory-crawler.mjs, package.json
runCrawler orchestrates relay queries → extraction → optional tweet verification → metadata refresh → zap/WoT enrichment → JSON output; adds relay-result summarization, directory sorting, file writing, console summary, isMain invocation, module exports, and crawl:directory npm script.
Crawler helper tests
scripts/relay-directory-crawler.test.mjs
Vitest suite covering normalizeTwitterHandle, extractTweetId, extractDirectoryInputs, lightningAddressToLnurlp, and computeWotScores with identity-status and auto-zap policy invariants.

Sequence Diagram

sequenceDiagram
  actor User
  participant NostrVerifyIdentity as nostr-verify-identity
  participant NIP07 as NIP-07 Signer
  participant XoEmbed as publish.x.com/oembed (JSONP)
  participant NDK as NDK/Relay

  rect rgba(100, 149, 237, 0.5)
    Note over User,NIP07: Connect step
    User->>NostrVerifyIdentity: click "Connect Nostr"
    NostrVerifyIdentity->>NIP07: getUserPubkey()
    NIP07-->>NostrVerifyIdentity: pubkey → npub
    NostrVerifyIdentity-->>User: show proof text + intent URL (proof step)
  end

  rect rgba(144, 238, 144, 0.5)
    Note over User,XoEmbed: Verify step
    User->>NostrVerifyIdentity: paste tweet URL, click "Verify & publish"
    NostrVerifyIdentity->>XoEmbed: fetchProofTweet (JSONP)
    XoEmbed-->>NostrVerifyIdentity: OEmbedResult (author handle + tweet html)
    NostrVerifyIdentity->>NostrVerifyIdentity: verifyTwitterProof (handle match + npub in html)
  end

  rect rgba(255, 165, 0, 0.5)
    Note over NostrVerifyIdentity,NDK: Publish step
    NostrVerifyIdentity->>NIP07: sign kind:10011 event
    NIP07-->>NostrVerifyIdentity: signed event
    NostrVerifyIdentity->>NDK: publish kind:10011
    NostrVerifyIdentity->>NDK: publish kind:0 (if existing profile)
    NDK-->>NostrVerifyIdentity: published kinds[]
    NostrVerifyIdentity-->>User: dispatch nc:verified event (done step)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • saiy2k/nostr-components#13: Modifies the same vite.config.esm.ts multi-entry Rollup build plumbing and component library export wiring that this PR extends with the nostr-verify-identity entry.

Poem

🐇 Hoppity-hop through the Nostr relay,
A tweet holds the proof — "my npub's okay!"
The rabbit checks oEmbed, confirms with a nod,
Then publishes kind:10011 (a wink to the gods).
X handle and pubkey, now linked without fear —
The directory grows, and the bunny cheers! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the primary addition: a new Nostr verify identity component. It is concise, clear, and directly reflects the main change in the changeset.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (5)
scripts/relay-directory-crawler.mjs (1)

566-573: ⚖️ Poor tradeoff

Sequential tweet verification is a performance bottleneck.

With --max-proofs 250 (default) and --timeout-ms 12000 (default), worst-case verification time is ~50 minutes. Each verifyCandidate awaits sequentially.

Consider adding concurrency control for parallel verification:

Proposed approach using a concurrency limiter
// Helper for bounded concurrency
async function mapWithConcurrency(items, fn, concurrency = 10) {
  const results = [];
  const executing = new Set();
  for (const item of items) {
    const promise = fn(item).then((result) => {
      executing.delete(promise);
      return result;
    });
    executing.add(promise);
    results.push(promise);
    if (executing.size >= concurrency) {
      await Promise.race(executing);
    }
  }
  return Promise.all(results);
}

// Usage in runCrawler:
const verifiedOrRejected = await mapWithConcurrency(
  candidates.slice(0, proofLimit),
  (candidate) => verifyCandidate(candidate, args.timeoutMs),
  10 // concurrent requests
);

The same pattern could be applied to checkZapSupport at lines 597-599.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/relay-directory-crawler.mjs` around lines 566 - 573, The sequential
awaiting of verifyCandidate calls in the args.verifyTweets block creates a
significant performance bottleneck, potentially taking ~50 minutes with default
settings. Implement a helper function that manages bounded concurrency
(accepting an array of items, an async function to apply, and a concurrency
limit parameter), then replace the sequential for loop that pushes to
verifiedOrRejected with a call to this concurrency-controlled function, passing
the candidates slice, verifyCandidate, and an appropriate concurrency limit.
Additionally, apply the same concurrency pattern to the checkZapSupport
verification logic to parallelize those operations as well.
scripts/relay-directory-crawler.test.mjs (4)

15-27: ⚡ Quick win

Consider adding edge-case tests for robustness.

The current tests cover the happy path and common invalid cases. Consider adding tests for:

  • null, undefined, and empty string inputs
  • Handles with leading/trailing whitespace
  • Exactly 15 and 16 character handles (boundary testing)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/relay-directory-crawler.test.mjs` around lines 15 - 27, The test
suite for the normalizeTwitterHandle function lacks edge-case coverage that
would help ensure robustness. Add additional test cases within the describe
block to cover: null, undefined, and empty string inputs; handles with leading
and trailing whitespace; and boundary cases for exactly 15 and 16 character
handles. These tests should verify that normalizeTwitterHandle handles these
edge cases appropriately by either normalizing them correctly or returning null
as expected.

29-37: ⚡ Quick win

Consider adding edge-case tests for input validation.

Add tests for:

  • null, undefined, and empty string inputs
  • Malformed URLs without status path
  • Very long numeric strings (to verify regex doesn't match arbitrary numbers)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/relay-directory-crawler.test.mjs` around lines 29 - 37, The
extractTweetId test suite is missing edge-case tests that are important for
ensuring robust input validation. Add additional test cases to the existing it
block or create new test cases within the describe('extractTweetId') block to
cover: null, undefined, and empty string inputs (these should likely return
null), malformed URLs that lack a status path component (to ensure the regex
properly validates URL structure), and very long numeric strings that don't
match the actual tweet ID pattern (to verify the function doesn't incorrectly
match arbitrary long numbers). Each edge case should have an expect statement
validating the expected behavior of extractTweetId when given these problematic
inputs.

91-116: ⚡ Quick win

Consider expanding WoT scoring test coverage.

The current test validates invariant preservation with a single follow event. Consider adding tests for:

  • Multiple WoT signals (follows, reports, assertions)
  • Different identityStatus values (verified, claimed, failed)
  • Score computation edge cases (no signals, conflicting signals)
  • Different autoZapAllowed starting values

This would increase confidence in the scoring algorithm's correctness.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/relay-directory-crawler.test.mjs` around lines 91 - 116, The test for
computeWotScores only covers a single scenario with a follow event and claimed
identity status. Add additional test cases to the same describe block to cover:
(1) multiple WoT signals such as follows, reports, and assertions in a single
call to computeWotScores, (2) different identity status values (verified,
claimed, and failed) to ensure scoring works correctly for each, (3) edge cases
like calling computeWotScores with empty signals array and with conflicting
signals, and (4) different starting values for autoZapAllowed (both true and
false). Each test should verify that computeWotScores correctly computes wot
scores while handling these various scenarios appropriately.

83-89: ⚡ Quick win

Add tests for invalid lightning address formats.

The test suite only covers the happy path. Consider adding tests for error cases:

  • Invalid formats: 'no-at-sign', '@missing-user', 'missing-domain@'
  • Edge cases: null, undefined, empty string
  • Multiple @ symbols
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/relay-directory-crawler.test.mjs` around lines 83 - 89, The test
suite for the lightningAddressToLnurlp function currently only covers the happy
path with a valid email address. Add additional test cases within the same
describe block to cover error cases and edge cases including invalid formats
(no-at-sign, `@missing-user`, missing-domain@), null and undefined inputs, empty
strings, and addresses with multiple @ symbols. Each test case should verify
that the function handles these inputs appropriately, either by throwing an
error or returning a specific error value as documented by the function's
specification.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/relay-directory-crawler.mjs`:
- Around line 288-294: The syndicationToken function loses precision when
converting the tweetId parameter to Number, since Tweet IDs (Snowflake IDs) are
19-digit numbers that exceed JavaScript's Number.MAX_SAFE_INTEGER limit. Replace
the Number conversion with BigInt to preserve precision during the calculation.
Convert the tweetId to BigInt, perform the division operation using BigInt
arithmetic, then convert the result to a string for the subsequent replace
operation to generate the syndication token correctly for all tweet IDs.
- Around line 110-115: The native WebSocket global used at line 111 in the
constructor attempt is only available in Node.js 22.0.0+ by default (or Node.js
21+ with the experimental flag), but there is no documented version requirement,
which may cause ReferenceError on unsupported versions. To fix this, either add
"engines": { "node": ">=22.0.0" } to package.json to enforce the minimum version
requirement, document the Node.js version requirement in the help text or README
for the script, or add a fallback mechanism that imports the `ws` package when
the native WebSocket is not available to support broader Node.js version
compatibility.

In `@scripts/relay-directory-crawler.test.mjs`:
- Line 79: The test assertion on the expect statement does not guard against the
potential case where metadataByPubkey.get(PUBKEY) returns undefined, which would
cause a TypeError instead of a clear test failure. Add a guard assertion before
accessing the lud16 property to first verify that metadataByPubkey.get(PUBKEY)
returns a defined value, then check that the lud16 property equals the expected
value. This ensures that if the entry is missing from metadataByPubkey, the test
will fail with a clear assertion message rather than a cryptic TypeError.

In `@src/nostr-verify-identity/nostr-verify.ts`:
- Around line 115-123: The proof URL input is cleared when render() is called
during verification failures in the handleVerify method (around line 115-123)
and other locations (lines 132-138 and 166-168), forcing users to re-paste the
URL on retry. Add a class property to store the proof URL draft in component
state, capture and persist the input value to this state property whenever it's
read in handleVerify, and then restore the stored value back into the input
field's value attribute during render or template binding to rehydrate the
input. Apply this state persistence pattern consistently across all three
affected locations where verification or error handling triggers a re-render.

In `@src/nostr-verify-identity/render.ts`:
- Around line 86-89: The input field with name="proof-url" lacks proper
accessibility labeling and currently relies only on placeholder and context
text. Add an explicit `<label>` element with a `for` attribute that references a
corresponding `id` on the input element. Assign a unique id to the proof-url
input and create a label element that references this id with appropriate
descriptive text for users completing the form.

In `@src/nostr-verify-identity/verify-utils.ts`:
- Around line 151-169: The verification logic does not validate that the author
handle was successfully resolved when no specific handle is declared. Add a
check after the npub validation and before the final success return to reject
cases where oembed.handle is empty or falsy, ensuring that unresolved author
handles fail verification before returning ok: true.
- Around line 231-237: The kind:10011 event is published unconditionally via
signAndPublish without verifying that the fetchEvent call succeeded, which means
if the fetch fails or times out, the empty tags fallback from mergeIdentityTag
will overwrite existing identity tags. Add a safety check before the
signAndPublish call to verify that existing10011 was successfully retrieved
(similar to the pattern used for kind:0 with `if (existing0)`), or alternatively
refactor to use NDK's publishReplaceable() method with appropriate error
handling to safely handle replaceable event updates and prevent data loss from
failed or uncertain reads.

---

Nitpick comments:
In `@scripts/relay-directory-crawler.mjs`:
- Around line 566-573: The sequential awaiting of verifyCandidate calls in the
args.verifyTweets block creates a significant performance bottleneck,
potentially taking ~50 minutes with default settings. Implement a helper
function that manages bounded concurrency (accepting an array of items, an async
function to apply, and a concurrency limit parameter), then replace the
sequential for loop that pushes to verifiedOrRejected with a call to this
concurrency-controlled function, passing the candidates slice, verifyCandidate,
and an appropriate concurrency limit. Additionally, apply the same concurrency
pattern to the checkZapSupport verification logic to parallelize those
operations as well.

In `@scripts/relay-directory-crawler.test.mjs`:
- Around line 15-27: The test suite for the normalizeTwitterHandle function
lacks edge-case coverage that would help ensure robustness. Add additional test
cases within the describe block to cover: null, undefined, and empty string
inputs; handles with leading and trailing whitespace; and boundary cases for
exactly 15 and 16 character handles. These tests should verify that
normalizeTwitterHandle handles these edge cases appropriately by either
normalizing them correctly or returning null as expected.
- Around line 29-37: The extractTweetId test suite is missing edge-case tests
that are important for ensuring robust input validation. Add additional test
cases to the existing it block or create new test cases within the
describe('extractTweetId') block to cover: null, undefined, and empty string
inputs (these should likely return null), malformed URLs that lack a status path
component (to ensure the regex properly validates URL structure), and very long
numeric strings that don't match the actual tweet ID pattern (to verify the
function doesn't incorrectly match arbitrary long numbers). Each edge case
should have an expect statement validating the expected behavior of
extractTweetId when given these problematic inputs.
- Around line 91-116: The test for computeWotScores only covers a single
scenario with a follow event and claimed identity status. Add additional test
cases to the same describe block to cover: (1) multiple WoT signals such as
follows, reports, and assertions in a single call to computeWotScores, (2)
different identity status values (verified, claimed, and failed) to ensure
scoring works correctly for each, (3) edge cases like calling computeWotScores
with empty signals array and with conflicting signals, and (4) different
starting values for autoZapAllowed (both true and false). Each test should
verify that computeWotScores correctly computes wot scores while handling these
various scenarios appropriately.
- Around line 83-89: The test suite for the lightningAddressToLnurlp function
currently only covers the happy path with a valid email address. Add additional
test cases within the same describe block to cover error cases and edge cases
including invalid formats (no-at-sign, `@missing-user`, missing-domain@), null and
undefined inputs, empty strings, and addresses with multiple @ symbols. Each
test case should verify that the function handles these inputs appropriately,
either by throwing an error or returning a specific error value as documented by
the function's specification.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c7bce19a-493e-41dc-923c-ab0cd626a1e8

📥 Commits

Reviewing files that changed from the base of the PR and between dfc22e5 and 2ac2ef0.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (13)
  • package.json
  • scripts/relay-directory-crawler.mjs
  • scripts/relay-directory-crawler.test.mjs
  • src/index.ts
  • src/nostr-verify-identity/__tests__/verify-utils.test.ts
  • src/nostr-verify-identity/nostr-verify.ts
  • src/nostr-verify-identity/render.ts
  • src/nostr-verify-identity/spec/spec.md
  • src/nostr-verify-identity/style.ts
  • src/nostr-verify-identity/verify-utils.ts
  • stories/nostr-verify-identity/NostrVerifyIdentity.stories.tsx
  • vite.config.esm.ts
  • vite.config.umd.ts

Comment thread scripts/relay-directory-crawler.mjs Outdated
Comment thread scripts/relay-directory-crawler.mjs Outdated
Comment thread scripts/relay-directory-crawler.test.mjs Outdated
Comment thread src/nostr-verify-identity/nostr-verify.ts
Comment thread src/nostr-verify-identity/render.ts Outdated
Comment thread src/nostr-verify-identity/verify-utils.ts
Comment thread src/nostr-verify-identity/verify-utils.ts Outdated
@saiy2k

saiy2k commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Hi @alokdangre,
This is a very good start doing Identity component and crawler.

1 major feedback. The component and crawler should go in 2 separate PRs, so it's easy to discuss and improve each in isolation. Here, I will post my feedback on the identity component.

  1. In the PR description, can you mention this component is for our own use only, not for the end-users.

    • And please also elaborate how we will use the component / where it will fit-in (the nostr-directory clone)
    • Also please make this component, a part of the local/dev storybook only, not for prod/deployed storybook (You can see the Testing folders for each component, only when ran locally).
  2. mirroring the identity tag into kind:0 when a profile already exists -> Why is this done?

    • User's will not be comfortable with messing with their kind:0
    • We should also add this only if there is a strong need. In which case, we also need to explain why this is done and make it optional.
  3. Dark theme is not working in the storybook.

@alokdangre

Copy link
Copy Markdown
Contributor Author

the identity component is primarily end-user facing,

More precisely:

  • End-user use:a Nostr user opens the component, connects their wallet, posts a proof tweet, and publishes the verified mapping.
  • our use: the component creates clean NIP-39 identity data that your crawler, directory, and extension can consume later.

@alokdangre

Copy link
Copy Markdown
Contributor Author

Because kind:0 is still the event most Nostr clients read as a user’s public profile, while kind:10011 is the canonical NIP-39 home.
So mirroring the i tag into kind:0 does two things:
Makes the identity visible in older/common clients
Many clients show profile metadata from kind:0 and may ignore kind:10011 for identity display. Without the mirror, the user “verifies” but nothing obvious changes in the client most people use.

Improves compatibility during the transition
kind:10011 is the spec-correct place for external identity tags, but mirroring makes the proof more widely consumable while the ecosystem is still inconsistent.

The important guardrail is what the component already does: it only mirrors into kind:0 if a profile event already exists. That avoids creating a blank profile and accidentally wiping name, about, picture, or lud16

@alokdangre alokdangre changed the title Add Nostr verify identity component and relay directory crawler Add Nostr verify identity component Jun 15, 2026
@saiy2k

saiy2k commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Thanks for the comments Alok. DMed in Signal

@saiy2k

saiy2k commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Makes the identity visible in older/common clients

Many clients show profile metadata from kind:0 and may ignore kind:10011 for identity display. Without the mirror, the user “verifies” but nothing obvious changes in the client most people use.

We can add a helper message somewhere in the component or the page, instructing the user to update their bio with their social links.
As I said earlier, user's wont' be comfortable with a page, changing their bio.

And once you made the component, dev only, as with 'testing' folders. I will give do a 2nd round of review.

Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants