Refreshes .claude/ skill library: fixes stale refs, adds 4 new skills

- Updates CLAUDE.md tech stack to current versions (Compose 1.10.3, Kotlin 2.3.20).
- Reframes kotlin-multiplatform iOS as mature; adds secp256k1-kmp 0.23.0 references.
- Updates desktop-expert Main.kt references (code grew from ~270 to 1341 lines and
  NavigationRail moved to ui/deck/SinglePaneLayout.kt); replaces obsolete
  "hardcoded ctrl = true" anti-pattern note with accurate isMacOS branching.
- Removes compose-desktop.md (superseded by desktop-expert/).
- Adds nostr-expert references: nip19-bech32, event-factory, crypto-and-encryption,
  large-cache. Adds kotlin-expert/common-utilities, compose-expert/rich-text-parsing,
  android-expert/image-loading.
- New skills: account-state (Account + LocalCache), relay-client (subscriptions,
  filter assemblers, preloaders), feed-patterns (FeedFilter + FeedViewModel family),
  auth-signers (NostrSigner across internal / NIP-46 / NIP-55).
This commit is contained in:
Claude
2026-04-21 21:00:45 +00:00
parent 9147f1b08b
commit 60edd473c7
31 changed files with 1743 additions and 373 deletions
@@ -0,0 +1,83 @@
# NIP-19: Bech32 Encoding & Parsing
Quartz implementation for `npub`, `nsec`, `note`, `nevent`, `nprofile`, `naddr`, `nrelay`, `nembed` — the user-facing encoded forms of Nostr identifiers.
## Layout
All under `quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip19Bech32/`:
- `Nip19Parser.kt` — the entry point. Parses any Bech32 or `nostr:` URI into a typed `Entity`.
- `bech32/Bech32Util.kt` — raw Bech32 encode/decode (bits ↔ 5-bit groups).
- `tlv/Tlv.kt` / `tlv/TlvBuilder.kt` — Type-Length-Value codec for composite entities (`nevent`, `nprofile`, `naddr`).
- `TlvTypes.kt` — TLV type constants (0 = special payload, 1 = relay, 2 = author, 3 = kind).
- `entities/` — one class per entity type (see below).
- `ATagExt.kt`, `ByteArrayExt.kt`, `EventExt.kt`, `ListEntityExt.kt`, `TlvBuilderExt.kt` — convenience extensions for encoding domain objects directly.
## Entity Types
Each is a `sealed class Entity` subclass under `entities/`:
| Class | Prefix | Payload | Purpose |
|-------------|------------|---------------------------------------------------|---------|
| `NPub` | `npub1...` | 32-byte pubkey | Public key |
| `NSec` | `nsec1...` | 32-byte private key | Private key (never log/share) |
| `NNote` | `note1...` | 32-byte event id | Bare note reference (no hints) |
| `NEvent` | `nevent1…` | TLV: event id + relays + author + kind | Rich note reference |
| `NProfile` | `nprofile…`| TLV: pubkey + relays | User reference with relay hints |
| `NAddress` | `naddr1…` | TLV: d-tag + relays + author + kind (addressable) | Parameterized replaceable event |
| `NRelay` | `nrelay1…` | TLV: relay URL | Relay pointer |
| `NEmbed` | `nembed1…` | Compressed event JSON | Full event embedded inline |
## Parsing
```kotlin
// From anywhere (URI, Bech32, nostr: prefix, "nostr:" + data):
val entity: Entity? = Nip19Parser.uriToRoute(input)?.entity
// More forgiving — strips scheme, whitespace, surrounding chars:
val parsed = Nip19Parser.tryParseAndClean(dirtyInput)
when (entity) {
is NPub -> entity.hex // 32-byte pubkey hex
is NEvent -> entity.hex + entity.relay + entity.author + entity.kind
is NAddress -> entity.atag // kind:pubkey:d-tag
is NProfile -> entity.hex + entity.relay
// …
}
```
## Encoding
The cleanest path is the entity's `toNostrUri()` / `toBech32()` methods (each entity class defines them). For composite entities (NEvent, NProfile, NAddress), internally the code builds a TLV buffer via `TlvBuilder`:
```kotlin
// TlvBuilder DSL (tlv/TlvBuilder.kt)
val bytes = TlvBuilder().apply {
addHex(TlvTypes.SPECIAL, eventIdHex)
addString(TlvTypes.RELAY, relayUrl)
addHex(TlvTypes.AUTHOR, authorHex)
addInt(TlvTypes.KIND, kind)
}.build()
Bech32Util.encode("nevent", bytes)
```
Kotlin-idiomatic extension helpers live in `TlvBuilderExt.kt`, `EventExt.kt`, and `ATagExt.kt` — prefer those over hand-building TLV.
## When to Use
- **Pasted input from users** → `Nip19Parser.tryParseAndClean` (handles prefixes, whitespace, leftover `nostr:`)
- **Internal routing / deep links** → `Nip19Parser.uriToRoute`
- **Outbound share links** → call the entity's `toNostrUri()` / `toBech32()` directly
- **Building a custom TLV entity** → `TlvBuilder` DSL + `Bech32Util.encode`
## Gotchas
- `NSec` should never be logged or propagated. Parse and discard the string buffer.
- Relay hints in `NEvent`/`NProfile`/`NAddress` are hints, not guarantees. The Outbox model (NIP-65) overrides them.
- TLV types are fixed (see `TlvTypes.kt`); do not reorder or invent new types without NIP-19 support.
- `NEmbed` is an Amethyst-specific compressed-event extension, not part of NIP-19 proper.
## Tests
See `quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip19Bech32/` for round-trip tests covering every entity and `Nip19Parser` input cleaning.