Engine

Patches

Patches are Kotlin. Sources live under apps/<app>/patch/src/main/kotlin/. Every patch is a top-level val built with the patch(...) DSL; the engine discovers them from the compiled JAR, not by file path.

Shape of a patch

import app.reseam.patch.boolOption
import app.reseam.patch.compatibleWith
import app.reseam.patch.optionsOf
import app.reseam.patch.patch
import app.reseam.patch.stringOption

val myPatch = patch(
    name = "My patch",
    description = "Does something useful.",
    compatibleWith = listOf(compatibleWith("com.example.app")),
    enabledByDefault = true,
    options = optionsOf(
        stringOption("some_string", default = "default_value"),
        boolOption("some_bool", default = true),
    ),
) {
    execute { ctx ->
        ctx.log.info("Patch executed")
    }
}

Metadata parameters

  • name: human-readable label.
  • description: one line.
  • compatibleWith: packages and optional versions. Patches whose target package or version doesn't match the APK are skipped. compatibleWith("com.example.app", "1.0", "1.1") restricts to specific versions.
  • dependsOn: other patches in the same bundle that must run first. Accepts ReseamPatch references or name strings.
  • enabledByDefault: whether the patch is on unless the user disables it. Defaults to true.
  • options: user-configurable parameters. See below.

Lifecycle

execute { ctx -> } runs once per target APK.

afterDependents { ctx -> } runs after every patch that depends on this one has finished. Most patches don't need it.

An uncaught exception marks the patch as failed; other patches keep running. When partial success is acceptable, prefer ctx.log.warn(...) and early return over throwing.

Options

import app.reseam.patch.boolOption
import app.reseam.patch.optionsOf
import app.reseam.patch.pathOption
import app.reseam.patch.stringOption

val configurablePatch = patch(
    name = "Configurable patch",
    description = "Reads user-supplied values at patch time.",
    compatibleWith = listOf(compatibleWith("com.example.app")),
    options = optionsOf(
        stringOption("api_endpoint", default = "api.example.com"),
        boolOption("enable_logging", default = false),
        pathOption("assets_dir"),
    ),
) {
    execute { ctx ->
        val endpoint = ctx.options.string("api_endpoint") ?: "default.com"
        val logging = ctx.options.bool("enable_logging") ?: false

        ctx.options.path("assets_dir")?.let { dir ->
            val entries = dir.listContents()
            val data = dir.readFile("logo.png")
        }
    }
}

Declarations: stringOption, boolOption, intOption, floatOption, stringListOption, pathOption. Read values with ctx.options.string(key), .bool, .int, .float, .stringList, .path. Each returns null when unset.

pathOption returns a PathOptionValue that exposes listContents() and readFile(relativePath) for reading from a user-selected directory.

Dependencies

Declare a dependency when one patch needs another to have run first:

val runtimeHost = patch(/* ... */) { /* ... */ }

val disableAutoplay = patch(
    name = "Disable autoplay",
    description = "Stops videos from auto-playing.",
    compatibleWith = listOf(compatibleWith("com.example.app")),
    dependsOn = listOf(runtimeHost),
) {
    execute { ctx ->
        // runtimeHost has already applied
    }
}

Disabling a dependency skips its dependents too.

Next: the runtime surface.

Reseam Reseam © 2026 Reseam Team