Kotlin Multiplatform SDK for idOS - Android, iOS, and JVM support.
- Kotlin: 2.2.10+
- Android: API 28+ (Android 9.0 Pie)
- iOS: 15.0+
- JVM: Java 11+
Add the dependency to your build.gradle.kts:
dependencies {
implementation("org.idos:idos-sdk-kotlin:0.0.8")
}Or in Groovy build.gradle:
dependencies {
implementation 'org.idos:idos-sdk-kotlin:0.0.8'
}Add the package dependency in Xcode:
- Go to File → Add Package Dependencies...
- Enter the repository URL:
https://github.com/idos-network/idos-sdk-kotlin - Select version
0.0.8or specify a version rule - Add to your target
Or add to your Package.swift:
dependencies: [
.package(url: "https://github.com/idos-network/idos-sdk-kotlin", from: "0.0.8")
]Download the latest release artifacts:
- Android: Get the
.aarfile from GitHub Releases - iOS: Get the
idos_sdk.xcframework.zipfrom GitHub Releases
import org.idos.kwil.security.signer.JvmEthSigner
import org.kethereum.bip32.toKey
import org.kethereum.bip39.model.MnemonicWords
import org.kethereum.bip39.toSeed
val mnemonic = MnemonicWords("your twelve or twenty four word mnemonic phrase")
val seed = mnemonic.toSeed("")
val key = seed.toKey("m/44'/60'/0'/0/0") // Ethereum derivation path
val signer = JvmEthSigner(key.keyPair)import org.idos.IdosClient
try {
val client = IdosClient.create(
baseUrl = "https://nodes.staging.idos.network",
chainId = "idos-testnet",
signer = signer
)
} catch (e: DomainError) {
println("Failed to create client: ${e.message}")
}try {
// User operations
val user = client.users.get()
println("User: ${user.id}")
// Wallet operations
val wallets = client.wallets.getAll()
println("Found ${wallets.size} wallets")
val txHash = client.wallets.add(AddWalletParams(id, address, publicKey, signature))
println("Wallet added: $txHash")
// Credential operations
val credentials = client.credentials.getAll()
credentials.forEach { println(it.publicNotes) }
// Access grant operations
val grants = client.accessGrants.getOwned()
println("${grants.size} grants")
} catch (e: DomainError) {
when (e) {
is DomainError.ValidationError -> println("Invalid input: ${e.message}")
is DomainError.AuthenticationRequired -> println("Auth required: ${e.message}")
is DomainError.NotFound -> println("Not found: ${e.message}")
else -> println("Error: ${e.message}")
}
}All operations are suspend functions that throw DomainError on failure.
suspend fun add(input: AddWalletParams): HexString- Add a new walletsuspend fun getAll(): List<GetWalletsResponse>- Get all walletssuspend fun remove(id: UuidString): HexString- Remove a wallet
suspend fun add(input: AddCredentialParams): HexString- Add a credentialsuspend fun getAll(): List<GetCredentialsResponse>- Get all owned credentialssuspend fun getOwned(id: UuidString): GetCredentialOwnedResponse- Get specific owned credentialsuspend fun getShared(id: UuidString): List<GetCredentialSharedResponse>- Get shared credentialssuspend fun edit(input: EditCredentialParams): HexString- Edit a credentialsuspend fun remove(id: UuidString): HexString- Remove a credentialsuspend fun share(input: ShareCredentialParams): HexString- Share a credential
suspend fun create(input: CreateAccessGrantParams): HexString- Create an access grantsuspend fun getOwned(): List<GetAccessGrantsOwnedResponse>- Get owned grantssuspend fun getGranted(userId, page, size): List<GetAccessGrantsGrantedResponse>- Get granted grantssuspend fun getForCredential(credentialId): List<GetAccessGrantsForCredentialResponse>- Get grants for credentialsuspend fun revoke(id: UuidString): HexString- Revoke an access grant
suspend fun get(): GetUserResponse- Get current user profilesuspend fun hasProfile(address: HexString): Boolean- Check if address has profile
suspend fun add(input: AddAttributeParams): HexString- Add an attributesuspend fun getAll(): List<GetAttributesResponse>- Get all attributessuspend fun edit(input: EditAttributeParams): HexString- Edit an attributesuspend fun remove(id: UuidString): HexString- Remove an attributesuspend fun share(input: ShareAttributeParams): HexString- Share an attribute
The SDK supports two encryption modes:
- LOCAL: Password-based encryption with local key storage
- MPC: Distributed encryption using Shamir's Secret Sharing across MPC nodes
The default EnclaveOrchestrator.create() supports both modes with type detection based on user enrollment. Use createLocal() or createMpc() for single-mode apps.
import org.idos.enclave.*
// Create orchestrator supporting BOTH modes (default)
val encryption = JvmEncryption() // or AndroidEncryption(context), IosEncryption()
val storage = JvmMetadataStorage() // or AndroidMetadataStorage(context), IosMetadataStorage()
val orchestrator = EnclaveOrchestrator.create(
encryption = encryption,
storage = storage,
mpcConfig = mpcConfig,
signer = signer,
hasher = Keccak256Hasher()
)
// Initialize type based on user's choice
orchestrator.initializeType(EnclaveKeyType.USER) // or EnclaveKeyType.MPC
// Observe state for UI updates
orchestrator.state.collect { state ->
when (state) {
is EnclaveState.Locked -> showPasswordPrompt()
is EnclaveState.Unlocking -> showLoadingIndicator()
is EnclaveState.Unlocked -> enableEncryptedFeatures()
is EnclaveState.NotAvailable -> showSetupScreen()
}
}
// Unlock (password required for LOCAL, optional for MPC)
val sessionConfig = EnclaveSessionConfig(ExpirationType.TIMED, 3600000) // 1 hour
orchestrator.unlock(userId, sessionConfig, password)
// Decrypt credential data
try {
orchestrator.withEnclave { enclave ->
val decryptedData = enclave.decrypt(
message = credential.content.toByteArray(),
senderPublicKey = credential.encryptorPublicKey.toByteArray()
)
println(String(decryptedData))
}
} catch (e: EnclaveError) {
when (e) {
is EnclaveError.NoKey -> println("Unlock enclave first")
is EnclaveError.KeyExpired -> println("Key expired, unlock again")
is EnclaveError.DecryptionFailed -> println("Decryption failed: ${e.reason}")
else -> println("Error: ${e.message}")
}
}For real-world usage patterns handling enclave state in a ViewModel, see CredentialDetailViewModel.kt:103-138
-
Copy the example environment file:
cp .env.example .env
-
Edit
.envwith your test credentials:MNEMONIC_WORDS=your twelve or twenty four word mnemonic phrase PASSWORD=your-password -
Run tests:
./gradlew allTest ./gradlew connectedCheck xcodebuild test -project iosApp.xcodeproj -scheme iosApp -destination 'YOUR_DEVICE'
Note: Your mnemonic should be for a wallet that has an idOS profile for integration tests to work.
See ARCHITECTURE.md for detailed documentation on:
- 4-layer architecture (Transport → Protocol → Domain → Public API)
- Package structure and responsibilities
- KWIL protocol implementation details
- Transaction signing scheme
- Error handling strategy
- Platform-specific implementations
All public APIs use suspend functions that throw DomainError on failure. Wrap calls in try-catch for error handling:
try {
val txHash = client.wallets.add(walletParams)
println("Success: $txHash")
} catch (e: DomainError) {
when (e) {
is DomainError.ValidationError -> println("Invalid input: ${e.message}")
is DomainError.AuthenticationRequired -> println("Auth failed: ${e.message}")
is DomainError.NotFound -> println("Not found: ${e.message}")
is DomainError.ActionFailed -> println("Action failed: ${e.message}")
else -> println("Error: ${e.message}")
}
}
// Clean, direct API - no Result wrapping needed
try {
val user = client.users.get()
println("User ID: ${user.id}")
} catch (e: DomainError) {
println("Failed to get user: ${e.message}")
}- JVM: Full support with KEthereum for Ethereum signing
- Android: Same as JVM, plus libsodium via Lazysodium for encryption, EncryptedFile with StrongBox for secure storage
- iOS: Darwin HTTP engine, libsodium XCFramework for encryption, iOS Keychain for secure storage
- SKIE library auto-converts Kotlin suspend/throws to Swift async/throws
- Seamless Swift interop with native error handling
- ARCHITECTURE.md - Detailed architecture documentation
- PUBLISHING.md - Complete guide for publishing releases
- KWIL Protocol - KWIL database documentation
- idOS Schema - Schema definitions
For maintainers publishing new releases, see PUBLISHING.md for:
- Initial setup (Maven Central, GPG keys, GitHub secrets)
- Creating releases (automated via GitHub Actions)
- Build tasks and troubleshooting
- Distribution to Maven Central, GitHub Releases, and Swift Package Manager
See LICENSE file for details.