
Modular libraries and framework integrations delivering server foundations, databases (Postgres, Mongo, Redis), messaging (Kafka, AMQP), GraphQL, durable workflows, migrations, telemetry, i18n, UI components and an OpenAPI-to-client generator.
Seventeen Kotlin libraries in forty-five published modules, under io.github.softistx:stx-* — a Ktor and Spring
Boot foundation, Postgres and MongoDB, Redis, Kafka and AMQP, object storage, GraphQL, workflows
that survive a restart, schema migrations, telemetry, i18n, and a Compose Multiplatform component
library. Each one publishes as its own artifact, with its framework integration beside it, so
depending on stx-jpa drags in neither Ktor nor Spring.
They are built with the JetBrains Kotlin Toolchain (the kotlin CLI, formerly Amper) — no
Gradle, no Maven, no gradlew. A module is a directory with a module.yaml, registered by path in
project.yaml. Consuming them needs none of that: they are ordinary Maven artifacts with
Gradle metadata, and docs/consuming.md has the snippet for Gradle, Maven and
the toolchain.
Alongside the libraries is an OpenAPI-to-Kotlin client generator, built as four modules that form one chain:
examples/demo-api/openapi.yaml one document
│
libs/api/stx-openapi-generator reads it, emits models and a typed client (KotlinPoet)
│
plugins/openapi wraps the generator as a toolchain build task
│
examples/demo-client a Ktorfit client, kotlinx.serialization ─┐
examples/demo-spring-client a Spring @HttpExchange client, Jackson 3 ─┴─ both call examples/demo-api
Two generated clients drive one hand-written server over real HTTP, so a disagreement between the two serialization libraries about what the document means fails a test rather than shipping.
examples/jpa-shop is separate from that chain: a Ktor catalogue over Postgres showing
stx-jpa's CRUD extensions, transaction guard and audit layer end to end. So is
examples/graphix-shop, a Ktor GraphQL catalogue over stx-graphix-ktor,
examples/graphix-codegen, the two GraphQL codegen plugins on one schema, and
examples/material-demo, the catalogue for the UI library — ./kotlin run -m md-desktop.
Alongside them are the shared service libraries, which have nothing to do with the generator:
libs/core/stx-common |
What more than one library here needs and nothing else, in two concurrency packages split by whether the caller can suspend — CoroutineSafeMap, KeyedMutex and Mailbox for the ones that can, Memo, Guarded and the concurrent-collection extensions for the Hibernate binders and SLF4J initialisers that cannot — plus CloseGuard, the keyset pagination both stores share, and the one lenient Json
|
libs/messaging/stx-amqp |
An AMQP connection over the RabbitMQ client: topology declared in one block, publishes that wait for the broker's confirm, deliveries as a Flow, and retries that are delay queues rather than a loop |
libs/api/stx-i18n |
Catalogs read strictly as UTF-8 and compiled at startup, a message resolved key by key down the locale chain, and Accept-Language negotiated against what is actually shipped |
libs/data/stx-jpa |
Postgres over Hibernate Reactive: ordinary annotated Kotlin entities, every session pinned to the event loop that opened it so a handler can suspend mid-transaction, and HQL, SQL and JPA Criteria — named by KProperty rather than by strings — through one suspending builder |
libs/ui/stx-material |
The one client-side library: Compose Multiplatform components over Material 3 — a whole palette derived from one colour seed, a component's look declared as a Style whose pressed and hovered states animate themselves, and motion as named durations instead of scattered tweens |
libs/messaging/stx-kafka |
A cluster and the clients over it: sends that suspend until the broker acknowledges them, records as a Flow with the offsets looked after, and topics and group lag from an admin client |
libs/core/stx-ktor |
The Ktor foundation every plugin here is built on: own/publish/resource/required — a connection opened with the application and closed with it, and a missing install that names itself — plus one CORS policy shared with Spring. The plugins themselves are modules beside their libraries: stx-redis-ktor, stx-mongo-ktor, stx-jpa-ktor, stx-amqp-ktor, stx-kafka-ktor, stx-storage-ktor, stx-i18n-ktor
|
libs/data/stx-mongo |
Session-aware collection extensions covering CRUD, keyset pagination, an opt-in audit trail, and a coroutine GridFS bucket |
libs/data/stx-redis |
A namespaced connection over Lettuce owning one Json, and the four kotlinx-serialized things built on one: a typed cache, a lock, topics, and streams with consumer groups |
libs/core/stx-spring-boot |
The Spring Boot seam: the WebFlux helpers — a translated error body and the request's own locale, taken from the exchange rather than a ThreadLocal — plus security, CORS, the JSON codec and the Spring Data Mongo layer. Each library's own auto-configuration is a module beside it: stx-mongo-spring, stx-jpa-spring, stx-redis-spring, stx-kafka-spring, stx-amqp-spring, stx-storage-spring, stx-i18n-spring
|
libs/data/stx-storage |
S3-compatible object storage over the MinIO SDK: buckets and objects as coroutines, and presigned URLs and upload forms for browsers |
libs/api/stx-graphix |
GraphQL over graphql-java 25: annotated Kotlin functions, @Serializable types, suspending execution. stx-graphix-ktor and stx-graphix-spring are the HTTP integrations, stx-graphix-koin builds the schema from a Koin container |
libs/messaging/stx-workflow |
Workflows that survive a restart: steps declared in order, each with the compensation that undoes it, one typed context threaded through them, and the state written down after every node — so a crash resumes rather than restarts. A workflow can also stop on purpose, for a person's approval, for a clock, or for another workflow it delegated to, and outlive the process it stopped in. Declared as a DSL or as annotations on a class — the two produce the same object — and wired into Ktor or Spring Boot by stx-workflow-ktor and stx-workflow-spring. stx-workflow-db keeps them in Redis, in SQL through stx-jpa, or in MongoDB |
libs/data/stx-migrations |
Schema migrations that are Kotlin all the way down, on neither Flyway nor Liquibase: a migration is a class with a version it declares, the ledger records what ran and who ran it, and a renewing lease keeps two instances of the application from running the same one twice. It is a gate — it runs in the process about to serve, before it serves, and a failure is an application that does not start. stx-migrations-db holds the ledger for MongoDB and for SQL through stx-jpa; stx-migrations-ktor and stx-migrations-spring are the two integrations |
libs/observability/stx-telemetry |
Logs and traces for a service made of coroutines: the current span is a CoroutineContext element rather than a ThreadLocal, so it is still right after a suspension moves the work — which is exactly what an MDC gets wrong. An event is a @Serializable type, whose serial name names it and whose fields are its attributes, so choosing what is logged is the same act as declaring a type. Signals queue without ever making a caller wait and drain to the exporters in one coroutine |
libs/observability/stx-telemetry/stx-telemetry-otlp |
Ships those logs and traces to an OTLP collector over HTTP in JSON, with kotlinx.serialization and the JDK's own HttpClient — no OpenTelemetry SDK, and no dependency but the core module |
libs/observability/stx-telemetry/stx-telemetry-slf4j |
The SLF4J bridge, both ways: an SPI provider that puts every third-party library's logs into the pipeline carrying the current span, or an exporter that writes this library's signals out to a logback an application already has |
libs/observability/stx-telemetry/stx-telemetry-mongo |
Those same logs and traces into a MongoDB collection, with the field names the JSON-lines format uses and retention as a TTL index — so the deleting is Mongo's background task rather than a job somebody has to own. It opens a pool of its own, because a burst of telemetry must not exhaust the one business requests are queueing for |
libs/observability/stx-telemetry/stx-telemetry-ktor |
The Ktor plugin: one telemetry per application and a server span per request, continuing an incoming traceparent and renamed to the matched route once Ktor knows it |
libs/observability/stx-telemetry/stx-telemetry-spring |
The Spring Boot auto-configuration: one telemetry behind stx.telemetry.enabled, every Exporter bean added to it, and a CoWebFilter — not a WebFilter — so a suspending @RestController method is inside the request's span |
libs/core/stx-testing |
What the integration specs run against: a backing service reused from the environment when one is named, and started as a container for the run when it is not |
dependencies {
implementation("io.github.softistx:stx-jpa:0.2.1")
implementation("io.github.softistx:stx-jpa-ktor:0.2.1")
}On Maven Central — no repository block, no token, PGP-signed, with a sources jar.
docs/consuming.md has the Maven and Kotlin Toolchain forms, and which
artifact you actually want: a library and its framework integration are separate, so depending on
stx-jpa drags in neither Ktor nor Spring.
Each library family has its own version. A library and its framework integrations —
stx-jpa, stx-jpa-ktor, stx-jpa-spring — are one release line and move together; separate
libraries do not. So a bump you see is a bump that concerns you.
docs/consuming.md says what that
promises, and the one thing it does not. Everything through 0.2.1 was released in lockstep; from
there each family moves on its own.
For working on the libraries rather than with them. You need no JDK and no Kotlin compiler — the wrapper provisions both.
./kotlin build # compile everything
./kotlin test # run every module's tests
./kotlin show modules # module names accepted by -mUse ./kotlin, not a bare kotlin: the wrapper pins the toolchain version.
CONTRIBUTING.md is the rest.
docs/consuming.md |
How to depend on io.github.softistx:stx-* from Gradle, Maven or the toolchain, which artifact to pick, and why all 45 share one version |
CONTRIBUTING.md |
How to set up, what a pull request needs, and what a reviewer will look for |
docs/releasing.md |
The release circuit — one version per library family, a changeset per PR, the Version Packages PR, and the six things about publishing here that are not guessable |
docs/openapi-support.md |
What the generator understands: type mapping, composition, enums, vendor extensions, and what it does not handle |
libs/api/stx-openapi-generator/README.md |
The generator itself — its shape, what each client emitter produces, how to add one |
examples/demo-api/README.md |
The document and the hand-written server behind it — and why the server is hand-written |
examples/demo-client/README.md |
The kotlinx client — security, failures, oneOf with and without a discriminator |
examples/demo-spring-client/README.md |
The Spring client — two documents in one module, and why it repeats the other client's tests |
libs/core/stx-common/README.md |
The shared module — what belongs in it, which concurrency type a given caller wants, why getOrPut on a ConcurrentHashMap is not atomic, and what the standard library already covers |
libs/messaging/stx-amqp/stx-amqp/README.md |
The AMQP library — exchanges and queues, what a confirm promises, and what prefetch is for |
libs/messaging/stx-amqp/stx-amqp-ktor/README.md |
The Ktor plugin — why it owns the connection and not the channel, and why the connect blocks |
libs/messaging/stx-amqp/stx-amqp-spring/README.md |
The Spring auto-configuration — what the bean owns and what it deliberately does not |
libs/api/stx-i18n/stx-i18n/README.md |
The i18n library — catalogs, the per-key locale walk, the missing-key policy, and negotiation |
libs/api/stx-i18n/stx-i18n-ktor/README.md |
The Ktor plugin — why the locale is resolved once at call setup, and why ?lang= is off by default |
libs/api/stx-i18n/stx-i18n-spring/README.md |
The Spring auto-configuration — why the narrowed locale resolver is the half that matters, and why Boot's own property wins |
libs/core/stx-ktor/README.md |
The Ktor foundation — the four lifecycle verbs and the rule behind them, what Ktor's container does to what a plugin registers, and why no integration lives here |
libs/core/stx-spring-boot/README.md |
The Spring integration — the opt-in stx.* model, why the IDE metadata is written by hand, and what compile-only buys a consumer |
docs/spring-mongo-queries.md |
What a stx-spring-boot Mongo query may say — the operators, the filter and sort grammars, and the keyset paging rules |
docs/spring-configuration.md |
Every stx.* key an application may set, its default, and what switching it on costs |
libs/data/stx-jpa/stx-jpa/README.md |
The Postgres library — the session confinement rule everything else follows from, and why each part is shaped the way it is |
libs/data/stx-jpa/stx-jpa-ktor/README.md |
The Ktor plugin — why it owns a factory and not a session, why runBlocking inside install, and why a scan that finds nothing fails |
libs/data/stx-jpa/stx-jpa-spring/README.md |
The Spring auto-configuration — why packages is required, and why the schema is somebody else's job |
docs/jpa-criteria.md |
What a stx-jpa query may say — operators, joins, fetch joins, entity graphs, projections, and the two escapes |
docs/jpa-mapping.md |
What a stx-jpa entity may say — the database, column names, identifiers, Instant/Uuid, JSON columns, validation |
docs/graphix.md |
What a stx-graphix schema may say — the annotations, scalars, field directives, DataLoaders, SDL scan, HTTP/SSE/graphql-ws |
libs/api/stx-graphix/stx-graphix/README.md |
The GraphQL engine — why SerialDescriptor and not Jackson, why there is no class scan in core |
libs/api/stx-graphix/stx-graphix-ktor/README.md |
The Ktor plugin — path, instance vs schema { }, the call on every operation, and the engine it registers with the container |
libs/api/stx-graphix/stx-graphix-spring/README.md |
The Spring Boot plugin — stx.graphix.enabled, @GraphQLController scan |
libs/api/stx-graphix/stx-graphix-koin/README.md |
Building the schema from a Koin container — fromKoin(), GraphixResolver
|
examples/graphix-shop/README.md |
The GraphQL catalogue — how to run it, the split SDL under resources/graphql/
|
plugins/dgs-codegen/README.md |
DGS codegen plugin — schema to Kotlin types |
plugins/apollo/README.md |
Apollo codegen plugin — schema and documents to Kotlin models |
examples/graphix-codegen/README.md |
Both plugins on one schema |
libs/ui/stx-material/README.md |
The UI library — its shape, how StxTheme slots into an existing Material 3 application, and how a component is added |
libs/ui/stx-material/docs/tokens.md |
What a token may say — colour roles, spacing, durations and easings, and why shapes and elevation stay M3's |
libs/ui/stx-material/docs/components.md |
Every component, its parameters, and its story in the catalogue |
examples/workflow-checkout/README.md |
The checkout saga — compensation, a fan-out, and a process killed mid-charge to show what at-least-once buys and costs |
examples/material-demo/README.md |
The catalogue — why it is three modules, how to run it, how a story is registered |
libs/messaging/stx-kafka/stx-kafka/README.md |
The Kafka library — publishing, the poll loop and its commits, and what at-least-once costs |
libs/messaging/stx-kafka/stx-kafka-ktor/README.md |
The Ktor plugin — the one here that opens nothing, and why a Kafka client makes that the right shape |
libs/messaging/stx-kafka/stx-kafka-spring/README.md |
The Spring auto-configuration — why its bean holds no connection, and why its wiring spec needs no broker |
libs/data/stx-mongo/stx-mongo/README.md |
The MongoDB library — its packages, and the reasoning behind the parts that are not obvious |
libs/data/stx-mongo/stx-mongo-ktor/README.md |
The Ktor plugin — why the client is built in the library and not in the plugin, and why uri and database have no defaults |
libs/data/stx-mongo/stx-mongo-spring/README.md |
The Spring auto-configuration — the codec registry argument again, and which of two MongoDatabase beans an application ends up with |
docs/telemetry.md |
What a stx-telemetry call may say — the root's settings, the log and span verbs, the severities, the attribute rules, traceparent and the signal model |
libs/observability/stx-telemetry/stx-telemetry/README.md |
The telemetry library — why the span is a coroutine context element and not an MDC, why an event is a type, and why writing a log never waits |
libs/observability/stx-telemetry/stx-telemetry-otlp/README.md |
The OTLP exporter — why not the Java SDK, the two encoding details that are easy to get wrong, and what is retried |
libs/observability/stx-telemetry/stx-telemetry-slf4j/README.md |
The SLF4J bridge — which direction to pick, why reading a third-party MDC is not a contradiction, and why both directions at once is refused |
libs/observability/stx-telemetry/stx-telemetry-mongo/README.md |
The MongoDB exporter — why the document is the line format plus the resource, why the instants are written back as dates, and why retention is an index and not a job |
libs/observability/stx-telemetry/stx-telemetry-ktor/README.md |
The Ktor plugin — why the span wraps the pipeline instead of being two hooks, why it is renamed after routing, and what a thrown handler costs the span |
libs/observability/stx-telemetry/stx-telemetry-spring/README.md |
The Spring auto-configuration — why CoWebFilter and not WebFilter, the dependency the specs found by hanging, and the 200 nobody set |
docs/workflow.md |
What a stx-workflow declaration may say — the verbs, the step scope, the statuses, the record and the store contract |
libs/messaging/stx-workflow/stx-workflow/README.md |
The workflow engine — checkpointing rather than replay, what at-least-once asks of a step, why a fan-out merges explicitly, and why a child is an instance rather than a call |
libs/messaging/stx-workflow/stx-workflow-db/README.md |
Where instances live — why one module and not three, and what each store does with an index, a lease and retention |
libs/messaging/stx-workflow/stx-workflow-spring/README.md |
The Spring auto-configuration — why an integration is a module beside its library, and why nothing is inferred about where instances live |
libs/messaging/stx-workflow/stx-workflow-ktor/README.md |
The Ktor plugin — and why own/publish/required had to become public |
docs/migrations.md |
What a stx-migrations migration may say — the version, the statuses, the runner's order, the ledger contract, the lock, and what a killed process leaves |
libs/data/stx-migrations/stx-migrations/README.md |
The migration library — why the version is declared rather than parsed, why a gate throws where the prior art logged, and why neither Flyway nor Liquibase is underneath it |
libs/data/stx-migrations/stx-migrations-db/README.md |
Where the ledger lives — why one module and not two, and per store: the uniqueness, the lock, the instants, and how DDL reaches a database with no JDBC in front of it |
libs/data/stx-migrations/stx-migrations-ktor/README.md |
The Ktor plugin — why runBlocking inside install is what makes it a gate, why it goes after the connection plugin, and why sql { } / mongo { } are sugar over the one gate that is the contract |
libs/data/stx-migrations/stx-migrations-spring/README.md |
The Spring auto-configuration — why an InitializingBean and not a suspending listener, and why naming a store without its connection is an error rather than a shrug |
libs/data/stx-redis/stx-redis/README.md |
The Redis library — the cache, the lock, topics and streams, and what each one refuses to do |
libs/data/stx-redis/stx-redis-ktor/README.md |
The Ktor plugin — why one connection and not one per request, and what handing it to the container costs and does not |
libs/data/stx-redis/stx-redis-spring/README.md |
The Spring auto-configuration — why it is off unless asked for, and why stx.redis is not spring.data.redis
|
libs/data/stx-storage/stx-storage/README.md |
The object storage library — objects, and what a presigned URL or upload form can promise |
libs/data/stx-storage/stx-storage-ktor/README.md |
The Ktor plugin — and why it is the one whose config is required rather than defaulted |
libs/data/stx-storage/stx-storage-spring/README.md |
The Spring auto-configuration — why no credential has a default, and why its spec points at a dead port |
libs/core/stx-testing/README.md |
The test support — where a spec's server comes from, and what cleans a container up afterwards |
plugins/openapi/README.md |
The build plugin: settings, and what each choice needs on the consuming module's classpath |
examples/jpa-shop/README.md |
The Postgres catalogue — stx-jpa's CRUD extensions, transaction guard and audit layer end to end |
examples/spring-orders/README.md |
The order book — a spec-first REST API, keyset paging, an audit trail and two migrations |
server/oauth/README.md |
The work in progress, not an example — a GraphQL service where the SDL owns the types |
AGENTS.md |
Build commands, module layout, and the conventions this repo holds itself to |
Pull requests target develop, never main. Anything touching libs/ needs a changeset
(bun changeset) and a publish to mavenLocal before a consumer is built against it —
CONTRIBUTING.md explains why that second step is the one that catches a broken
POM. Be civil: Code of Conduct.
Found a vulnerability? Not an issue — SECURITY.md.
Apache-2.0. Third-party documentation cached under .agents/skills/*/references/ is
attributed in NOTICE.
Seventeen Kotlin libraries in forty-five published modules, under io.github.softistx:stx-* — a Ktor and Spring
Boot foundation, Postgres and MongoDB, Redis, Kafka and AMQP, object storage, GraphQL, workflows
that survive a restart, schema migrations, telemetry, i18n, and a Compose Multiplatform component
library. Each one publishes as its own artifact, with its framework integration beside it, so
depending on stx-jpa drags in neither Ktor nor Spring.
They are built with the JetBrains Kotlin Toolchain (the kotlin CLI, formerly Amper) — no
Gradle, no Maven, no gradlew. A module is a directory with a module.yaml, registered by path in
project.yaml. Consuming them needs none of that: they are ordinary Maven artifacts with
Gradle metadata, and docs/consuming.md has the snippet for Gradle, Maven and
the toolchain.
Alongside the libraries is an OpenAPI-to-Kotlin client generator, built as four modules that form one chain:
examples/demo-api/openapi.yaml one document
│
libs/api/stx-openapi-generator reads it, emits models and a typed client (KotlinPoet)
│
plugins/openapi wraps the generator as a toolchain build task
│
examples/demo-client a Ktorfit client, kotlinx.serialization ─┐
examples/demo-spring-client a Spring @HttpExchange client, Jackson 3 ─┴─ both call examples/demo-api
Two generated clients drive one hand-written server over real HTTP, so a disagreement between the two serialization libraries about what the document means fails a test rather than shipping.
examples/jpa-shop is separate from that chain: a Ktor catalogue over Postgres showing
stx-jpa's CRUD extensions, transaction guard and audit layer end to end. So is
examples/graphix-shop, a Ktor GraphQL catalogue over stx-graphix-ktor,
examples/graphix-codegen, the two GraphQL codegen plugins on one schema, and
examples/material-demo, the catalogue for the UI library — ./kotlin run -m md-desktop.
Alongside them are the shared service libraries, which have nothing to do with the generator:
libs/core/stx-common |
What more than one library here needs and nothing else, in two concurrency packages split by whether the caller can suspend — CoroutineSafeMap, KeyedMutex and Mailbox for the ones that can, Memo, Guarded and the concurrent-collection extensions for the Hibernate binders and SLF4J initialisers that cannot — plus CloseGuard, the keyset pagination both stores share, and the one lenient Json
|
libs/messaging/stx-amqp |
An AMQP connection over the RabbitMQ client: topology declared in one block, publishes that wait for the broker's confirm, deliveries as a Flow, and retries that are delay queues rather than a loop |
libs/api/stx-i18n |
Catalogs read strictly as UTF-8 and compiled at startup, a message resolved key by key down the locale chain, and Accept-Language negotiated against what is actually shipped |
libs/data/stx-jpa |
Postgres over Hibernate Reactive: ordinary annotated Kotlin entities, every session pinned to the event loop that opened it so a handler can suspend mid-transaction, and HQL, SQL and JPA Criteria — named by KProperty rather than by strings — through one suspending builder |
libs/ui/stx-material |
The one client-side library: Compose Multiplatform components over Material 3 — a whole palette derived from one colour seed, a component's look declared as a Style whose pressed and hovered states animate themselves, and motion as named durations instead of scattered tweens |
libs/messaging/stx-kafka |
A cluster and the clients over it: sends that suspend until the broker acknowledges them, records as a Flow with the offsets looked after, and topics and group lag from an admin client |
libs/core/stx-ktor |
The Ktor foundation every plugin here is built on: own/publish/resource/required — a connection opened with the application and closed with it, and a missing install that names itself — plus one CORS policy shared with Spring. The plugins themselves are modules beside their libraries: stx-redis-ktor, stx-mongo-ktor, stx-jpa-ktor, stx-amqp-ktor, stx-kafka-ktor, stx-storage-ktor, stx-i18n-ktor
|
libs/data/stx-mongo |
Session-aware collection extensions covering CRUD, keyset pagination, an opt-in audit trail, and a coroutine GridFS bucket |
libs/data/stx-redis |
A namespaced connection over Lettuce owning one Json, and the four kotlinx-serialized things built on one: a typed cache, a lock, topics, and streams with consumer groups |
libs/core/stx-spring-boot |
The Spring Boot seam: the WebFlux helpers — a translated error body and the request's own locale, taken from the exchange rather than a ThreadLocal — plus security, CORS, the JSON codec and the Spring Data Mongo layer. Each library's own auto-configuration is a module beside it: stx-mongo-spring, stx-jpa-spring, stx-redis-spring, stx-kafka-spring, stx-amqp-spring, stx-storage-spring, stx-i18n-spring
|
libs/data/stx-storage |
S3-compatible object storage over the MinIO SDK: buckets and objects as coroutines, and presigned URLs and upload forms for browsers |
libs/api/stx-graphix |
GraphQL over graphql-java 25: annotated Kotlin functions, @Serializable types, suspending execution. stx-graphix-ktor and stx-graphix-spring are the HTTP integrations, stx-graphix-koin builds the schema from a Koin container |
libs/messaging/stx-workflow |
Workflows that survive a restart: steps declared in order, each with the compensation that undoes it, one typed context threaded through them, and the state written down after every node — so a crash resumes rather than restarts. A workflow can also stop on purpose, for a person's approval, for a clock, or for another workflow it delegated to, and outlive the process it stopped in. Declared as a DSL or as annotations on a class — the two produce the same object — and wired into Ktor or Spring Boot by stx-workflow-ktor and stx-workflow-spring. stx-workflow-db keeps them in Redis, in SQL through stx-jpa, or in MongoDB |
libs/data/stx-migrations |
Schema migrations that are Kotlin all the way down, on neither Flyway nor Liquibase: a migration is a class with a version it declares, the ledger records what ran and who ran it, and a renewing lease keeps two instances of the application from running the same one twice. It is a gate — it runs in the process about to serve, before it serves, and a failure is an application that does not start. stx-migrations-db holds the ledger for MongoDB and for SQL through stx-jpa; stx-migrations-ktor and stx-migrations-spring are the two integrations |
libs/observability/stx-telemetry |
Logs and traces for a service made of coroutines: the current span is a CoroutineContext element rather than a ThreadLocal, so it is still right after a suspension moves the work — which is exactly what an MDC gets wrong. An event is a @Serializable type, whose serial name names it and whose fields are its attributes, so choosing what is logged is the same act as declaring a type. Signals queue without ever making a caller wait and drain to the exporters in one coroutine |
libs/observability/stx-telemetry/stx-telemetry-otlp |
Ships those logs and traces to an OTLP collector over HTTP in JSON, with kotlinx.serialization and the JDK's own HttpClient — no OpenTelemetry SDK, and no dependency but the core module |
libs/observability/stx-telemetry/stx-telemetry-slf4j |
The SLF4J bridge, both ways: an SPI provider that puts every third-party library's logs into the pipeline carrying the current span, or an exporter that writes this library's signals out to a logback an application already has |
libs/observability/stx-telemetry/stx-telemetry-mongo |
Those same logs and traces into a MongoDB collection, with the field names the JSON-lines format uses and retention as a TTL index — so the deleting is Mongo's background task rather than a job somebody has to own. It opens a pool of its own, because a burst of telemetry must not exhaust the one business requests are queueing for |
libs/observability/stx-telemetry/stx-telemetry-ktor |
The Ktor plugin: one telemetry per application and a server span per request, continuing an incoming traceparent and renamed to the matched route once Ktor knows it |
libs/observability/stx-telemetry/stx-telemetry-spring |
The Spring Boot auto-configuration: one telemetry behind stx.telemetry.enabled, every Exporter bean added to it, and a CoWebFilter — not a WebFilter — so a suspending @RestController method is inside the request's span |
libs/core/stx-testing |
What the integration specs run against: a backing service reused from the environment when one is named, and started as a container for the run when it is not |
dependencies {
implementation("io.github.softistx:stx-jpa:0.2.1")
implementation("io.github.softistx:stx-jpa-ktor:0.2.1")
}On Maven Central — no repository block, no token, PGP-signed, with a sources jar.
docs/consuming.md has the Maven and Kotlin Toolchain forms, and which
artifact you actually want: a library and its framework integration are separate, so depending on
stx-jpa drags in neither Ktor nor Spring.
Each library family has its own version. A library and its framework integrations —
stx-jpa, stx-jpa-ktor, stx-jpa-spring — are one release line and move together; separate
libraries do not. So a bump you see is a bump that concerns you.
docs/consuming.md says what that
promises, and the one thing it does not. Everything through 0.2.1 was released in lockstep; from
there each family moves on its own.
For working on the libraries rather than with them. You need no JDK and no Kotlin compiler — the wrapper provisions both.
./kotlin build # compile everything
./kotlin test # run every module's tests
./kotlin show modules # module names accepted by -mUse ./kotlin, not a bare kotlin: the wrapper pins the toolchain version.
CONTRIBUTING.md is the rest.
docs/consuming.md |
How to depend on io.github.softistx:stx-* from Gradle, Maven or the toolchain, which artifact to pick, and why all 45 share one version |
CONTRIBUTING.md |
How to set up, what a pull request needs, and what a reviewer will look for |
docs/releasing.md |
The release circuit — one version per library family, a changeset per PR, the Version Packages PR, and the six things about publishing here that are not guessable |
docs/openapi-support.md |
What the generator understands: type mapping, composition, enums, vendor extensions, and what it does not handle |
libs/api/stx-openapi-generator/README.md |
The generator itself — its shape, what each client emitter produces, how to add one |
examples/demo-api/README.md |
The document and the hand-written server behind it — and why the server is hand-written |
examples/demo-client/README.md |
The kotlinx client — security, failures, oneOf with and without a discriminator |
examples/demo-spring-client/README.md |
The Spring client — two documents in one module, and why it repeats the other client's tests |
libs/core/stx-common/README.md |
The shared module — what belongs in it, which concurrency type a given caller wants, why getOrPut on a ConcurrentHashMap is not atomic, and what the standard library already covers |
libs/messaging/stx-amqp/stx-amqp/README.md |
The AMQP library — exchanges and queues, what a confirm promises, and what prefetch is for |
libs/messaging/stx-amqp/stx-amqp-ktor/README.md |
The Ktor plugin — why it owns the connection and not the channel, and why the connect blocks |
libs/messaging/stx-amqp/stx-amqp-spring/README.md |
The Spring auto-configuration — what the bean owns and what it deliberately does not |
libs/api/stx-i18n/stx-i18n/README.md |
The i18n library — catalogs, the per-key locale walk, the missing-key policy, and negotiation |
libs/api/stx-i18n/stx-i18n-ktor/README.md |
The Ktor plugin — why the locale is resolved once at call setup, and why ?lang= is off by default |
libs/api/stx-i18n/stx-i18n-spring/README.md |
The Spring auto-configuration — why the narrowed locale resolver is the half that matters, and why Boot's own property wins |
libs/core/stx-ktor/README.md |
The Ktor foundation — the four lifecycle verbs and the rule behind them, what Ktor's container does to what a plugin registers, and why no integration lives here |
libs/core/stx-spring-boot/README.md |
The Spring integration — the opt-in stx.* model, why the IDE metadata is written by hand, and what compile-only buys a consumer |
docs/spring-mongo-queries.md |
What a stx-spring-boot Mongo query may say — the operators, the filter and sort grammars, and the keyset paging rules |
docs/spring-configuration.md |
Every stx.* key an application may set, its default, and what switching it on costs |
libs/data/stx-jpa/stx-jpa/README.md |
The Postgres library — the session confinement rule everything else follows from, and why each part is shaped the way it is |
libs/data/stx-jpa/stx-jpa-ktor/README.md |
The Ktor plugin — why it owns a factory and not a session, why runBlocking inside install, and why a scan that finds nothing fails |
libs/data/stx-jpa/stx-jpa-spring/README.md |
The Spring auto-configuration — why packages is required, and why the schema is somebody else's job |
docs/jpa-criteria.md |
What a stx-jpa query may say — operators, joins, fetch joins, entity graphs, projections, and the two escapes |
docs/jpa-mapping.md |
What a stx-jpa entity may say — the database, column names, identifiers, Instant/Uuid, JSON columns, validation |
docs/graphix.md |
What a stx-graphix schema may say — the annotations, scalars, field directives, DataLoaders, SDL scan, HTTP/SSE/graphql-ws |
libs/api/stx-graphix/stx-graphix/README.md |
The GraphQL engine — why SerialDescriptor and not Jackson, why there is no class scan in core |
libs/api/stx-graphix/stx-graphix-ktor/README.md |
The Ktor plugin — path, instance vs schema { }, the call on every operation, and the engine it registers with the container |
libs/api/stx-graphix/stx-graphix-spring/README.md |
The Spring Boot plugin — stx.graphix.enabled, @GraphQLController scan |
libs/api/stx-graphix/stx-graphix-koin/README.md |
Building the schema from a Koin container — fromKoin(), GraphixResolver
|
examples/graphix-shop/README.md |
The GraphQL catalogue — how to run it, the split SDL under resources/graphql/
|
plugins/dgs-codegen/README.md |
DGS codegen plugin — schema to Kotlin types |
plugins/apollo/README.md |
Apollo codegen plugin — schema and documents to Kotlin models |
examples/graphix-codegen/README.md |
Both plugins on one schema |
libs/ui/stx-material/README.md |
The UI library — its shape, how StxTheme slots into an existing Material 3 application, and how a component is added |
libs/ui/stx-material/docs/tokens.md |
What a token may say — colour roles, spacing, durations and easings, and why shapes and elevation stay M3's |
libs/ui/stx-material/docs/components.md |
Every component, its parameters, and its story in the catalogue |
examples/workflow-checkout/README.md |
The checkout saga — compensation, a fan-out, and a process killed mid-charge to show what at-least-once buys and costs |
examples/material-demo/README.md |
The catalogue — why it is three modules, how to run it, how a story is registered |
libs/messaging/stx-kafka/stx-kafka/README.md |
The Kafka library — publishing, the poll loop and its commits, and what at-least-once costs |
libs/messaging/stx-kafka/stx-kafka-ktor/README.md |
The Ktor plugin — the one here that opens nothing, and why a Kafka client makes that the right shape |
libs/messaging/stx-kafka/stx-kafka-spring/README.md |
The Spring auto-configuration — why its bean holds no connection, and why its wiring spec needs no broker |
libs/data/stx-mongo/stx-mongo/README.md |
The MongoDB library — its packages, and the reasoning behind the parts that are not obvious |
libs/data/stx-mongo/stx-mongo-ktor/README.md |
The Ktor plugin — why the client is built in the library and not in the plugin, and why uri and database have no defaults |
libs/data/stx-mongo/stx-mongo-spring/README.md |
The Spring auto-configuration — the codec registry argument again, and which of two MongoDatabase beans an application ends up with |
docs/telemetry.md |
What a stx-telemetry call may say — the root's settings, the log and span verbs, the severities, the attribute rules, traceparent and the signal model |
libs/observability/stx-telemetry/stx-telemetry/README.md |
The telemetry library — why the span is a coroutine context element and not an MDC, why an event is a type, and why writing a log never waits |
libs/observability/stx-telemetry/stx-telemetry-otlp/README.md |
The OTLP exporter — why not the Java SDK, the two encoding details that are easy to get wrong, and what is retried |
libs/observability/stx-telemetry/stx-telemetry-slf4j/README.md |
The SLF4J bridge — which direction to pick, why reading a third-party MDC is not a contradiction, and why both directions at once is refused |
libs/observability/stx-telemetry/stx-telemetry-mongo/README.md |
The MongoDB exporter — why the document is the line format plus the resource, why the instants are written back as dates, and why retention is an index and not a job |
libs/observability/stx-telemetry/stx-telemetry-ktor/README.md |
The Ktor plugin — why the span wraps the pipeline instead of being two hooks, why it is renamed after routing, and what a thrown handler costs the span |
libs/observability/stx-telemetry/stx-telemetry-spring/README.md |
The Spring auto-configuration — why CoWebFilter and not WebFilter, the dependency the specs found by hanging, and the 200 nobody set |
docs/workflow.md |
What a stx-workflow declaration may say — the verbs, the step scope, the statuses, the record and the store contract |
libs/messaging/stx-workflow/stx-workflow/README.md |
The workflow engine — checkpointing rather than replay, what at-least-once asks of a step, why a fan-out merges explicitly, and why a child is an instance rather than a call |
libs/messaging/stx-workflow/stx-workflow-db/README.md |
Where instances live — why one module and not three, and what each store does with an index, a lease and retention |
libs/messaging/stx-workflow/stx-workflow-spring/README.md |
The Spring auto-configuration — why an integration is a module beside its library, and why nothing is inferred about where instances live |
libs/messaging/stx-workflow/stx-workflow-ktor/README.md |
The Ktor plugin — and why own/publish/required had to become public |
docs/migrations.md |
What a stx-migrations migration may say — the version, the statuses, the runner's order, the ledger contract, the lock, and what a killed process leaves |
libs/data/stx-migrations/stx-migrations/README.md |
The migration library — why the version is declared rather than parsed, why a gate throws where the prior art logged, and why neither Flyway nor Liquibase is underneath it |
libs/data/stx-migrations/stx-migrations-db/README.md |
Where the ledger lives — why one module and not two, and per store: the uniqueness, the lock, the instants, and how DDL reaches a database with no JDBC in front of it |
libs/data/stx-migrations/stx-migrations-ktor/README.md |
The Ktor plugin — why runBlocking inside install is what makes it a gate, why it goes after the connection plugin, and why sql { } / mongo { } are sugar over the one gate that is the contract |
libs/data/stx-migrations/stx-migrations-spring/README.md |
The Spring auto-configuration — why an InitializingBean and not a suspending listener, and why naming a store without its connection is an error rather than a shrug |
libs/data/stx-redis/stx-redis/README.md |
The Redis library — the cache, the lock, topics and streams, and what each one refuses to do |
libs/data/stx-redis/stx-redis-ktor/README.md |
The Ktor plugin — why one connection and not one per request, and what handing it to the container costs and does not |
libs/data/stx-redis/stx-redis-spring/README.md |
The Spring auto-configuration — why it is off unless asked for, and why stx.redis is not spring.data.redis
|
libs/data/stx-storage/stx-storage/README.md |
The object storage library — objects, and what a presigned URL or upload form can promise |
libs/data/stx-storage/stx-storage-ktor/README.md |
The Ktor plugin — and why it is the one whose config is required rather than defaulted |
libs/data/stx-storage/stx-storage-spring/README.md |
The Spring auto-configuration — why no credential has a default, and why its spec points at a dead port |
libs/core/stx-testing/README.md |
The test support — where a spec's server comes from, and what cleans a container up afterwards |
plugins/openapi/README.md |
The build plugin: settings, and what each choice needs on the consuming module's classpath |
examples/jpa-shop/README.md |
The Postgres catalogue — stx-jpa's CRUD extensions, transaction guard and audit layer end to end |
examples/spring-orders/README.md |
The order book — a spec-first REST API, keyset paging, an audit trail and two migrations |
server/oauth/README.md |
The work in progress, not an example — a GraphQL service where the SDL owns the types |
AGENTS.md |
Build commands, module layout, and the conventions this repo holds itself to |
Pull requests target develop, never main. Anything touching libs/ needs a changeset
(bun changeset) and a publish to mavenLocal before a consumer is built against it —
CONTRIBUTING.md explains why that second step is the one that catches a broken
POM. Be civil: Code of Conduct.
Found a vulnerability? Not an issue — SECURITY.md.
Apache-2.0. Third-party documentation cached under .agents/skills/*/references/ is
attributed in NOTICE.