Files
amethyst/.claude/skills/searchable-events/references/searchable-kinds.md
Claude 0057843c2e feat(quartz): type Zapstore identity proofs, WalletScrutiny verifications and Kanban boards
Adds typed classes for the app-release trust kinds and the kind-30301
collision found in the 2026-10-08 relay census:

- 30509 IdentityProofEvent (experimental/zapstore/identityProof): links an
  APK signing certificate (d = SHA-256 of its DER) to the publishing npub.
  Exposes the signed message, expiry, revocation, certificateHashMatches()
  and verifyEd25519Signature(); RSA / P-256 proofs (every live one) need an
  X.509 parser and verifiers Quartz does not have.
- 9401 AssetBundleEvent (experimental/walletScrutiny/assetBundle): release
  files registered by hash for reproducible-build checks; reuses NIP-82's
  `i` / `version` tags and joins NIP-82 assets by hash.
- 30301 BuildVerificationEvent (experimental/walletScrutiny/verification):
  the verdict, verified hashes, attachments and the based-on verification;
  searchable by its description and markdown report.
- 30301 KanbanBoardEvent and 30302 KanbanCardEvent (experimental/kanban),
  per NIP PR #1665, searchable by title and description.

Kind 30301 is shared, so EventFactory picks the class by tags as it does
for 38000: `i` + `status` is a verification, a title or named column a
board, and the encrypted planner app's tasks fall to
UnrecognizedKind30301Event. Kind 30302 does the same for Fieldbook's team
memberships (UnrecognizedKind30302Event). Probes answer as the board and
the card.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FrQx1ZvQka64aFAnZM1Hjd
2026-10-08 15:23:25 +00:00

279 lines
28 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Searchable kinds — the authoritative implementor table
Every concrete `SearchableEvent` implementor in Quartz, with the exact `indexableContent()`
expression. **Update this file in the same PR as any change to the searchable set or to an
`indexableContent()` body** (see SKILL.md). Verified against the code 2026-10-08.
Counts: 201 concrete classes covering 201 kind values (`GitStatusEvent` spans 4 kinds;
kind 30063 is shared by two NIPs, kind 38000 by three classes, kind 30301 by two and kind 38385 with other apps — see the footnotes). The kind
set is exactly `SearchableKinds.ALL` (`nip50Search/SearchableKinds.kt`). File paths are under
`quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/`.
Separator legend: **NL** = `joinToString("\n")`, **SP** = `joinToString(" ")`.
| Kind | Class | Package | `indexableContent()` |
|---|---|---|---|
| 0 | MetadataEvent | nip01Core/metadata | `contactMetaData()?.let { listOfNotNull(it.name, it.displayName, it.about, it.nip05, it.lud06, it.lud16, it.website, it.picture, it.banner).joinToString(" ") } ?: ""` (SP) |
| 1 | TextNoteEvent | nip10Notes | `listOfNotNull(subject(), content)` NL |
| 5 | DeletionRequestEvent | nip09Deletions | `content` (the deletion reason) |
| 9 | ChatEvent | nipC7Chats | `content` |
| 11 | ThreadEvent | nip7DThreads | `listOfNotNull(title(), content)` NL |
| 14 | ChatMessageEvent | nip17Dm/messages | `listOfNotNull(subject(), content)` NL |
| 15 | ChatMessageEncryptedFileHeaderEvent | nip17Dm/files | `summary().orEmpty()` — `content` is the encrypted file's URL, not text |
| 20 | PictureEvent | nip68Picture | `(listOfNotNull(title(), content) + location() + imageDescriptions())` NL — `location` tag values, then each imeta's non-blank `alt` |
| 21 | VideoNormalEvent | nip71Video | inherited `RegularVideoEvent`: `(listOfNotNull(title(), content) + segmentTitles())` NL |
| 22 | VideoShortEvent | nip71Video | inherited `RegularVideoEvent`: `(listOfNotNull(title(), content) + segmentTitles())` NL |
| 24 | PublicMessageEvent | nipA4PublicMessages | `content` |
| 31 | ExternalCitationEvent | experimental/citations | inherited `CitationEvent`: `listOfNotNull(title(), summary(), content, author(), publishedBy())` NL |
| 32 | HardcopyCitationEvent | experimental/citations | `listOfNotNull(title(), chapterTitle(), summary(), content, author(), editor(), publishedIn(), publishedBy())` NL |
| 33 | PromptCitationEvent | experimental/citations | `listOfNotNull(title(), summary(), content, author(), publishedBy(), llm())` NL |
| 40 | ChannelCreateEvent | nip28PublicChat/admin | `channelInfo().let { listOfNotNull(it.name, it.about, it.picture).joinToString(" ") }` (SP) |
| 41 | ChannelMetadataEvent | nip28PublicChat/admin | same as kind 40 (SP) |
| 42 | ChannelMessageEvent | nip28PublicChat/message | `content` |
| 43 | ChannelHideMessageEvent | nip28PublicChat/admin | `reason()` — the `reason` of NIP-28's `{"reason": …}` JSON content, or the plain-text content |
| 44 | ChannelMuteUserEvent | nip28PublicChat/admin | `reason()` — as kind 43 |
| 54 | PodcastEpisodeEvent | nipF4Podcasts/episode | `listOfNotNull(title(), description(), content)` NL |
| 62 | RequestToVanishEvent | nip62RequestToVanish | `content` |
| 64 | ChessGameEvent | nip64Chess/game | `searchText().all()` NL — the PGN's `Event`, `Site`, `White`, `Black`, `Annotator`, `Opening`, `Variation`, `WhiteTeam`, `BlackTeam` tag-pair values, then its `{…}` comments; never the movetext |
| 818 | WikiMergeRequestEvent | nip54Wiki | `content` |
| 1010 | TextNoteModificationEvent | experimental/edits | `listOfNotNull(content, summary())` NL (content first) |
| 1022 | BidConfirmationEvent | nip15Marketplace/bidConfirmation | `confirmationData()?.message.orEmpty()` — the `message` field of the JSON content |
| 1063 | FileMetadataEvent | nip94FileMetadata | `listOfNotNull(summary(), content)` NL |
| 1065 | FileStorageHeaderEvent | experimental/nip95/header | `listOfNotNull(summary(), content)` NL — the caption; the base64 payload lives in the separate 1064 and is never indexed |
| 1068 | PollEvent | nip88Polls/poll | `buildString { append(content); options().forEach { append('\n').append(it.label) } }` |
| 1111 | CommentEvent | nip22Comments | `(listOf(content) + tags.hashtags())` NL |
| 1163 | ProfileGalleryEntryEvent | experimental/profileGallery | `listOfNotNull(summary())` NL |
| 1301 | WorkoutRecordEvent | experimental/fitness/workout | `listOfNotNull(title(), content)` NL |
| 1311 | LiveActivitiesChatMessageEvent | nip53LiveActivities/chat | `(listOf(content) + tags.hashtags())` NL |
| 1312 | LiveActivitiesRaidEvent | nip53LiveActivities/raid | `content` |
| 1313 | LiveActivitiesClipEvent | nip53LiveActivities/clip | `listOfNotNull(title(), content)` NL |
| 1315 | RoadEventReportEvent | experimental/roadstr/report | `content` |
| 1337 | CodeSnippetEvent | nipC0CodeSnippets | `listOfNotNull(snippetName(), snippetDescription(), content)` NL |
| 1617 | GitPatchEvent | nip34Git/patch | `content` |
| 1618 | GitPullRequestEvent | nip34Git/pr | `(listOfNotNull(subject(), content) + labels())` NL |
| 1621 | GitIssueEvent | nip34Git/issue | `(listOfNotNull(subject(), content) + topics())` NL |
| 1622 | GitReplyEvent | nip34Git/reply | `content` |
| 1624 | GitCoverNoteEvent | nip34Git/coverNote | `content` (markdown cover note) |
| 1630–1633 | GitStatusEvent | nip34Git/status | inherited `GitStatusEvent`: `content` (open/applied/closed/draft) |
| 1808 | AudioHeaderEvent | experimental/audio/header | `content` |
| 1985 | LabelEvent | nip32Labeling | `(listOf(content) + labels().map { it.label }).filter { it.isNotEmpty() }` NL |
| 2003 | TorrentEvent | nip35Torrents | `(listOfNotNull(title(), content) + fileNames())` NL — the `file` tags' paths |
| 2004 | TorrentCommentEvent | nip35Torrents | `content` |
| 2473 | BirdDetectionEvent | experimental/birdstar | `listOfNotNull(summary(), speciesName())` NL |
| 3302 | ConcordChatEditEvent | concord/cord03Channels | `content` |
| 5050 | DvmTextGenerationRequestEvent | nip90Dvms/textGeneration | `inputs().filter { it.type == "prompt" \|\| it.type == "text" }.joinToString(" ") { it.value }` (SP) |
| 5100 | DvmImageGenerationRequestEvent | nip90Dvms/imageGeneration | `listOfNotNull(prompt(), negativePrompt()).joinToString(" ")` (SP) |
| 5128 | SiteSnapshotEvent | nip5aStaticWebsites | `listOfNotNull(title(), description())` NL |
| 5129 | NappletSnapshotEvent | nip5dNapplets | `listOfNotNull(title(), description())` NL |
| 5250 | DvmTextToSpeechRequestEvent | nip90Dvms/textToSpeech | `text() ?: ""` |
| 5302 | DvmContentSearchRequestEvent | nip90Dvms/contentSearch | `searchQuery() ?: ""` |
| 5303 | DvmPeopleSearchRequestEvent | nip90Dvms/peopleSearch | `searchQuery() ?: ""` |
| 5901 | DvmOpReturnRequestEvent | nip90Dvms/opReturn | `text() ?: ""` |
| 6969 | ZapPollEvent | experimental/zapPolls | `buildString { append(content); pollOptionsArray().forEach { append('\n').append(it.descriptor) } }` |
| 7516 | GeocacheFoundLogEvent | nipCCGeocaching/foundLog | `content` |
| 8002 | ArchivedIdentityEvent | buzz/iaIdentityArchival | `content` |
| 8003 | UnarchivedIdentityEvent | buzz/iaIdentityArchival | `content` |
| 8333 | OnchainZapEvent | nipBCOnchainZaps/zap | `content` |
| 9002 | GroupEditMetadataEvent | nip29RelayGroups/moderation | `(listOfNotNull(name(), about()) + hashtags())` NL |
| 9007 | CreateGroupEvent | nip29RelayGroups/moderation | `listOfNotNull(name(), about())` NL |
| 9021 | GroupJoinRequestEvent | nip29RelayGroups/request | `content` |
| 9022 | GroupLeaveRequestEvent | nip29RelayGroups/request | `content` |
| 9035 | ArchiveRequestEvent | buzz/iaIdentityArchival | `content` |
| 9036 | UnarchiveRequestEvent | buzz/iaIdentityArchival | `content` |
| 9041 | ZapGoalEvent | nip75ZapGoals | `listOfNotNull(summary(), content)` NL |
| 9321 | NutzapEvent | nip61Nutzaps/nutzap | `content` |
| 9734 | ZapRequestEvent | nip57Zaps | `content` |
| 9735 | ZapReceiptEvent | nip57Zaps | `zapRequest?.content.orEmpty()` — indexes the **embedded 9734's** content |
| 9736 | Bolt12ZapEvent | nipB1Bolt12Zaps/zap | `content` |
| 9737 | Bolt12ZapIntentEvent | nipB1Bolt12Zaps/intent | `content` |
| 9802 | HighlightEvent | nip84Highlights | `listOfNotNull(comment(), searchableContext(), content)` NL — `searchableContext()` is the `context` tag, else a `textquoteselector`'s prefix and suffix (whitespace-collapsed, space-joined) |
| 9998 | ListHeaderEvent | experimental/decentralizedLists/header | `tags.searchableListContent()` NL — `names` (singular, plural), `titles` (singular, plural), `name`, `title`, `description`, `comments`, then in tag order every `t` value that is not `isMachineValue` and every value of every other tag (except `alt`, `client`, `imeta`) that passes `isNaturalLanguageValue` (has whitespace or non-ASCII, or is one capitalized letters-only word; never JSON, numbers, URIs of any scheme, addresses, hex ids, UUIDs, bech32) |
| 9999 | ListItemEvent | experimental/decentralizedLists/item | same as 9998 |
| 10003 | BookmarkListEvent | nip51Lists/bookmarkList | `listOfNotNull(titleOrName())` NL |
| 10100 | AgentProfileEvent | buzz/agentProfiles | `profileOrNull()?.let { listOfNotNull(it.name, it.displayName).joinToString("\n") } ?: ""` |
| 10154 | PodcastMetadataEvent | nipF4Podcasts/metadata | `listOfNotNull(title(), description())` NL |
| 11316 | CvmServerAnnouncementEvent | contextvm/cep06Announcements | `listOfNotNull(serverName(), about())` NL (class lives in `CvmAnnouncementEvents.kt`) |
| 11333 | SnoAvatarEvent | cyberspace/deck0003Sno | `nameTag().orEmpty()` — the `name` tag only; `content` is SNO geometry JSON |
| 11871 | AttestorProficiencyEvent | experimental/attestations/proficiency | `listOfNotNull(description())` NL |
| 12473 | BirdexEvent | experimental/birdstar | `(listOfNotNull(summary()) + speciesNames())` NL |
| 15128 | RootSiteEvent | nip5aStaticWebsites | `listOfNotNull(title(), description())` NL |
| 15129 | RootNappletEvent | nip5dNapplets | `listOfNotNull(title(), description())` NL |
| 30000 | FollowSetEvent | nip51Lists/followSet | `listOfNotNull(titleOrName(), description())` NL |
| 30001 | OldBookmarkListEvent | nip51Lists/bookmarkList | `listOfNotNull(title())` NL |
| 30002 | RelaySetEvent | nip51Lists/relaySets | `listOfNotNull(title(), description())` NL |
| 30003 | BookmarkSetEvent | nip51Lists/bookmarkSet | `listOfNotNull(titleOrName(), description())` NL |
| 30004 | ArticleCurationSetEvent | nip51Lists/articleCurationSet | `listOfNotNull(title(), description())` NL |
| 30005 | VideoCurationSetEvent | nip51Lists/videoCurationSet | `listOfNotNull(title(), description())` NL |
| 30006 | PictureCurationSetEvent | nip51Lists/pictureCurationSet | `listOfNotNull(title(), description())` NL |
| 30008 | AcceptedBadgeSetEvent | nip58Badges/accepted | `listOfNotNull(title(), description())` NL |
| 30009 | BadgeDefinitionEvent | nip58Badges/definition | `listOfNotNull(name(), description(), content)` NL |
| 30015 | InterestSetEvent | nip51Lists/interestSet | `(listOfNotNull(title(), description()) + publicHashtags())` NL |
| 30017 | StallEvent | nip15Marketplace/stall | `stallData()?.let { listOfNotNull(it.name, it.description).joinToString("\n") } ?: ""` |
| 30018 | ProductEvent | nip15Marketplace/product | `productData()?.let { (listOfNotNull(it.name, it.description) + categories()).joinToString("\n") } ?: ""` |
| 30019 | MarketplaceEvent | nip15Marketplace/marketplace | `marketplaceData()?.let { listOfNotNull(it.name, it.about).joinToString("\n") } ?: ""` |
| 30020 | AuctionEvent | nip15Marketplace/auction | `auctionData()?.let { (listOfNotNull(it.name, it.description) + tags.hashtags()).joinToString("\n") } ?: ""` |
| 30023 | LongFormContentEvent | nip23LongContent | `(listOfNotNull(title(), summary(), content) + topics())` NL |
| 30024 | LongFormDraftEvent | nip23LongContent/draft | `tags.longFormIndexableContent(content)` — the same as 30023: `(listOfNotNull(title(), summary(), content) + topics())` NL |
| 30030 | EmojiPackEvent | nip30CustomEmoji/pack | `(listOfNotNull(titleOrName(), description()) + publicEmojiCodes())` NL — the public `emoji` tags' shortcodes; `content` is NOT indexed (it is the NIP-44 ciphertext of the private emoji tags) |
| 30031 | StickerPackEvent | nip30CustomEmoji/stickers | `(listOfNotNull(title(), description()) + stickers().map { it.code })` NL (never `content`) |
| 30040 | PublicationIndexEvent | experimental/publications | `listOfNotNull(title(), author(), summary())` NL |
| 30041 | PublicationContentEvent | experimental/publications | `listOfNotNull(title(), content)` NL |
| 30045 | BookshelfDirectoryEvent | experimental/library | `listOfNotNull(title(), summary(), content)` NL |
| 30054 | Podcasting20EpisodeEvent | nipXXPodcasting20/episode | `(listOfNotNull(title(), description(), content) + personNames() + soundbiteTitles() + topics())` NL |
| 30055 | Podcasting20TrailerEvent | nipXXPodcasting20/trailer | `listOfNotNull(title(), content)` NL |
| 30063 | ReleaseArtifactSetEvent † | nip51Lists/releaseArtifactSet | `listOfNotNull(title(), description(), searchableReleaseNotes())` NL |
| 30066 | LiveChessMoveEvent | nip64Chess/move | `content` |
| 30068 | LiveChessDrawOfferEvent | nip64Chess/draw | `content` |
| 30142 | LearningResourceEvent | experimental/library | `(listOfNotNull(title(), alternateTitle(), summary(), alternateSummary(), content, author()) + facetLabels())` NL — `facetLabels()` = resource types, educational levels, subjects, deduplicated |
| 30175 | PersonaEvent | buzz/apPersonas | `personaOrNull()?.let { listOfNotNull(it.displayName, it.description, it.systemPrompt).joinToString("\n") } ?: ""` |
| 30176 | TeamEvent | buzz/teams | `teamOrNull()?.let { listOfNotNull(it.name, it.description, it.instructions).joinToString("\n") } ?: ""` |
| 30177 | ManagedAgentEvent | buzz/managedAgents | `agentOrNull()?.let { listOfNotNull(it.name, it.systemPrompt).joinToString("\n") } ?: ""` |
| 30178 | TeamCatalogEvent | buzz/teamCatalog | `catalogOrNull()?.let { catalog -> buildList { add(catalog.name); catalog.description?.let(::add); catalog.instructions?.let(::add); catalog.members.forEach { member -> add(member.displayName); member.systemPrompt?.let(::add) } }.joinToString("\n") } ?: ""` |
| 30267 | AppCurationSetEvent | nip51Lists/appCurationSet | `listOfNotNull(titleOrName(), description())` NL |
| 30296 | InteractiveStoryPrologueEvent | experimental/interactiveStories | inherited base: `(listOfNotNull(title(), summary(), content) + optionLabels())` NL |
| 30297 | InteractiveStorySceneEvent | experimental/interactiveStories | inherited base: `(listOfNotNull(title(), summary(), content) + optionLabels())` NL |
| 30301 | KanbanBoardEvent ¶ | experimental/kanban/board | `listOfNotNull(title(), description())` NL |
| 30301 | BuildVerificationEvent ¶ | experimental/walletScrutiny/verification | `listOfNotNull(description(), report())` NL (both from the JSON `content`) |
| 30302 | KanbanCardEvent ¶ | experimental/kanban/card | `listOfNotNull(title(), description())` NL |
| 30311 | LiveActivitiesEvent | nip53LiveActivities/streaming | `listOfNotNull(title(), summary(), content)` NL |
| 30312 | MeetingSpaceEvent | nip53LiveActivities/meetingSpaces | `listOfNotNull(room(), summary(), content)` NL |
| 30313 | MeetingRoomEvent | nip53LiveActivities/meetingSpaces | `listOfNotNull(title(), summary())` NL |
| 30315 | UserStatusEvent | nip38UserStatus | `content` |
| 30382 | UserAssertionEvent | nip85TrustedAssertions/users | `(listOfNotNull(petName(), summary()) + topics())` NL — public tags only, never the NIP-44 content |
| 30392 | UserTrustedListEvent | experimental/trustedLists/users | inherited `TrustedListEvent`: `title() ?: ""` — the label only; `metric`/`d` are machine ids and `content` is a JSON echo of the membership |
| 30393 | EventTrustedListEvent | experimental/trustedLists/events | inherited `TrustedListEvent`: `title() ?: ""` |
| 30394 | AddressableTrustedListEvent | experimental/trustedLists/addressables | inherited `TrustedListEvent`: `title() ?: ""` |
| 30395 | ExternalIdTrustedListEvent | experimental/trustedLists/externalIds | inherited `TrustedListEvent`: `title() ?: ""` |
| 30402 | ClassifiedsEvent | nip99Classifieds | `(listOfNotNull(title(), summary(), content, location()) + categories())` NL |
| 30617 | GitRepositoryEvent | nip34Git/repository | `(listOfNotNull(name(), description(), content) + hashtags())` NL |
| 30620 | WorkflowDefEvent | buzz/workflow | `listOfNotNull(name(), content)` NL |
| 30621 | ProjectEvent | buzz/mpProjects | `listOfNotNull(displayName(), description())` NL |
| 30817 | NipTextEvent | experimental/nipsOnNostr | `listOfNotNull(title(), content)` NL |
| 30818 | WikiArticleEvent | nip54Wiki | `listOfNotNull(title(), summary(), content)` NL |
| 31337 | AudioTrackEvent | experimental/audio/track | `listOfNotNull(subject())` NL |
| 31871 | AttestationEvent | experimental/attestations/attestation | `content` |
| 31872 | AttestationRequestEvent | experimental/attestations/request | `content` |
| 31873 | AttestorRecommendationEvent | experimental/attestations/recommendation | `listOfNotNull(description())` NL |
| 31890 | FeedDefinitionEvent | feedDefinition | `title().orEmpty()` (now registered in `EventFactory`; before that it parsed as a plain `Event` and was never searched) |
| 31922 | CalendarDateSlotEvent | nip52Calendar/appt/day | `(listOfNotNull(title(), summary(), content) + locations() + hashtags())` NL |
| 31923 | CalendarTimeSlotEvent | nip52Calendar/appt/time | `(listOfNotNull(title(), summary(), content) + locations() + hashtags())` NL |
| 31924 | CalendarCollectionEvent | nip52Calendar/calendar | `listOfNotNull(title(), content)` NL |
| 31925 | CalendarRSVPEvent | nip52Calendar/rsvp | `content` |
| 31987 | RelayReviewEvent | experimental/ratings | `content` |
| 31990 | AppDefinitionEvent | nip89AppHandlers/definition | `appMetaData()?.let { listOfNotNull(it.name, it.username, it.displayName, it.about, it.nip05, it.lud06, it.lud16, it.website, it.picture, it.banner, it.image).joinToString(" ") } ?: ""` (SP) |
| 32176 | BlossomPieceIndexEvent | experimental/library | `listOfNotNull(title(), summary(), content)` NL |
| 32267 | SoftwareApplicationEvent | experimental/nip82SoftwareApps/application | `listOfNotNull(name(), summary(), content)` NL |
| 33331 | SnoObjectEvent | cyberspace/deck0003Sno | `nameTag().orEmpty()` — the `name` tag only; `content` is SNO geometry JSON |
| 33401 | ExerciseTemplateEvent | experimental/fitness/workout | `listOfNotNull(title(), content)` NL |
| 33402 | WorkoutTemplateEvent | experimental/fitness/workout | `listOfNotNull(title(), content)` NL |
| 33534 | RelayRoleEvent | nip43RelayMembers/roles | `listOfNotNull(label(), description())` NL |
| 33863 | FundraiserEvent | experimental/agora | `listOfNotNull(title(), content)` NL |
| 34139 | MusicPlaylistEvent | experimental/music/playlist | `listOfNotNull(title(), description(), content)` NL |
| 34235 | AddressableNormalVideoEvent | nip71Video | inherited `AddressableVideoEvent`: `(listOfNotNull(title(), content) + segmentTitles())` NL |
| 34236 | AddressableShortVideoEvent | nip71Video | inherited `AddressableVideoEvent`: `(listOfNotNull(title(), content) + segmentTitles())` NL |
| 34259 | EntityRatingEvent | experimental/ratings | `content` |
| 34550 | CommunityDefinitionEvent | nip72ModCommunities/definition | `listOfNotNull(name(), description(), rules(), content)` NL |
| 35128 | NamedSiteEvent | nip5aStaticWebsites | `listOfNotNull(title(), description())` NL |
| 35129 | NamedNappletEvent | nip5dNapplets | `listOfNotNull(title(), description())` NL |
| 36767 | ThemeDefinitionEvent | experimental/profileTheme/definition | `listOfNotNull(title(), description())` NL (never the colors, fonts or URLs) |
| 36787 | MusicTrackEvent | experimental/music/track | `listOfNotNull(title(), artist(), album(), content)` NL |
| 37516 | GeocacheListingEvent | nipCCGeocaching/listing | `listOfNotNull(cacheName(), content)` NL (the `hint` is deliberately not indexed — matching a hint is spoiling it) |
| 37517 | GeocacheCurationListEvent | nipCCGeocaching/curation | `listOfNotNull(title(), description(), content)` NL |
| 38000 | MintRecommendationEvent ‡ | nip87Ecash/recommendation | `content` |
| 38000 | BallotEvent ‡ | experimental/ballots | `buildList { election()?.let { add(it) }; answers().forEach { add(it.answer) } }` NL |
| 38000 | PredictionMarketEvent ‡ | experimental/predictionMarkets | `(listOfNotNull(title(), description()) + outcomes() + listOfNotNull(resolution(), cancelReason(), socialPost()))` NL |
| 38192 | Ps1SaveEvent | experimental/ps1saves | `listOfNotNull(summary(), saveTitle(), region(), filename())` NL |
| 38383 | P2POrderEvent | nip69P2pOrderEvents | `(listOfNotNull(makerName(), currency()) + paymentMethods().orEmpty()).joinToString(" ")` (SP) |
| 38385 | MostroInfoEvent § | nip69P2pOrderEvents/mostroInfo | `(listOfNotNull(instanceName()) + fiatCurrenciesAccepted().orEmpty()).joinToString(" ")` (SP) |
| 39000 | GroupMetadataEvent | nip29RelayGroups/metadata | `(listOfNotNull(name(), about()) + hashtags())` NL |
| 39003 | GroupRolesEvent | nip29RelayGroups/metadata | `roles().flatMap { listOfNotNull(it.name, it.description) }` NL |
| 39089 | StarterPackEvent | nip51Lists/starterPack | `(listOfNotNull(title(), description()) + hashtags())` NL |
| 39092 | MediaStarterPackEvent | nip51Lists/mediaStarterPack | `listOfNotNull(title(), description())` NL |
| 39307 | TextTrackEvent | nip71Video/textTrack | `if (WebVttText.hasCues(content)) WebVttText.cueText(content) else content` — `cueText` is each cue's payload lines (NL) with tags removed and character references decoded; no header, timings, cue settings, ids, `NOTE`/`STYLE`/`REGION` blocks. Content with no `-->` timing line is not a caption file and is indexed as written |
| 39701 | WebBookmarkEvent | nipB0WebBookmarks | `(listOfNotNull(title(), description()) + hashtags())` NL |
| 39998 | AddressableListHeaderEvent | experimental/decentralizedLists/header | same as 9998 |
| 39999 | AddressableListItemEvent | experimental/decentralizedLists/item | same as 9998 |
| 40002 | StreamMessageV2Event | buzz/stream | `content` |
| 40003 | StreamMessageEditEvent | buzz/stream | `content` |
| 40006 | StreamMessageScheduledEvent | buzz/stream | `content` |
| 40007 | StreamReminderEvent | buzz/stream | `content` |
| 40008 | StreamMessageDiffEvent | buzz/stream | `description() ?: ""` — the `description` tag; the body is a unified diff and stays out |
| 40099 | SystemMessageEvent | buzz/stream | `payload()?.let { listOfNotNull(it.topic, it.purpose, it.publicReason).joinToString("\n") } ?: ""` — fields of the JSON content |
| 40100 | CanvasEvent | buzz/stream | `content` |
| 40901 | ChannelSummaryEvent | buzz/stream/sidecars | `summary()?.let { listOfNotNull(it.name, it.about, it.topic, it.purpose).joinToString("\n") } ?: ""` — fields of the JSON content |
| 42000 | ProductFeedbackEvent | buzz/moderation | `content` |
| 43001 | JobRequestEvent | buzz/jobs | `content` |
| 43002 | JobAcceptedEvent | buzz/jobs | `content` |
| 43003 | JobProgressEvent | buzz/jobs | `content` |
| 43004 | JobResultEvent | buzz/jobs | `content` |
| 43005 | JobCancelEvent | buzz/jobs | `content` |
| 43006 | JobErrorEvent | buzz/jobs | `content` |
| 45001 | ForumPostEvent | buzz/forum | `content` |
| 45003 | ForumCommentEvent | buzz/forum | `content` |
| 45010 | ArtifactEvent | buzz/arArtifacts | `listOfNotNull(title(), textBody())` NL — `textBody()` is `content` unless blank or JSON (starts with `{`/`[`) |
| 46030 | ApprovalGrantEvent | buzz/workflow | `content` |
| 46031 | ApprovalDenyEvent | buzz/workflow | `content` |
| 48106 | HuddleGuidelinesEvent | buzz/huddles | `content` |
† **Kind 30063 is shared** by NIP-51 release artifact sets and NIP-82 software releases, and
`ReleaseArtifactSetEvent` parses both. It indexes `title()` and `description()`, plus `content`
(the release notes, via `searchableReleaseNotes()`) only when it is non-blank and the event
carries the NIP-82 `i` + `version` tags — a NIP-51 set may hold encrypted private items in
`content`, which must never be indexed.
‡ **Kind 38000 is shared** by NIP-87 mint recommendations and two app formats, and `EventFactory`
picks the class by tags: `MintRecommendationEvent` when any `k` is 38172/38173 (or, with no
`k`, a non-blank `u` or an `a` to a 38172/38173 address); else `BallotEvent` on a non-blank
`election`; else `PredictionMarketEvent` on a `market`, ≥2 `outcome`s, or `type` + `end`; else
`UnrecognizedKind38000Event` — addressable (stores still key it by `d`) but unsearchable (the
spam votes that make up most of the kind). Kind-level probes (`EventFactory.probe`) answer as
`MintRecommendationEvent`. A market's `title()`/`description()` come from its `data` tag JSON,
the `title` tag, or JSON `content`, in that order; it also indexes its outcome labels, the
resolution, the cancel reason, and `content` as `socialPost()` when it is a non-blank, non-JSON post
and the market has no `description()` it would merely repeat.
§ **Kind 38385 is shared** by Mostro instance info and other apps (Paygress lease revocations,
bond-trade assignments, game scores). `EventFactory` builds `MostroInfoEvent` only when a `z` of
`info` or a `mostro_version` tag is present, else `UnrecognizedKind38385Event` — addressable but
unsearchable. Kind-level probes answer as `MostroInfoEvent`. Mostro's 38384 / 38386 and RoboSats'
31986 are split the same way, but none of their classes is searchable.
**Kind 30301 is shared** by Kanban boards (NIP PR #1665), WalletScrutiny reproducible-build
verifications and an encrypted planner app. `EventFactory` builds `BuildVerificationEvent` when a
non-blank `i` and `status` are present, else `KanbanBoardEvent` on a non-blank `title` or a `col`
with a name, else `UnrecognizedKind30301Event` — addressable but unsearchable (the planner's
NIP-44 tasks). Kind-level probes answer as `KanbanBoardEvent`. A verification indexes the
`description` and the markdown report (`content`) of its JSON `content`. Kind 30302 is split the
same way: `KanbanCardEvent` on a `title` or an `a` to a 30301 board, else
`UnrecognizedKind30302Event` (Fieldbook team memberships).
## Abstract bases (no kind of their own)
| Base class | Body | Concrete kinds |
|---|---|---|
| `CitationEvent` | `listOfNotNull(title(), summary(), content, author(), publishedBy())` NL | 31 (32 and 33 override with their extra fields) |
| `GitStatusEvent` | `content` | 1630, 1631, 1632, 1633 |
| `InteractiveStoryBaseEvent` | `(listOfNotNull(title(), summary(), content) + optionLabels())` NL — the non-blank `option` choice texts | 30296, 30297 |
| `AddressableVideoEvent` | `(listOfNotNull(title(), content) + segmentTitles())` NL — the `segment` tags' titles | 34235, 34236 |
| `RegularVideoEvent` | `(listOfNotNull(title(), content) + segmentTitles())` NL | 21, 22 |
| `TrustedListEvent` | `title() ?: ""` | 30392, 30393, 30394, 30395 |
## How to regenerate / verify this table
```bash
# The authoritative kind set (sorted, one comment per kind naming the class):
cat quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip50Search/SearchableKinds.kt
# All implementor files:
grep -rln "override fun indexableContent" quartz/src/commonMain
# For each, pair the KIND constant with the indexableContent() body.
# Searchability on the store path additionally requires EventFactory registration:
grep -n "<ClassName>" quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/EventFactory.kt
```
Two tests keep the code side from drifting: `SearchableKindsTest` (commonTest) builds every kind
0–65535 through `EventFactory` and asserts the searchable ones are exactly `SearchableKinds.ALL`,
and `IndexableContentGoldenTest` (jvmTest) records each kind's `indexableContent()` for a
tag-rich sample event in `quartz/src/jvmTest/resources/indexable-content.golden` — a CI-diffable
snapshot. Diff `SearchableKinds.kt` and the golden file at a version bump; this table is still
updated by hand.