From addb2a4abb81e9d389b8cf84d7f75be6a00a0ebd Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 27 Apr 2026 14:12:23 +0000 Subject: [PATCH] fix(quartz): accept legacy nostrnests tag names on kind-30312 read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first-party nostrnests web client (NestsUI-v2) emits kind-30312 events using NIP-53 *streaming-event* tag names rather than the kind-30312 names defined by NIP-53 itself. Confirmed against `nostrnests/nests/NestsUI-v2/src/components/EditRoomDialog.tsx` and `useRoomList.ts`: - room name: `title` (NIP-53 spec for 30312: `room`) - MoQ relay: `streaming` (NIP-53 spec for 30312: `endpoint`) - moq-auth: `auth` (NIP-53 spec for 30312: `service`) - status: `live` (NIP-53 spec for 30312: `open` / `private` / `closed` / `planned`) Our parsers followed the NIP-53 spec, so events from the production nostrnests web app fell through to status=null, which `checkStatus` then surfaced as "Ended" in the live-rooms UI. Add legacy-alias acceptance to all four parsers — read-side only; we still emit the canonical NIP-53 forms, matching EGG-01. Per-tag changes: StatusTag: "live" → OPEN, "ended" → CLOSED RoomNameTag: "title" alias → room EndpointUrlTag: "streaming" alias → endpoint ServiceUrlTag: "auth" alias → service No emit-side changes; no spec change. https://claude.ai/code/session_01RDpuki4t8StSg1CZcXnV5b --- .../meetingSpaces/tags/EndpointUrlTag.kt | 10 +++++++++- .../meetingSpaces/tags/RoomNameTag.kt | 10 +++++++++- .../meetingSpaces/tags/ServiceUrlTag.kt | 9 ++++++++- .../meetingSpaces/tags/StatusTag.kt | 13 +++++++++++++ 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/EndpointUrlTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/EndpointUrlTag.kt index 3872826b5..acc8b1966 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/EndpointUrlTag.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/EndpointUrlTag.kt @@ -27,9 +27,17 @@ class EndpointUrlTag { companion object Companion { const val TAG_NAME = "endpoint" + /** + * Legacy alias used by first-generation nostrnests web clients, + * which reused the NIP-53 streaming-event tag name for the + * kind-30312 MoQ relay URL. Accepted on read; we always emit + * the canonical [TAG_NAME]. + */ + const val LEGACY_TAG_NAME = "streaming" + fun parse(tag: Array): String? { ensure(tag.has(1)) { return null } - ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[0] == TAG_NAME || tag[0] == LEGACY_TAG_NAME) { return null } ensure(tag[1].isNotEmpty()) { return null } return tag[1] } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/RoomNameTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/RoomNameTag.kt index f952f6c43..3a4b03179 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/RoomNameTag.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/RoomNameTag.kt @@ -27,9 +27,17 @@ class RoomNameTag { companion object Companion { const val TAG_NAME = "room" + /** + * Legacy alias used by first-generation nostrnests web clients, + * which reused the NIP-53 streaming-event `title` tag for the + * kind-30312 room name. Accepted on read; we always emit the + * canonical [TAG_NAME]. + */ + const val LEGACY_TAG_NAME = "title" + fun parse(tag: Array): String? { ensure(tag.has(1)) { return null } - ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[0] == TAG_NAME || tag[0] == LEGACY_TAG_NAME) { return null } ensure(tag[1].isNotEmpty()) { return null } return tag[1] } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/ServiceUrlTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/ServiceUrlTag.kt index 9d85782f9..521a90ac0 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/ServiceUrlTag.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/ServiceUrlTag.kt @@ -27,9 +27,16 @@ class ServiceUrlTag { companion object Companion { const val TAG_NAME = "service" + /** + * Legacy alias used by first-generation nostrnests web clients + * for the moq-auth sidecar URL. Accepted on read; we always emit + * the canonical [TAG_NAME]. + */ + const val LEGACY_TAG_NAME = "auth" + fun parse(tag: Array): String? { ensure(tag.has(1)) { return null } - ensure(tag[0] == TAG_NAME) { return null } + ensure(tag[0] == TAG_NAME || tag[0] == LEGACY_TAG_NAME) { return null } ensure(tag[1].isNotEmpty()) { return null } return tag[1] } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/StatusTag.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/StatusTag.kt index 49b15c2bd..b3a39cee8 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/StatusTag.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip53LiveActivities/meetingSpaces/tags/StatusTag.kt @@ -40,9 +40,22 @@ class StatusTag { fun parse(code: String): STATUS? = when (code) { PLANNED.code -> PLANNED + OPEN.code -> OPEN + PRIVATE.code -> PRIVATE + CLOSED.code -> CLOSED + + // Legacy aliases used by first-generation nostrnests + // web clients that reused NIP-53 streaming-event + // status values for kind-30312 meeting spaces. + // Accept on read to preserve interop; we always emit + // the canonical EGG-01 codes. + "live" -> OPEN + + "ended" -> CLOSED + else -> null } }