Skip to content

ripstop-dev/ripstop-kotlin

Repository files navigation

Ripstop

Ripstop Kotlin SDK

CI License: MIT

Remote config, update walls and maintenance mode for Android. Decided at the edge, verified on device.

Installation

Not on Maven Central yet. Until dev.ripstop:ripstop is published, include the repo as a composite build:

// settings.gradle.kts
includeBuild("../ripstop-kotlin")
// build.gradle.kts
dependencies {
    implementation("dev.ripstop:ripstop")
}

Gradle substitutes the coordinates with the local build. Copying src/ in as a source module works too. It is one module with a single ~100 KB dependency.

Quick Start

// Off the main thread, because init performs the first check.
val ripstop = Ripstop.init(
    apiKey = "rs_pub_your_key",
    appVersion = BuildConfig.VERSION_NAME,
    storage = SharedPrefsStorage(context),   // four lines, below
)

when (val decision = ripstop.check()) {
    is RsDecision.ForceUpdate -> showBlockingWall(decision)
    is RsDecision.SoftUpdate  -> showDismissibleSheet(decision)
    is RsDecision.Maintenance -> showMaintenance(decision)
    RsDecision.None           -> Unit   // carry on
}

The when is exhaustive: if the protocol grows a decision, your app stops compiling instead of silently showing nobody anything.

Why you still pass appVersion

The Swift and Flutter SDKs read the installed version themselves. Doing it on Android means PackageManager.getPackageInfo(context.packageName, 0), and this library takes no Context. That is the same reason it ships no SharedPreferences storage, and what keeps it usable from plain JVM code and from tests.

You lose nothing by passing it. BuildConfig.VERSION_NAME is generated by the Android Gradle plugin from your module's versionName, so it is the same string PackageManager would return. Nobody types it, and it cannot drift from the build it ships in. What you must not pass is a literal.

Pass the version only: 4.1.0, not 4.1.0 (312). versionCode is not part of a semantic version, and the rules are compared as semantic versions. Anything unreadable is treated as no opinion, so a mistake here lets the app run rather than walling it.

Storage

The library does not depend on Android, so it cannot ship a SharedPreferences implementation, and in exchange it stays usable from plain JVM code and from tests. Wiring one up is four lines:

class SharedPrefsStorage(context: Context) : RipstopStorage {
    private val prefs = context.getSharedPreferences("ripstop", Context.MODE_PRIVATE)
    override fun read(key: String) = prefs.getString(key, null)
    override fun write(key: String, value: String) { prefs.edit().putString(key, value).apply() }
    override fun remove(key: String) { prefs.edit().remove(key).apply() }
}

Remote config

val enabled = ripstop.values["checkout_enabled"] as? Boolean ?: true

What it does when things break

Situation What happens
No network The last signed payload drives the decision
No network, no cache RsDecision.None, so your app runs unrestricted
Server returns 500, or times out Cache, then None
Signature doesn't verify Treated as a failure. A forged payload can never block your app
Cache edited on device Re-verified on read, so it grants nothing
Maintenance on, then network lost The wall stays, until a fresh signed payload clears it

Why not JCA for Ed25519

The JVM has had Ed25519 in the standard library since Java 15, but Android does not expose it below API 33. An SDK built on JCA would silently stop verifying signatures on most of the installed base. That is the worst kind of failure, because it looks like it works. This uses net.i2p.crypto:eddsa (~100 KB, pure Java).

Conformance

Every Ripstop SDK runs the same vectors.json: version ordering, evaluation order, snooze accounting, the fail-open state machine. ./gradlew test runs it here, alongside tests that sign with a real key pair.

Full docs: ripstop.dev/docs/kotlin

License

MIT

About

Ripstop SDK for Kotlin. Required updates, update nudges, maintenance mode and remote config.

Topics

Resources

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages