
Enables interaction with identity network nodes: wallet, credential, attribute and access-grant management, transaction signing, plus encrypted key storage with password-based LOCAL or Shamir‑based MPC orchestration.
Kotlin Multiplatform SDK for idOS - Android, iOS, and JVM support.
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:
https://github.com/idos-network/idos-sdk-kotlin
0.0.8 or specify a version ruleOr 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:
.aar file from GitHub Releases
idos_sdk.xcframework.zip from 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 walletsuspend 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 credentialsuspend 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 grantsuspend fun get(): GetUserResponse - Get current user profilesuspend fun hasProfile(address: HexString): Boolean - Check if address has profilesuspend 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 attributeThe SDK supports two encryption modes:
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 .envEdit .env with 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:
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}")
}For maintainers publishing new releases, see PUBLISHING.md for:
See LICENSE file for details.
Kotlin Multiplatform SDK for idOS - Android, iOS, and JVM support.
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:
https://github.com/idos-network/idos-sdk-kotlin
0.0.8 or specify a version ruleOr 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:
.aar file from GitHub Releases
idos_sdk.xcframework.zip from 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 walletsuspend 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 credentialsuspend 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 grantsuspend fun get(): GetUserResponse - Get current user profilesuspend fun hasProfile(address: HexString): Boolean - Check if address has profilesuspend 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 attributeThe SDK supports two encryption modes:
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 .envEdit .env with 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:
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}")
}For maintainers publishing new releases, see PUBLISHING.md for:
See LICENSE file for details.