account-state
Account state and in-memory event store patterns in Amethyst. Use when working with `Account.kt` (per-user state objects — `kind3FollowList`, `nip65RelayList`,…
Integration guide for using the Quartz Nostr KMP library in external projects. Use when: (1) adding Quartz as a Gradle dependency, (2) setting up NostrClient with WebSocket, (3) creating/signing/sending events, (4) building relay subscriptions with Filter, (5) handling keys with
$ npx -y skills add vitorpamplona/amethyst --skill quartz-integration --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/quartz-integrationContext preview
The summary Claude sees to decide when to auto-load this skill.
Integration guide for using the Quartz Nostr KMP library in external projects. Use when: (1) adding Quartz as a Gradle dependency, (2) setting up NostrClient with WebSocket, (3) creating/signing/sending events, (4) building relay subscriptions with Filter, (5) handling keys with
name: quartz-integration description: Integration guide for using the Quartz Nostr KMP library in external projects. Use when: (1) adding Quartz as a Gradle dependency, (2) setting up NostrClient with WebSocket, (3) creating/signing/sending events, (4) building relay subscriptions with Filter, (5) handling keys with KeyPair/NostrSignerInternal, (6) using Bech32 encoding/decoding (NIP-19), (7) platform-specific setup (Android vs JVM/Desktop), (8) NIP-57 zaps, NIP-17 DMs, NIP-44 encryption in external projects, (9) running a relay on Quartz and serving/building its NIP-11 relay information document (application/nostr+json).
Reference for integrating `com.vitorpamplona.quartz:quartz` into external Nostr KMP projects.
**Published artifact**: `com.vitorpamplona.quartz:quartz:1.15.2` (Maven Central) **Targets**: JVM 21+, Android (minSdk 21+), iOS (XCFramework `quartz-kmpKit`) **License**: MIT
---
[versions]
quartz = "1.15.2"
[libraries]
quartz = { module = "com.vitorpamplona.quartz:quartz", version.ref = "quartz" }kotlin {
sourceSets {
commonMain.dependencies {
implementation(libs.quartz)
}
}
}dependencies {
implementation("com.vitorpamplona.quartz:quartz:1.15.2")
}Quartz exposes these as `api` (you get them transitively):
| Dependency | Used for | |-----------|----------| | `fr.acinq.secp256k1:secp256k1-kmp-*` | Schnorr signing | | `com.github.anthonynsimon:rfc3986-normalizer` | Relay URL normalization | | `com.fasterxml.jackson.module:jackson-module-kotlin` | Event JSON parsing |
For Android, add to `build.gradle.kts`:
android {
packaging {
resources.excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}---
typealias HexKey = String // 64-char hex string (pubkey, event id, sig) typealias Kind = Int // Event kind number typealias TagArray = Array<Array<String>>
@Immutable
open class Event(
val id: HexKey, // SHA-256 of canonical JSON (64 hex chars)
val pubKey: HexKey, // Author public key (64 hex chars)
val createdAt: Long, // Unix timestamp (seconds)
val kind: Kind, // Event type
val tags: TagArray, // [["e","eventid"], ["p","pubkey"], ...]
val content: String,
val sig: HexKey, // Schnorr signature (128 hex chars)
)// Regular events — stored by relays forever val isRegular = kind in 1..9999 // Replaceable events — relay keeps only latest per (pubkey, kind) val isReplaceable = kind == 0 || kind == 3 || kind in 10000..19999 // Addressable events — relay keeps latest per (pubkey, kind, d-tag) val isAddressable = kind in 30000..39999 // Ephemeral events — relays don't persist val isEphemeral = kind in 20000..29999
---
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair // Generate fresh random keys val keyPair = KeyPair() // From existing private key bytes val keyPair = KeyPair(privKey = myPrivKeyBytes) // Read-only (public key only, cannot sign) val keyPair = KeyPair(pubKey = myPubKeyBytes) // Access val pubKeyHex: String = keyPair.pubKey.toHexKey() val privKeyHex: String? = keyPair.privKey?.toHexKey()
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser
// ByteArray → hex
val hex = byteArray.toHexKey()
// hex → ByteArray
val bytes = hex.hexToByteArray()
// Bech32 import (npub, nsec)
val parsed = Nip19Parser.uriToRoute("npub1abc...")
// or
val parsed = Nip19Parser.uriToRoute("nsec1abc...")> Hex ↔ ByteArray is a first-class utility in Quartz — see **§3.1 Hex utilities** below.
---
Nostr keys, event ids and signatures travel as lower-case hex strings. Quartz models this with the `HexKey` typealias (just a `String`) plus extension functions — **do not** write your own byte loop or pull in a third-party codec.
**Packages:** `com.vitorpamplona.quartz.nip01Core.core` (the extensions) and `com.vitorpamplona.quartz.utils` (the underlying `Hex` object).
import com.vitorpamplona.quartz.nip01Core.core.HexKey // typealias = String import com.vitorpamplona.quartz.nip01Core.core.toHexKey // ByteArray → hex import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray // hex → ByteArray import com.vitorpamplona.quartz.nip01Core.core.hexToByteArrayOrNull import com.vitorpamplona.quartz.nip01Core.core.isValid import com.vitorpamplona.quartz.utils.Hex // Encode / decode val hex: HexKey = pubKeyBytes.toHexKey() // lower-case, 2 chars per byte val bytes: ByteArray = hex.hexToByteArray() // throws on odd length // Untrusted input → decode safely val maybe: ByteArray? = userInput.hexToByteArrayOrNull() // null if not valid hex // Validate without decoding (no allocation) Hex.isHex(userInput) // even-length, all hex digits (any length) Hex.isHex64(userInput) // fast path for a 32-byte key/id (checks first 64 chars) hex.isValid() // 64 chars AND valid hex (pubkey / event-id shape) // Compare a hex string to raw bytes without decoding Hex.isEqual(incomingHexId, myIdBytes)
| Need | Call | Notes | |------|------|-------| | ByteArray → hex | `bytes.toHexKey()` | lower-case output | | hex → ByteArray (strict) | `hex.hexToByteArray()` | throws on odd length | | hex → ByteArray (safe) | `hex.hexToByteArrayOrNull()` | `null` on invalid hex | | is this valid hex? | `Hex.isHex(s)` / `Hex.isHex64(s)` | `isHex64` ~30% faster for keys/ids | | i
Account state and in-memory event store patterns in Amethyst. Use when working with `Account.kt` (per-user state objects — `kind3FollowList`, `nip65RelayList`,…
Patterns for extending `amy`, the Amethyst CLI in `cli/`. Use when adding an `amy <verb>` command, touching files under `cli/src/main/kotlin/…/cli/`, wiring a…
Android platform patterns for the `amethyst/` module. Use when working with (1) Android navigation (Navigation Compose, type-safe routes, bottom nav), (2)…
Signer abstraction patterns in Amethyst. Use when working with event signing, choosing between a local keypair (`NostrSignerInternal`), a remote NIP-46 bunker…
Advanced Compose Multiplatform UI patterns for shared composables. Use when working with visual UI components, state management patterns (remember,…
Use when writing or reviewing Jetpack Compose layout APIs, modifier parameters, modifier chain construction, hardcoded root layout decisions, or layout…