Why it exists
Every Kotlin service ends up writing the same four things, slightly differently, and getting one of them subtly wrong: the shutdown, the health endpoints, the environment reading, and the build stamp. Each is small enough to write in an afternoon and none of them is small enough to get right by accident — and the one that fails does so invisibly, because a shutdown is the thing nobody watches. kore is those four, once, for a Ktor service, and it is native-first, because the facts that make it necessary are invisible from the JVM.
The sharpest one: Ktor's EmbeddedServer.stop runs its steps in the opposite order on the JVM and on Kotlin/Native. ApplicationStopping — where every example tells you to close your pool and your broker connection — runs after the drain on one platform and before it on the other, from identical source, with nothing in the API saying so. A pool closed before the drain is a request that was already being served and now fails.
What you get
-
A shutdown whose order is the specification, not a side effect:
SIGTERM → announce readiness goes false, then a wait long enough to matter → drain accept stops; in-flight finishes; new arrivals get 503 + Connection: close → release consumers flush then close, then pools, then telemetry — each with its own deadline → exit inside the grace period, on its ownAsserted twice: a property test over the stage machine, and an end-to-end run that sends a real
SIGTERMto a real container under load. Both were written before the implementation. -
Three probes that ask different questions.
startup,readyandlive, with dependency checks — not one/healthwired to all three, which is what a chart does when nobody stops it. A check that has never run reports503, and so does a cached one past its refresh budget: a stale result is not a healthy result. -
A typed configuration from the environment with
--print-configand a refusal on an unknown variable under the prefix — including a near miss, where the message names the declared variable you probably meant. Half the "it is different in prod" incidents are a variable nothing reads. -
/versionwith the commit and the build date, compiled in by a Gradle plugin rather than read at runtime, so a deployment cannot get it wrong the way it got the image tag wrong. -
tracy, metrik and katcher wired in one call, flushed in the right order on the way out.
Explicitly not: a DI container, a router, a configuration framework with seven sources, or an observability library.
What it costs
Measured, not estimated — fifteen alternating runs per cell, the same binary with kore and without:
time to first /health |
RSS at ready | stop under load | |
|---|---|---|---|
| JVM | noise | +5.6 MB | +2.0 s |
| native | noise | +1.6 MB | +2.0 s |
The stop column is not a cost. In those same runs the arm without kore finished none of the requests in flight when the signal arrived and dropped all of them — both platforms, every round — while the arm with kore finished all of them. Two seconds is what the work costs when it is not thrown away.
The startup column is printed as noise because three runs of the same harness gave +12 ms, −23 ms and +30 ms. An earlier version of that table called it "+1 %", which read as a measured cost and was an artefact of which afternoon the run happened on.
Install
repositories {
maven("https://reposilite.kotlin.website/snapshots") {
content { includeGroupByRegex("io\\.github\\.youndie.*") }
}
}
dependencies {
implementation("io.github.youndie:kore-core:0.1.3")
implementation("io.github.youndie:kore-ktor:0.1.3")
}
plugins {
id("io.github.youndie.kore.build") version "0.1.3" // what /version reports
}
Where it actually is
All five features built, 0.1.3 published, 51 backlog items closed, suites running on arm64 Linux and Apple silicon on every pull request. Nothing is on Maven Central yet.
The first real adoption found four defects in a day that none of that caught — among them a documented example that cannot run on the JVM, and a module that had never been published at all. That is the honest measure of where this is: the library works, and the things a consumer trips over are still being found by consumers.