# kore

> The process lifecycle of a Kotlin server binary, owned once: shutdown in a defined order, three health questions, a typed configuration that refuses an unknown variable, and /version with the commit.

Page: https://kotlin.website/kore

## 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:

  ```text
  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 own
  ```

  Asserted twice: a property test over the stage machine, and an end-to-end run that sends a real
  `SIGTERM` to a real container under load. Both were written **before** the implementation.
- **Three probes that ask different questions.** `startup`, `ready` and `live`, with dependency
  checks — not one `/health` wired to all three, which is what a chart does when nobody stops it. A
  check that has never run reports `503`, 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-config` and 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.
- **`/version` with 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

```kotlin
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.
