Secrets storage,
done right for KMP.
A coroutine-first Kotlin Multiplatform library that wraps native secure storage — EncryptedSharedPreferences on Android and Keychain Services on iOS — behind one tiny, suspendable API. No hand-rolled crypto.
dependencies { implementation("io.github.alims-repo:secure-vault:0.3.0") // Optional — Compose Multiplatform helpers: implementation("io.github.alims-repo:secure-vault-compose:0.3.0") }
# gradle/libs.versions.toml [libraries] secure-vault = { module = "io.github.alims-repo:secure-vault", version = "0.3.0" } secure-vault-compose = { module = "io.github.alims-repo:secure-vault-compose", version = "0.3.0" }
dependencies { implementation 'io.github.alims-repo:secure-vault:0.3.0' implementation 'io.github.alims-repo:secure-vault-compose:0.3.0' }
Designed like a library you'd actually want to depend on.
Small surface, sealed errors, suspending I/O, explicit visibility — every public symbol earns its place.
OS-native crypto
No bespoke ciphers. AES-256-GCM via the Android Keystore on one side, Keychain Services on the other — you inherit OS guarantees, not custom risk.
Coroutine-first
Every operation is suspend and dispatches onto the right I/O dispatcher per platform. No manual withContext, no blocked main threads.
Sealed errors
One VaultException hierarchy: InvalidKey, CryptoFailure, Tampered, StorageUnavailable. Catch what matters, ignore platform noise.
Namespaced
Vaults are scoped per VaultConfig.namespace. Isolate auth tokens from feature flags, or one product from another in the same app.
Tiny API
Six suspending methods total: put, get, remove, contains, clear, keys. That's the whole library.
ABI-stable
Public surface pinned with binary-compatibility-validator. Patch releases never break consumers — by construction.
One API. Two native backends.
Write your business logic against SecureVault in commonMain. One factory call, identical on every target.
class AuthRepository( private val vault: SecureVault, ) { suspend fun saveSession(token: String) = vault.put("session", token) suspend fun session(): String? = vault.get("session") suspend fun logout() = vault.remove("session") }
class App : Application() { override fun onCreate() { super.onCreate() // no Context plumbing — captured via androidx.startup val vault = SecureVault("com.acme.auth") } }
let vault = SecureVaultsKt.SecureVault( namespace: "com.acme.auth", accessibility: .afterFirstUnlock )
Six methods. That's the whole contract.
All methods are suspend and may throw VaultException. Full KDoc shipped in the artefact's sources jar.
| Signature | Description |
|---|---|
| put(key, value) | Store a value, overwriting any previous one. |
| get(key) | Return the value for key, or null if absent. |
| remove(key) | Delete the entry. Idempotent — no-op if the key isn't present. |
| contains(key) | Return true if a value is currently stored under key. |
| clear() | Remove every entry inside this vault's namespace. |
| keys() | Snapshot of the current key set in this namespace. |
Where it runs.
More targets (JVM/Desktop, watchOS) are planned for v0.2 — see the changelog for the roadmap.
| Target | Backend |
|---|---|
| android | EncryptedSharedPreferences (Android Keystore) |
| iosArm64 | Keychain Services (kSecClassGenericPassword) |
| iosSimulatorArm64 | Keychain Services (kSecClassGenericPassword) |