본문으로 건너뛰기
cccskills
무료GitHub에 공개

nostr-addressable-event-dual-tag-query

Fix "count shows X but list shows empty" bugs when querying related events (comments, reactions, zaps, reposts) for Nostr addressable events (Kind 30000-39999). Use when: (1) REST API shows engagement count > 0 but WebSocket query returns empty, (2) Comments/reactions exist but app shows "No comments yet" or similar, (3) Working with NIP-22 comments, NIP-25 reactions, or NIP-18 reposts on Kind 34235/34236 video events or other addressable events. Root cause: clients may reference addressable events using either E tag (event ID) or A tag (kind:pubkey:d-tag format), and querying only one tag misses events tagged with the other.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

실행 점수 미검증

설치 방법 보기

포함된 파일(1)

  • SKILL.md6.2 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Nostr Addressable Event Dual-Tag Query

Problem

When querying related events (comments, reactions, reposts, zaps) for Nostr addressable events (Kind 30000-39999), the query returns fewer or zero results even though an indexing service shows the correct count. This happens because addressable events can be referenced two ways, and different clients use different methods.

Context / Trigger Conditions

Use this skill when:

  • Video/post shows comment count of 5, but comments modal says "No comments yet"
  • REST API (like Funnelcake) returns correct engagement counts, but WebSocket queries return empty
  • Working with NIP-22 comments (Kind 1111) on addressable events
  • Working with NIP-25 reactions (Kind 7) on addressable events
  • Working with NIP-18 reposts (Kind 6/16) on addressable events
  • Any feature querying events that reference Kind 30000-39999 events

Root Cause

For addressable events (Kind 30000-39999, like Kind 34236 videos), related events can reference the target using either:

  1. E tag: References by event ID (64-character hex)

    ["E", "abc123...def456", "", "author-pubkey"]
    
  2. A tag: References by addressable identifier (kind:pubkey:d-tag)

    ["A", "34236:abc123...def456:my-video-id", "", "author-pubkey"]
    

Different Nostr clients and indexers use different conventions:

  • Some always use E tags (event ID-based)
  • Some prefer A tags for addressable events (following NIP-22 strictly)
  • Some use both tags

If your app only queries by one tag type, you'll miss events tagged with the other.

Solution

1. Update Filter Class (if needed)

Ensure your Filter class supports uppercase A tag queries:

// In Filter class
List<String>? uppercaseA;  // Add this field

// In toJson()
if (uppercaseA != null) {
  data['#A'] = uppercaseA;
}

2. Query by BOTH Tags

When loading related events, run two parallel queries and merge results:

Future<List<Event>> loadRelatedEvents({
  required String eventId,
  required String? addressableId,  // Format: "kind:pubkey:d-tag"
}) async {
  // Query by E tag (event ID)
  final filterByE = Filter(
    kinds: [targetKind],
    uppercaseE: [eventId],
  );

  // If addressable ID available, also query by A tag
  if (addressableId != null && addressableId.isNotEmpty) {
    final filterByA = Filter(
      kinds: [targetKind],
      uppercaseA: [addressableId],
    );

    // Run both queries in parallel
    final results = await Future.wait([
      nostrClient.queryEvents([filterByE]),
      nostrClient.queryEvents([filterByA]),
    ]);

    // Merge and deduplicate by event ID
    final eventMap = <String, Event>{};
    for (final event in results[0]) {
      eventMap[event.id] = event;
    }
    for (final event in results[1]) {
      eventMap[event.id] = event;
    }

    return eventMap.values.toList();
  }

  return nostrClient.queryEvents([filterByE]);
}

3. Post with BOTH Tags

When creating related events, include both E and A tags for maximum compatibility:

final tags = <List<String>>[
  // Always include E tag
  ['E', rootEventId, '', authorPubkey],

  // Include A tag for addressable events
  if (rootAddressableId != null && rootAddressableId.isNotEmpty)
    ['A', rootAddressableId, '', authorPubkey],

  // ... other tags
];

4. Build Addressable ID

Construct the addressable identifier from event metadata:

String? get addressableId {
  if (dTag == null) return null;
  return '$kind:$pubkey:$dTag';  // e.g., "34236:abc123...:my-video-id"
}

Verification

After implementing:

  1. Find an event where REST API shows count > 0 but your app showed empty
  2. Verify the list now shows the expected items
  3. Post a new related event (comment/reaction)
  4. Verify it appears when querying by either E or A tag

Example

Before fix - only queries by E tag:

final filter = Filter(
  kinds: [1111],  // Comments
  uppercaseE: [videoEventId],  // Only E tag!
);
// Returns 0 comments even though 5 exist (tagged with A)

After fix - queries both tags:

final filterByE = Filter(kinds: [1111], uppercaseE: [videoEventId]);
final filterByA = Filter(kinds: [1111], uppercaseA: [addressableId]);

final results = await Future.wait([
  client.queryEvents([filterByE]),
  client.queryEvents([filterByA]),
]);
// Returns all 5 comments regardless of how they were tagged

Notes

  • This pattern applies to ANY feature that queries events referencing addressable events
  • The d-tag may contain colons, so parse addressable IDs carefully: parts.sublist(2).join(':')
  • For count queries (NIP-45), take the maximum of both counts (may over-count if events have both tags)
  • Some relays may not support #A filter queries yet - test with your target relays
  • This is especially important for interoperability between different Nostr clients

Affected NIPs

  • NIP-22: Comments (Kind 1111) - uses E/A tags for root scope
  • NIP-25: Reactions (Kind 7) - references target event
  • NIP-18: Reposts (Kind 6/16) - references reposted event
  • NIP-33: Parameterized Replaceable Events (Kind 30000-39999) - defines addressable format
  • NIP-71: Video Events (Kind 34235/34236) - common use case for this pattern

References

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Fix ArgoCD ExternalSecret deployment failing with "namespace X is not permitted in project Y". Use when: (1) ExternalSecret shows OutOfSync in ArgoCD but won't sync, (2) ArgoCD application status shows "namespace X is not permitted in project 'infrastructure'", (3) ExternalSecret targets a namespace managed by a different ArgoCD project, (4) Using apps-of-apps pattern with separate infrastructure and application projects.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

Art direction for any content — reads text, PDF, Word, HTML, PPT, then proposes 2-3 creative directions with photography style, mood, and visual language. After selection, generates AI image prompts and visual briefs section-by-section. Use when the user shares content and needs visual direction, image sourcing, or creative direction for any material.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

Fix "Null check operator used on a null value" errors when an object is set to null during an async await. Use when: (1) Object reference is nullified while awaiting, (2) Code accesses object with ! after await returns, (3) Cancel/dispose operations run concurrently with async operations on same object. Solution: capture local reference before await.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

Add custom metadata headers (x-amz-meta-*) to AWS v4 signed requests for GCS S3-compatible API. Use when: (1) Adding custom metadata to GCS uploads via S3 API, (2) Getting signature mismatch errors after adding new headers, (3) x-amz-meta-* headers being ignored or causing 403 errors. Custom headers MUST be included in canonical headers and signed headers list.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

Fix password/secret authentication failures caused by trailing newlines when creating Google Cloud secrets (or similar) with bash here-strings. Use when: (1) Password authentication fails with correct password, (2) Secret created with `<<< "value"` syntax, (3) Error like "password authentication failed" or "invalid token" despite correct value. Bash here-strings (`<<<`) add a trailing newline that corrupts secrets.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

Fix silent video/media processing failures caused by URL extraction code that filters on file extensions (.mp4, .webm, .webp). Use when: (1) Media moderation, transcoding, or analysis silently skips files from Blossom or content-addressed storage servers, (2) URL extraction from Nostr event tags (imeta, r tags) drops URLs without recognized extensions, (3) CDN fallback URLs append .mp4 but the actual server uses extensionless content-addressed paths like /{sha256}. Common in Nostr video events (kind 34236) where different clients use different URL formats.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

divinevideo/divine-mobile2652026년 10월 23일 업데이트

divinevideo의 스킬 모두 보기

이 스킬의 문제 신고