KoniKode · Software built with intention

KNOWLEDGE BASE

Documentation

DOCUMENTATION
CURRENT APPLICATIONClean Framework
DocumentationClean Framework

Clean Framework · DOCUMENTATION

Configuration

Configuration

Bounded candidate reference — 2026-10-20: 0.2.0-rc.1 is committed locally through 84f415b3098af5653577b2c65eea31fca21df79c, unpublished and not production-approved; stable PHP >=8.5.11 <8.6.0 is required. This is documentation, not adoption: this knowledge base retains its installed 0.1.0-rc.1 dependency and PHP 8.2.11 local profile. Dated PHP 8.2 evidence is historical; earlier dirty-source reports retain their original test provenance, not retroactive clean-commit proof. Candidate examples are illustrative: API signatures were reviewed, but candidate code was not executed by this editorial update. See current checkpoint, open criteria and adoption boundary.

Optional module preferences are now bounded non-executable config/modules.json, with no PHP fallback. General application configuration/controllers/views remain PHP. See module activation; no KB adoption occurred.

Optional capability activation has one app-owned config/modules.json policy with independent environment overrides, explicit catalog/readiness and shared guards. It never replaces security/credentials. See module activation.

Applications define a value-free ConfigSchema in PHP and environment bindings beside their composition root. The framework supplies loading, conversion, validation, immutability, and redaction semantics; it does not know application secret values.

Environment selection and precedence

APP_ENV accepts DEV, DEVELOPMENT, TEST, TESTING, STAGE, STAGING, PROD, or PRODUCTION, normalized to dev, test, stage, or prod. Missing or unsupported values fail startup. Loader exposes an explicit missing-environment compatibility flag for an existing application's temporary migration from the old implicit-PROD behavior; new applications must not enable it.

For a selected environment, values are merged in this order (later wins):

  • base PHP configuration;
  • config/environments/<environment>.php;
  • a generic runtime/local variable;
  • <ENVIRONMENT>_ prefixed runtime/local variable.

For example, STAGE_DB_HOST overrides DB_HOST only under APP_ENV=STAGE. It does not alter DEV, TEST, or PROD. Runtime environment variables beat values in a local .env file. Parsing .env does not call putenv().

Use environment-prefixed variables for database, storage, session, queue, provider, and OAuth values. Generic variables are a deliberate fallback and should be limited to settings that are truly identical. STAGE and PROD secrets should be injected by the host rather than written to .env.

Adding a setting

Add a non-secret default, an environment overlay (including test.php), a binding, and a schema definition:

use Clean\Framework\Config\ConfigDefinition;
use Clean\Framework\Config\ConfigSchema;
use Clean\Framework\Config\EnvironmentSource;
use Clean\Framework\Config\Loader;

// $root is the consuming application's absolute directory, not public/.
$loader = new Loader(EnvironmentSource::fromRuntime($root . '/.env'));
$schema = new ConfigSchema([
    new ConfigDefinition('mail.timeout', type: 'int', required: true, minimum: 1, maximum: 30),
    new ConfigDefinition('features.preview', type: 'bool', required: true),
    new ConfigDefinition('mail.api_key', secret: true, requiredIn: ['stage', 'prod']),
]);

$config = $loader->load($root . '/config/app.php', $root . '/config/environments', [
    'mail.timeout' => ['MAIL_TIMEOUT', 'int'],
    'features.preview' => ['FEATURE_PREVIEW', 'bool'],
    'mail.api_key' => ['MAIL_API_KEY', 'string'],
], schema: $schema);

load() takes a base PHP file path and an overlay directory path, not arrays of loaded settings. The fragment assumes those files declare non-secret defaults for mail.timeout and features.preview; required secrets must be injected for the selected environment. For a complete runnable composition, see APPLICATION_WALKTHROUGH.md.

Supported conversions are string, bool, int, and float. Invalid typed values fail startup rather than silently changing behavior. Definitions can declare required/all-environment or environment-specific requirements, types, numeric or string-length ranges, regular expressions, URL/timezone/identifier formats, allowed environments, allowed values, and secret handling.

The returned Config implements ConfigRepository and exposes no mutation API. Schema validation completes before a kernel or worker is created.

Secret rules

  • Never commit a populated .env.
  • Prefer host-injected secrets in STAGE and PROD.
  • Use distinct credentials for DEV, STAGE, and PROD.
  • Never print config values from doctor or exception responses.
  • Do not place provider/account keys in framework defaults.
  • Rotate a secret immediately if it enters version control or logs.

Schema-declared secrets are stored as SecretValue. String interpolation, var_dump(), and JSON encode only produce [REDACTED]; ordinary PHP serialization is rejected. A trusted adapter must call $config->secret('provider.token')->reveal() explicitly at its composition edge. The wrapper is a disclosure guard, not encryption.

Config::require() rejects missing critical values. Config::missing() lets a non-mutating doctor command report only missing key names. config:check revalidates the active schema and reports only the environment and definition count, never values.

The neutral skeleton contains independent dev.php, test.php, stage.php, and prod.php overlays plus an application-owned config/schema.php example.

Database composition binds DB_CHARSET and DB_TIMEZONE. The charset defaults to utf8mb4; the time zone defaults to +00:00 and accepts only MySQL-safe fixed offsets from -13:59 through +14:00. Use a fixed offset here rather than relying on server-local or named-zone tables, and convert application display times outside the persistence boundary.

Migration composition binds MIGRATION_LEDGER_TABLE, MIGRATION_LOCK_NAME, and MIGRATION_APPLIED_BY independently per environment. The neutral defaults are cf_migrations, clean-framework-migrations, and local-cli. The actor is a safe release/command identity, not a credential or a person's free-form name. Production backup/checkpoint verification is an application/deployment service binding rather than a committed configuration token.

HTTP routing configuration is application-owned as well. APP_BASE_PATH configures both request stripping and named URL generation. HTTP_CANONICAL_PATH_MODE accepts normalize, redirect_safe, or strict; the safe-redirect mode redirects only GET/HEAD aliases and rejects unsafe method aliases. TRUSTED_PROXIES is a comma-separated exact-IP/CIDR list and must contain only deployment-controlled peers. Body, file, upload-byte, and JSON-depth environment bindings remain global ceilings; routes may enforce smaller application-specific limits in middleware.

Editorial provenance

Source: docs/CONFIGURATION.md. Source SHA-256: ae582279b643190e63d307fdc8fc8c4b8228d6d5bbd3027763bfa014e97db60e.

Framework candidate reference; editorial checkpoint 2026-10-20. See candidate status and adoption boundary.

EXPLORE FURTHER

Drag a divider or use Left and Right Arrow keys to resize. Hold Shift for larger steps. Enter hides or restores the sidebar; Home and End select its minimum or maximum width. Double-click resets the width.

Install KoniKode