# shildik

> An OIDC provider that runs as one native binary against one Postgres, where a sign-in method is a module and a distribution is a main() you write yourself.

Page: https://kotlin.website/shildik

## 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-password` cannot be configured into having passwords.
- **Two ports.** The admin API lives on its own port and is never exposed; a request to
  `/admin` from 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

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

```kotlin
repositories { maven("https://reposilite.kotlin.website/snapshots") }

dependencies {
    implementation("io.github.youndie.shildik:oidc-auth-server:$version")  // verify tokens
    implementation("io.github.youndie.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.
