Skip to content

Security

Kayan validates config against the schema you declare, but it is not trying to solve every security problem around build tooling.

The short version is:

  • Kayan treats config files as untrusted data.
  • Kayan treats the declared schema as trusted project code.
  • Kayan treats custom adapters as trusted build code.

That split is the most important part of the threat model.

By default, the Gradle plugin uses KayanValidationMode.SUBSET: it validates keys declared in the local schema and ignores undeclared keys so several modules can read one shared config file. If one module owns the whole config file and undeclared keys should fail the build, opt into KayanValidationMode.STRICT. See Validation and Multi-Module Shared Config for the tradeoff.

Kayan is mainly trying to protect build integrity and generated-source safety. If a config file drifts from the declared schema, the build should fail clearly instead of guessing what the author meant.

That means Kayan is designed to:

  • validate schema-declared keys instead of guessing what undeclared data means
  • reject unknown keys when KayanValidationMode.STRICT is enabled
  • enforce declared value types and nullability
  • require explicit flavor resolution
  • reject custom-only flavors that do not exist in the base config
  • generate Kotlin literals from parsed values rather than raw config text

For built-in types, config should become data in generated Kotlin, not code.

Kayan is not a secret-management system.

If a value should not appear in source control, generated Kotlin, schema artifacts, or normal Gradle configuration, it should not live in Kayan config. Use your platform’s secret-management or environment-specific secure storage instead.

Kayan also does not defend against hostile build logic. If someone can change build.gradle.kts, buildscript dependencies, or a custom adapter class, they already control code that runs in the build.

Built-in schema entries are data-driven. Custom adapters are code-driven.

That distinction matters. Kayan can validate adapter metadata and surface adapter failures with useful context, but it cannot make an untrusted adapter safe. A custom BuildTimeConfigAdapter can execute arbitrary logic and can render arbitrary Kotlin expressions.

If you use adapters, review them like any other Gradle plugin or build logic. See Custom Adapters for the adapter contract.

buildValue() is useful because it lets Gradle configuration read the same resolved values that generated Kotlin will expose later. It also means config can influence dependency wiring, task inputs, and other build decisions.

Kayan keeps this boundary narrow by requiring schema-declared keys and checked accessors, but it cannot protect a project from its own design choices. If your build uses buildValue() for important decisions, config changes should be reviewed with the same care as code changes.

  • Do not store secrets in Kayan-managed config.
  • Keep config review strict, especially for changes that affect buildValue().
  • Use KayanValidationMode.STRICT when a single module owns the full config file and unexpected keys should fail the build.
  • Treat adapter code as trusted code.
  • Let CI run config resolution so broken or suspicious changes fail early.

The full engineering threat model lives in the repository at THREAT_MODEL.md.