Storage SPI

Underneath the public API sits a minimal interface that makes the storage engine swappable — this is how it's built, not something a consumer of the library implements or configures directly.

internal interface Storage {
    fun getBytes(key: String): ByteArray?
    fun putBytes(key: String, value: ByteArray)
    fun remove(key: String)
    fun contains(key: String): Boolean
    fun keys(prefix: String): List<String>
}

Every decomposed field key (see Field Decomposition) is read and written through exactly these five operations. Two implementations exist:

Everything above this interface — the public API, field decomposition, serialization, reactivity — lives in commonMain and has no idea which Storage implementation it's talking to. Only the concrete implementation is platform-specific, which is what keeps the entire public surface KMP-clean. See Shared KMP Persistence and Platform Support.