Persistence & Storage Format
This page documents the JSON formats used to persist and transport Konditional configuration:
- Snapshot: the full configuration payload (
ConfigurationSnapshotCodec.encode(...)) - Patch: an incremental update payload (
ConfigurationSnapshotCodec.applyPatchJson(...))
These formats are intentionally storage-agnostic: you can store the JSON in files, a database, or ship it over the network.
Mental model: what is persisted
At persistence time, the library serializes a list of flag definitions:
FeatureId format (key)
Each flag is stored under a stable FeatureId string:
feature::${namespaceIdentifierSeed}::${featureKey}
Where:
${namespaceIdentifierSeed}isNamespace.identifierSeed(defaults to the namespaceid)${featureKey}is the feature key (typically the Kotlin property name)
Example (Global namespace, darkMode):
feature::global::darkMode
Backward compatibility: older snapshots may contain value::${namespaceIdentifierSeed}::${featureKey}. These are
normalized on load.
Value encoding (defaultValue / rule value)
Both defaultValue and each rule's value are encoded as a discriminated union (tagged object). The type field
acts as a discriminator that determines
which variant is being used:
{
"type": "BOOLEAN",
"value": true
}
Supported shapes:
| Kind | JSON shape |
|---|---|
| Boolean | { "type": "BOOLEAN", "value": ${boolean} } |
| String | { "type": "STRING", "value": "${string}" } |
| Int | { "type": "INT", "value": ${int} } |
| Double | { "type": "DOUBLE", "value": ${double} } |
| Enum | { "type": "ENUM", "value": "${enumName}", "enumClassName": "${fqcn}" } |
| Data class | { "type": "DATA_CLASS", "dataClassName": "${fqcn}", "value": { ...primitive fields... } } |
The DATA_CLASS representation stores a primitive map of fields along with the fully qualified class name.
The generated OpenAPI schema includes a discriminator block that maps each type value to its corresponding schema
reference (e.g., "BOOLEAN" →
#/components/schemas/BooleanFlagValue).
Version range encoding (versionRange)
Rules may include a versionRange object. Like flag values, it uses a discriminated union pattern where the type
field determines which variant and
required fields apply:
{
"type": "MIN_AND_MAX_BOUND",
"min": {
"major": 2,
"minor": 0,
"patch": 0
},
"max": {
"major": 4,
"minor": 0,
"patch": 0
}
}
Valid type values:
UNBOUNDEDMIN_BOUND(requiresmin)MAX_BOUND(requiresmax)MIN_AND_MAX_BOUND(requiresminandmax)
Locale and platform values are serialized as their stable ids (LocaleTag.id / PlatformTag.id). The built-in
AppLocale and Platform enums use their enum names as ids, so existing snapshots remain stable.