Files
amethyst/.claude/skills/quartz-kmp.md
T
nrobi144 82f8bc62a0 feat: Add Compose Multiplatform Desktop support foundation
- Add desktopApp module with JVM entry point and sidebar navigation
- Add Claude specs for AI-assisted development:
  - Agent definitions: nostr-protocol, kotlin-multiplatform, compose-ui, kotlin-coroutines
  - Skills: quartz-kmp conversion, compose-desktop patterns
  - Commands: desktop-run, nip, extract
- Update Gradle configuration with Compose Multiplatform 1.7.1 plugin
- Add coroutines and secp256k1 JVM dependencies to version catalog

Next steps:
- Convert Quartz library to full KMP (expect/actual for crypto)
- Implement relay connections in desktop app
- Share UI components between Android and Desktop

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 08:21:02 +02:00

4.0 KiB

Quartz KMP Conversion Skill

When working with Quartz library conversion to Kotlin Multiplatform:

Current Structure (Android-only)

quartz/
├── build.gradle
└── src/
    ├── main/kotlin/          # All Nostr code here
    ├── test/
    └── androidTest/

Target Structure (KMP)

quartz/
├── build.gradle.kts
└── src/
    ├── commonMain/kotlin/    # Shared protocol code
    ├── commonTest/kotlin/    # Shared tests
    ├── androidMain/kotlin/   # Android crypto, storage
    ├── androidTest/kotlin/
    ├── jvmMain/kotlin/       # Desktop crypto, storage
    └── jvmTest/kotlin/

Platform Abstractions Required

1. Cryptography (expect/actual)

// commonMain
expect object Secp256k1 {
    fun sign(data: ByteArray, privateKey: ByteArray): ByteArray
    fun verify(data: ByteArray, signature: ByteArray, pubKey: ByteArray): Boolean
    fun pubKeyCreate(privateKey: ByteArray): ByteArray
}

// androidMain - uses secp256k1-kmp-jni-android
actual object Secp256k1 {
    actual fun sign(data: ByteArray, privateKey: ByteArray): ByteArray {
        return fr.acinq.secp256k1.Secp256k1.sign(data, privateKey)
    }
    // ...
}

// jvmMain - uses secp256k1-kmp-jni-jvm
actual object Secp256k1 {
    actual fun sign(data: ByteArray, privateKey: ByteArray): ByteArray {
        return fr.acinq.secp256k1.Secp256k1.sign(data, privateKey)
    }
    // ...
}

2. NIP-44 Encryption (Sodium)

// commonMain
expect object Nip44 {
    fun encrypt(plaintext: String, sharedSecret: ByteArray): String
    fun decrypt(ciphertext: String, sharedSecret: ByteArray): String
}

// androidMain - lazysodium-android
// jvmMain - lazysodium-java or libsodium-jni

3. Secure Random

// commonMain
expect fun secureRandomBytes(size: Int): ByteArray

// androidMain
actual fun secureRandomBytes(size: Int): ByteArray {
    return SecureRandom().let { random ->
        ByteArray(size).also { random.nextBytes(it) }
    }
}

// jvmMain
actual fun secureRandomBytes(size: Int): ByteArray {
    return java.security.SecureRandom().let { random ->
        ByteArray(size).also { random.nextBytes(it) }
    }
}

Build Configuration

// quartz/build.gradle.kts
plugins {
    kotlin("multiplatform")
    id("com.android.library")
}

kotlin {
    androidTarget {
        compilations.all {
            kotlinOptions.jvmTarget = "17"
        }
    }

    jvm("desktop") {
        compilations.all {
            kotlinOptions.jvmTarget = "17"
        }
    }

    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation(libs.kotlinx.coroutines.core)
                implementation(libs.kotlinx.collections.immutable)
            }
        }

        val androidMain by getting {
            dependencies {
                implementation(libs.secp256k1.kmp.jni.android)
                implementation(libs.lazysodium.android)
            }
        }

        val desktopMain by getting {
            dependencies {
                implementation(libs.secp256k1.kmp.jni.jvm)
                // lazysodium-java or alternative
            }
        }
    }
}

android {
    namespace = "com.vitorpamplona.quartz"
    compileSdk = 35
    defaultConfig.minSdk = 26
}

Migration Steps

  1. Convert build.gradle to build.gradle.kts with KMP plugin
  2. Move pure Kotlin code to commonMain/
  3. Identify platform dependencies (crypto, JNA, Android APIs)
  4. Create expect declarations for platform-specific APIs
  5. Implement actuals in androidMain and jvmMain
  6. Update imports in amethyst module
  7. Test on both platforms

Files to Move to commonMain

Most of Quartz can be shared:

  • Event classes and parsing
  • Filter definitions
  • Relay message types
  • NIP implementations (logic only)
  • Utilities (hex encoding, bech32)

Files Needing expect/actual

  • Secp256k1.kt - Signature operations
  • Nip04.kt - Legacy encryption (uses AES)
  • Nip44.kt - Modern encryption (uses ChaCha)
  • KeyPair.kt - Key generation