Rust · TypeScript · Kotlin

Health checks, declared not improvised

A tiny DSL for service readiness/liveness checks — run concurrently, aggregated correctly, serialized anywhere.

Open source · MIT license · Rust · TypeScript · Kotlin

Health.kt
val health = healthChecks {
    check("database", critical = true) {
        if (db.ping()) up() else down("primary unreachable")
    }
    check("cache") { up("hitRate" to "0.93") }
    check("disk", timeout = 2.seconds) {
        val free = freePercent()
        when {
            free < 5  -> down("disk almost full: $free%")
            free < 15 -> degraded("disk low: $free%")
            else      -> up("freePercent" to free.toString())
        }
    }
}

val report = health.run()   // all checks run concurrently
report.status               // UP | DEGRADED | DOWN
report.toJson()
What it is

health-dsl turns your ad-hoc /health endpoint into a declaration: declare checks, run them concurrently with per-check timeouts, aggregate a correct overall status (critical-down vs degraded), and serialize to JSON. Native in Rust, TypeScript, and Kotlin.

Most services grow an ad-hoc /health endpoint: a pile of try/catch blocks, inconsistent timeouts, one slow dependency that hangs the whole probe, and no clear distinction between "we are down" and "we are degraded but still serving." health-dsl makes that a declaration.

You declare checks; they run concurrently with per-check timeouts, so report.durationMs is roughly your slowest check, not the sum. Aggregation is correct by design: a critical dependency that is DOWN makes the system DOWN, while a non-critical failure (or any degraded check) caps the system at DEGRADED but still healthy — you stop conflating "page someone" with "we are fine." No check can break the report: exceptions, panics, and timeouts fold into a DOWN outcome. Then serialize anywhere with a dependency-free JSON renderer or a map/struct form for Jackson, kotlinx.serialization, serde, or a Spring Actuator HealthIndicator.

Why health-dsl

Built to get out of your way

⚖️

Correct aggregation

Status is ordered UP < DEGRADED < DOWN — aggregation is "take the worst," with a non-critical DOWN capped at DEGRADED. A critical dependency going down pages you; a degraded cache does not.

Concurrent, with timeouts

Checks run concurrently (tokio / Promise.all / coroutines) with per-check timeouts. A hung dependency becomes a DOWN outcome after its timeout instead of hanging the whole probe. durationMs reflects your slowest check.

📤

Serialize anywhere

A dependency-free JSON renderer plus a map/struct form for Jackson, kotlinx.serialization, serde, or a Spring Actuator HealthIndicator. One stable JSON shape across all three languages.

Installation

Add it in one line

Same library, three ecosystems. Pick yours.

Rustcargo
cargo add health-dsl --git https://github.com/slothlabsorg/health-dsl
TypeScriptnpm
npm i @slothlabs/health-dsl
Kotlin / JVMgradle · jitpack
repositories {
    maven("https://jitpack.io")
}

dependencies {
    implementation("com.github.slothlabsorg:health-dsl:v0.1.0")
}

npm packages publish under the @slothlabs scope · JitPack builds from the git tag v0.1.0

In practice

One small example

A tiny DSL for service readiness/liveness checks — run concurrently, aggregated correctly, serialized anywhere.

RustTypeScriptKotlinHealth ChecksObservabilityReadiness
Health.kt
val health = healthChecks {
    check("database", critical = true) {
        if (db.ping()) up() else down("primary unreachable")
    }
    check("cache") { up("hitRate" to "0.93") }
    check("disk", timeout = 2.seconds) {
        val free = freePercent()
        when {
            free < 5  -> down("disk almost full: $free%")
            free < 15 -> degraded("disk low: $free%")
            else      -> up("freePercent" to free.toString())
        }
    }
}

val report = health.run()   // all checks run concurrently
report.status               // UP | DEGRADED | DOWN
report.toJson()

Try health-dsl in your stack

Free and open source. Rust, TypeScript, or Kotlin — same semantics everywhere.