Scaffold :shared KMP module and :desktop Compose app for bunker signing

Adds a Kotlin Multiplatform :shared module (androidTarget + jvm("desktop"))
depending on the multiplatform com.vitorpamplona.quartz:quartz coordinate
(which resolves quartz-android for Android and quartz-jvm for desktop via
Gradle variant-aware resolution), plus a new :desktop module scaffolded
with Compose Multiplatform Desktop and packaging config for
deb/msi/dmg installers. The :app module is untouched and still builds.
This commit is contained in:
Claude
2026-07-01 17:45:08 +00:00
parent f58d26ab98
commit 438a782cb1
7 changed files with 137 additions and 0 deletions
+5
View File
@@ -7,6 +7,11 @@ plugins {
alias(libs.plugins.serialization) apply false
alias(libs.plugins.kotlin.ksp) version libs.versions.ksp.get() apply false
alias(libs.plugins.gradle.ktlint) version libs.versions.ktlint.get() apply false
alias(libs.plugins.kotlinMultiplatform) apply false
alias(libs.plugins.jetbrainsCompose) apply false
alias(libs.plugins.jetbrainsComposeCompiler) apply false
alias(libs.plugins.androidKotlinMultiplatformLibrary) apply false
alias(libs.plugins.kotlinJvm) apply false
}
tasks.register<Copy>("installGitHook") {
+40
View File
@@ -0,0 +1,40 @@
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
plugins {
alias(libs.plugins.kotlinJvm)
alias(libs.plugins.jetbrainsCompose)
alias(libs.plugins.jetbrainsComposeCompiler)
alias(libs.plugins.gradle.ktlint) version libs.versions.ktlint.get()
}
kotlin {
jvmToolchain(21)
}
dependencies {
implementation(project(":shared"))
implementation(compose.desktop.currentOs)
implementation(compose.material3)
implementation(compose.materialIconsExtended)
implementation(libs.quartz.multiplatform)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.core) // zxing core, for bunker connection QR codes
implementation(libs.java.keyring)
implementation(libs.androidx.sqlite.bundled)
testImplementation(kotlin("test"))
}
compose.desktop {
application {
mainClass = "com.greenart7c3.nostrsigner.desktop.MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "Amber Bunker"
packageVersion = "1.0.0"
}
}
}
@@ -0,0 +1,23 @@
package com.greenart7c3.nostrsigner.desktop
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
fun main() = application {
Window(onCloseRequest = ::exitApplication, title = "Amber Bunker") {
App()
}
}
@Composable
fun App() {
MaterialTheme {
Surface {
Text("Amber Bunker Desktop")
}
}
}
+11
View File
@@ -33,6 +33,9 @@ coroutinesTest = "1.11.0"
kmpTor = "2.6.0"
kmpTorResource = "409.5.0"
secp256k1Jni = "0.23.0"
composeMultiplatform = "1.11.1"
androidxSqliteBundled = "2.6.2"
javaKeyring = "1.0.4"
[libraries]
datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
@@ -53,6 +56,7 @@ material-icons-extended = { module = "androidx.compose.material:material-icons-e
material3 = { module = "androidx.compose.material3:material3", version.ref = "material3" }
navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "nav_version" }
quartz = { module = "com.vitorpamplona.quartz:quartz-android", version.ref = "quartz" }
quartz-multiplatform = { module = "com.vitorpamplona.quartz:quartz", version.ref = "quartz" }
kotlinx-collections-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "collections" }
room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomKtx" }
room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomKtx" }
@@ -79,15 +83,22 @@ paging-common = { group = "androidx.paging", name = "paging-common", version.ref
paging-compose = { group = "androidx.paging", name = "paging-compose", version.ref = "pagingCommon" }
paging-runtime = { group = "androidx.paging", name = "paging-runtime", version.ref = "pagingCommon" }
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutinesTest" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutinesTest" }
kmptor-runtime = { module = "io.matthewnelson.kmp-tor:runtime", version.ref = "kmpTor" }
kmptor-resource-exec = { module = "io.matthewnelson.kmp-tor:resource-exec-tor", version.ref = "kmpTorResource" }
secp256k1-jni-jvm = { module = "fr.acinq.secp256k1:secp256k1-kmp-jni-jvm", version.ref = "secp256k1Jni" }
androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "androidxSqliteBundled" }
java-keyring = { module = "com.github.javakeyring:java-keyring", version.ref = "javaKeyring" }
[plugins]
androidLibrary = { id = "com.android.library", version.ref = "agp" }
androidKotlinMultiplatformLibrary = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" }
androidApplication = { id = "com.android.application", version.ref = "agp" }
gradle_ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "ktlint" }
serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
kotlin_ksp = { id = "com.google.devtools.ksp", version = "ksp" }
jetbrainsComposeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" }
+2
View File
@@ -17,3 +17,5 @@ dependencyResolutionManagement {
}
rootProject.name = "Nostr Signer"
include(":app")
include(":shared")
include(":desktop")
+37
View File
@@ -0,0 +1,37 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidKotlinMultiplatformLibrary)
alias(libs.plugins.gradle.ktlint) version libs.versions.ktlint.get()
}
kotlin {
android {
namespace = "com.greenart7c3.nostrsigner.shared"
compileSdk = 37
minSdk = 26
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
}
withHostTest {}
}
jvm("desktop") {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
}
}
sourceSets {
commonMain.dependencies {
implementation(libs.quartz.multiplatform)
implementation(libs.kotlinx.coroutines.core)
}
commonTest.dependencies {
implementation(kotlin("test"))
implementation(libs.kotlinx.coroutines.test)
}
}
}
@@ -0,0 +1,19 @@
package com.greenart7c3.nostrsigner.shared
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
/** Thin, platform-agnostic wrapper around a Quartz [NostrSignerInternal] used by the bunker signing engine. */
class BunkerSigner(keyPair: KeyPair) {
private val signer = NostrSignerInternal(keyPair)
val pubKey: String get() = signer.pubKey
suspend fun nip44Encrypt(plainText: String, toPublicKey: String): String = signer.nip44Encrypt(plainText, toPublicKey)
suspend fun nip44Decrypt(cipherText: String, fromPublicKey: String): String = signer.nip44Decrypt(cipherText, fromPublicKey)
suspend fun nip04Encrypt(plainText: String, toPublicKey: String): String = signer.nip04Encrypt(plainText, toPublicKey)
suspend fun nip04Decrypt(cipherText: String, fromPublicKey: String): String = signer.nip04Decrypt(cipherText, fromPublicKey)
}