Typed layered JSON and YAML config for Kotlin Multiplatform, JVM, and Android.
Kayan is a Kotlin Gradle plugin that turns layered JSON or YAML config into a typed
Kotlin object. Kotlin Multiplatform, JVM, and Android projects can read that object
without platform-specific BuildConfig wiring.
The name comes from the Arabic word كيان (Kayan), which means "entity", "structure", or "being".
JSON is valid YAML, but Kayan still makes the format choice explicit so your builds do not develop a split personality.
- Keep config in JSON or YAML while exposing a typed Kotlin API.
- Let the consuming app own the schema and generated property names.
- Resolve layered defaults and overrides deterministically at build time.
- Work in shared Kotlin across KMP, JVM, and Android modules.
- Reuse resolved values inside Gradle itself with
buildValue("key").
Add Maven Central to Gradle plugin resolution when consuming the published plugin:
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
}
}Add a config file:
{
"flavors": {
"prod": {
"api_base_url": "https://api.example.com",
"feature_search_enabled": false
},
"dev": {
"api_base_url": "https://dev.example.com",
"feature_search_enabled": true
}
},
"brand_name": "Example App"
}YAML works too:
flavors:
prod:
api_base_url: https://api.example.com
feature_search_enabled: false
dev:
api_base_url: https://dev.example.com
feature_search_enabled: true
brand_name: Example AppApply the plugin and declare your schema:
plugins {
kotlin("multiplatform") version "<kotlin-version>"
id("io.github.mohamadjaara.kayan") version "<kayan-version>"
}
kayan {
packageName.set("sample.generated")
flavor.set("prod")
schema {
string("api_base_url", "API_BASE_URL", required = true)
boolean("feature_search_enabled", "FEATURE_SEARCH_ENABLED")
string("brand_name", "BRAND_NAME")
}
}Generate the config source:
./gradlew generateKayanConfigUse the generated object from shared code:
import sample.generated.SampleConfig
val baseUrl = SampleConfig.API_BASE_URL
val searchEnabled = SampleConfig.FEATURE_SEARCH_ENABLED
val brandName = SampleConfig.BRAND_NAMEKayan writes generated source to build/generated/kayan/kotlin and adds the
directory to the appropriate source set.
For Kotlin Multiplatform projects that need one config API in shared code but different values per
target, add targets in the config file and configure Kayan targets:
brand_name: Example App
flavors:
prod:
targets:
android:
bundle_id: com.example.android
ios:
bundle_id: com.example.ioskayan {
packageName.set("sample.generated")
flavor.set("prod")
targets("android", "ios")
}Kayan generates an expect object into commonMain and matching actual object declarations into
the configured target source sets.
For non-standard source set names or custom target labels, use the DSL form:
kayan {
targets {
ios()
jvm("desktop")
sourceSet("appleMain", "ios-shared")
}
}Kayan is for non-sensitive build and app configuration. Schema values may appear in generated source or Gradle configuration, so keep API keys, passwords, tokens, and other secrets in dedicated secure storage.
buildValue()is experimental. Opt in with@file:OptIn(io.kayan.gradle.ExperimentalKayanGradleApi::class)when using the Gradle build-time API frombuild.gradle.kts.
When Gradle logic needs the same resolved config, use buildValue() directly in
build.gradle.kts:
@file:OptIn(io.kayan.gradle.ExperimentalKayanGradleApi::class)
val isSearchEnabled =
kayan.buildValue("feature_search_enabled")
.asBoolean()
dependencies {
if (isSearchEnabled) {
implementation("com.example:search-sdk:1.0.0")
}
}Use this for conditional dependencies, task inputs, and other configuration-time
decisions. Provider variants such as asStringProvider() defer resolution for
lazy task wiring.
- Overview
- Quick start
- Gradle usage
- Build-time config access
- Resolution order
- Config file shape
- Schema types
- Validation
- Schema export
- BuildConfig migration
- White-label setup
- Commands
- Threat model
Build everything:
./gradlew buildRun plugin tests:
./gradlew :gradle-plugin:testThe app in sample/ consumes the local plugin and uses its generated config on
desktop, web, and Apple targets.
Run the desktop sample:
./gradlew -p sample runRun the web sample:
./gradlew -p sample wasmJsBrowserDevelopmentRunMore sample details are in sample/README.md.