agentsclimarketplace

Kora config hocon

Skill kora-projects/kora-skills/plugins/kora-v1/skills/kora-config-hocon

Agent Skills for Kora Framework — compile-time DI for Java/Kotlin backend development.

Install
npx -y skills add kora-projects/kora-skills --skill kora-config-hocon

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

HOCON configuration in Kora services via the config-hocon module and HoconConfigModule. Maps application.conf into type-safe interfaces with @ConfigSource (application config) and @ConfigValueExtractor (reusable/library config). Covers required vs @Nullable vs default-method values, environment substitution (${VAR}, ${?VAR}, default = ${?VAR} override), supported types (Duration, Period, Size, UUID, List/Set/Map, nested objects), injecting the raw Config with @Environment/@SystemProperties/@ApplicationConfig tags, and the config file watcher. Use when adding typed config to a Kora app, choosing @ConfigSource vs @ConfigValueExtractor, wiring credentials through env vars, or debugging "config value not found" graph build failures.

SKILL.md

11.8 KB, as published. Nobody here has run it

Kora Config HOCON

Artifact: ru.tinkoff.kora:config-hocon | Module: HoconConfigModule | Annotations package: ru.tinkoff.kora.config.common.annotation

HOCON is the recommended config format for Kora. The config-hocon module maps application.conf into type-safe interfaces at compile time. Define a config interface, annotate it, and inject it as an ordinary graph dependency through the constructor. There is no field injection and no runtime reflection — the annotation processor generates the extractor.

Quick Start

1. Dependencies (build.gradle)

dependencies {
    koraBom platform("ru.tinkoff.kora:kora-parent:1.2.17")

    // MANDATORY — without the annotation processor nothing is generated
    annotationProcessor "ru.tinkoff.kora:annotation-processors"

    implementation "ru.tinkoff.kora:config-hocon"
    implementation "ru.tinkoff.kora:logging-logback"
}

Kotlin uses ksp "ru.tinkoff.kora:symbol-processors" instead of annotationProcessor. All ru.tinkoff.kora:* artifacts inherit their version from the kora-parent BOM — never version them individually.

2. Enable the module on @KoraApp

package com.example.app;

import ru.tinkoff.kora.application.graph.KoraApplication;
import ru.tinkoff.kora.common.KoraApp;
import ru.tinkoff.kora.config.hocon.HoconConfigModule;
import ru.tinkoff.kora.logging.logback.LogbackModule;

@KoraApp
public interface Application extends
        HoconConfigModule,
        LogbackModule {

    static void main(String[] args) {
        KoraApplication.run(ApplicationGraph::graph);
    }
}

3. Config file src/main/resources/application.conf

app {
  name = "Task Management App"
  name = ${?APP_NAME}          # optional override: only applied if APP_NAME is set
  version = ${APP_VERSION}     # required: startup fails if APP_VERSION is missing
  environment = "development"
}

4. Typed config interface with @ConfigSource

package com.example.app;

import ru.tinkoff.kora.config.common.annotation.ConfigSource;

@ConfigSource("app")
public interface AppConfig {

    String name();

    String version();

    String environment();
}

@ConfigSource("app") binds the app section and registers AppConfig as a graph component.

5. Inject the config through the constructor

package com.example.app;

import ru.tinkoff.kora.common.Component;

@Component
public final class AppService {

    private final AppConfig config;

    public AppService(AppConfig config) {
        this.config = config;
    }

    public String describe() {
        return config.name() + " v" + config.version();
    }
}

@ConfigSource vs @ConfigValueExtractor

These are the two mapping styles. Pick by ownership of the config path.

@ConfigSource("path")@ConfigValueExtractor
Binds a fixed config pathYes — path is hard-codedNo — the path is chosen at extraction time
Registered as a graph componentYes, inject directlyNo, it only generates a ConfigValueExtractor<T>
Use forone stable application sectiona reusable shape mapped to several paths / library config
Use as a nested typeNoYes — nested objects inside a config interface

Rule of thumb: top-level config interface that maps one stable section → @ConfigSource. A shape reused at multiple paths, or a nested object/list element type → @ConfigValueExtractor.

Nested objects must use @ConfigValueExtractor

A nested interface that represents a sub-object (or list element) is not annotated with @ConfigSource — it is annotated with @ConfigValueExtractor. Only the outer interface that owns a fixed path carries @ConfigSource.

@ConfigSource("foo")
public interface FooConfig {

    String someString();

    BarConfig bar();          // mapped sub-object
    List<BarConfig> bars();   // mapped list of sub-objects

    @ConfigValueExtractor
    interface BarConfig {
        String someBarString();
        BazConfig baz();

        @ConfigValueExtractor
        interface BazConfig {
            String someBazString();
        }
    }
}
foo {
  someString = "value"
  bar = { someBarString = "s", baz.someBazString = "s" }
  bars = [
    { someBarString = "s1", baz.someBazString = "s1" },
    { someBarString = "s2", baz.someBazString = "s2" }
  ]
}

See references/config-source-reference.md for the reusable-shape pattern (extracting one @ConfigValueExtractor type at two paths via @Tag and ConfigValueExtractor.extract(config.get(path))).


Required, optional, and default values

By default every config method is required (NotNull) — a missing value fails the graph build at startup. There is no @DefaultValue annotation in Kora; defaults are expressed with a Java default method.

@ConfigSource("services.foo")
public interface FooServiceConfig {

    String bar();                 // required — fails fast if absent

    @Nullable
    String optionalBar();         // optional — null if absent

    default int baz() {           // default value when absent
        return 42;
    }
}
  • Required: plain method. Any missing value aborts startup with a clear error.
  • Optional: annotate with any @Nullable (jakarta.annotation.Nullable, javax.annotation.Nullable, or org.jetbrains.annotations.Nullable). In Kotlin use a nullable return type (fun bar(): String?).
  • Default: a default method (Java) / method with a body (Kotlin). Used only when the value is absent from the config.

There is no auto-invoked validate() hook. To validate values, do it in a component that consumes the config (e.g. in its constructor) and throw if invalid.


Environment variable substitution

Substitution is a HOCON feature resolved before mapping. Three forms:

app {
  required  = ${APP_URL}           # required: missing var → startup fails
  optional  = ${?APP_URL}          # optional: missing var → key is omitted
  withDefault = 8080               # default-then-override pattern:
  withDefault = ${?APP_PORT}       #   keeps 8080 unless APP_PORT is set
}

The idiomatic "default then optional override" pattern assigns the literal first, then re-assigns with ${?VAR} so the literal survives when the variable is unset. Externalize every credential and host this way. See references/hocon-syntax-reference.md for value references and string concatenation.


Supported value types

@ConfigSource / @ConfigValueExtractor map a broad set of types out of the box, including:

  • Primitives and boxed: boolean, int, long, double, float, short
  • String, BigInteger, BigDecimal, UUID, Pattern, Properties
  • Time: Duration ("250s"), Period ("1d" or 1), LocalDate, LocalTime, LocalDateTime, OffsetTime, OffsetDateTime
  • Size — byte sizes like 1Mb (decimal) / 1Mib (binary); a bare number means bytes
  • Any enum (matched by toString())
  • List<T>, Set<T>, Map<K,V>, Either<A,B> of the above
  • Nested objects via @ConfigValueExtractor

A list/set may be written as an array ["v1","v2"] or a comma string "v1,v2". For the full list see references/hocon-syntax-reference.md.

Note: the size type is ru.tinkoff.kora.config.common.Size. There is no DataSize type in Kora.


Injecting the raw Config

For a generic abstraction over the whole config you may inject ru.tinkoff.kora.config.common.Config. The resolved config layers environment variables, system properties, and the config file. Tags select a single layer:

TagWhat you get
(no tag)Full config: file + env vars + system properties
@EnvironmentEnvironment variables only
@SystemPropertiesSystem properties only
@ApplicationConfigConfig file only
@Component
public final class FooService {
    public FooService(@Environment Config config) { /* ... */ }
}

Prefer typed @ConfigSource interfaces over the raw Config: injecting Config directly means any config change refreshes every component that depends on it.


Config file resolution and the watcher

HoconConfigModule loads, in priority order:

  1. config.resource system property (a file on the classpath), if set
  2. config.file system property (a filesystem path), if set
  3. application.conf from resources, if present
  4. an empty config otherwise

reference.conf files (library defaults) are merged first, then application.conf is overlaid. This is the mechanism for selecting per-environment config files — point config.resource/config.file at the variant you want; there is no config.environment profile switch.

java -Dconfig.resource=application-prod.conf -jar app.jar
java -Dconfig.file=/etc/app/application.conf -jar app.jar

Kora watches the config file and rebuilds the affected part of the graph on change. Disable it with the KORA_CONFIG_WATCHER_ENABLED env var or the kora.config.watcher.enabled system property.


Common pitfalls

SymptomCause / fix
Startup fails: required config value not foundA non-@Nullable, non-default method has no value. Provide it, or mark @Nullable / add a default.
Nested config type not generatedNested object interfaces need @ConfigValueExtractor, not @ConfigSource.
${VAR} aborts startupRequired substitution and the env var is unset. Use ${?VAR} for optional or a default-then-override.
Nothing is generated at allMissing annotationProcessor "ru.tinkoff.kora:annotation-processors" (Java) / ksp "ru.tinkoff.kora:symbol-processors" (Kotlin).
Config not loadedapplication.conf not in src/main/resources/, or a config.resource/config.file override points elsewhere.
Expected a @DefaultValue / DataSizeNeither exists in Kora. Use a default method and Size.

References & assets

FilePurpose
references/config-source-reference.md@ConfigSource vs @ConfigValueExtractor, reusable shapes, library config factories
references/hocon-syntax-reference.mdHOCON syntax, substitution, includes, full supported-type list
assets/application.conf.templateBase HOCON config template
assets/AppConfig.java.templateTyped @ConfigSource interface template

Related skills

Source of truth

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.