Why it exists
A platform needed three things from Keycloak — service tokens, browser sign-in, and keys to verify them — and paid 640 MiB of memory for everything else. The part that hurt was not the memory. It was the magic link: in Keycloak it lived as a jar built against somebody else's SPI, loaded into somebody else's process, assembled by hand, with the sources eventually lost.
Here a sign-in method is a module that implements one interface, and a distribution is a main() that lists the ones it wants. What a build does not carry is a fact of the build rather than a setting.
What you get
- One native binary against one Postgres. 45–48 MiB resident in production, measured over a 20-hour window; the JVM build of the same code is kept as a rollback path.
- The OIDC a platform actually calls:
client_credentials, JWKS, discovery, authorization code with PKCE,id_token, refresh rotation,userinfo,end_session. - Sign-in methods as modules — Google, magic link, password. A build without
auth-passwordcannot be configured into having passwords. - Two ports. The admin API lives on its own port and is never exposed; a request to
/adminfrom outside gets a 404 rather than a 403, so the existence of a management contour is not confirmed. - A CLI instead of a console. Clients, roles, users and signing keys, with the whole configuration exportable to git and applied back with
shildik apply. - Key rotation without downtime: several signing keys live side by side, JWKS serves them all, and a retiring key stays for a day because that is how long consumers cache it.
Assemble one
val server = shildikServer(
config = ShildikConfig(issuer = "https://id.example.com", /* … */),
storage = { config -> sqlx4kStorageModule(config.jdbcUrl, config.dbUser, config.dbPassword) },
)
server.start(wait = true)
Sign-in methods are handed to the graph the same way — by listing them. The libraries a consuming service needs are published from the same repository:
repositories { maven("https://reposilite.kotlin.website/snapshots") }
dependencies {
implementation("ru.workinprogress.shildik:oidc-auth-server:$version") // verify tokens
implementation("ru.workinprogress.shildik:oidc-auth-client:$version") // ask for one
}
The validator checks the signature and the expiry and fetches JWKS itself. iss is deliberately not checked: during a provider migration a service has to accept tokens from both, and two key sources live side by side until the old one is gone.
Status
In production. Shop owners sign in through it, every platform service takes tokens and keys from it, and it has already been through a user migration off Keycloak, a move to Kotlin/Native and a PostgreSQL major upgrade. What is new is the packaging — the module boundaries and the published API may still move.