Appendix: Configuration Reference¶
Every key Kinetis and its packages read from the environment, the exact
mechanics behind a named connection, and why .env sits outside the AOT
cache. For the task-first path — .env loading, typed Config access,
and when a named connection is practically relevant — see Configuration.
Named connections¶
Any storage technology — Redis, a SQL database, and so on — can be configured more than once under a name, alongside the usual unnamed default connection. The name is inserted, uppercased, as a segment right after the first underscore in the key:
REDIS_HOST=cache.internal # default
REDIS_CACHE2_HOST=cache2.internal # named "cache2"
DB_HOST=db.internal # default
DB_DB2_HOST=db2.internal # named "db2"
Config::scopedKey(string $key, string $connection = 'default'): string
is the shared helper every technology’s connection builder — see
Database for kinetis/database-bridge’s ConnectionFactory
and RedisSimpleCache — uses to compute which exact variable to read:
Config::scopedKey('REDIS_HOST'); // 'REDIS_HOST'
Config::scopedKey('REDIS_HOST', 'cache2'); // 'REDIS_CACHE2_HOST'
Config::scopedKey('FILESYSTEM_S3_BUCKET', 'archive'); // 'FILESYSTEM_ARCHIVE_S3_BUCKET'
The segment always lands after the first underscore, so a key with a
longer prefix splits at that same point: FILESYSTEM_S3_BUCKET becomes
FILESYSTEM_ARCHIVE_S3_BUCKET, not FILESYSTEM_S3_ARCHIVE_BUCKET.
'default' resolves to the plain, unprefixed key — a connection you
never name behaves exactly as if this feature did not exist.
Every package bootstrap reads its own selector unscoped — DB_CONNECTION,
QUEUE_CONNECTION, FILESYSTEM_DRIVER, MAILER_DSN,
SEARCH_OPENSEARCH_HOST, SEARCH_ELASTICSEARCH_HOST, SESSION_DRIVER,
BROADCAST_DRIVER — and wires the default connection alone. A named
connection is never resolved automatically: build one explicitly in
bootstrap.php and register it under an id of your own, or construct it
where it is needed:
DB_REPORTING_CONNECTION=pgsql
DB_REPORTING_HOST=reporting.internal
DB_REPORTING_NAME=reports
DB_REPORTING_USER=reports
DB_REPORTING_PASSWORD=secret
use Kinetis\DatabaseBridge\ConnectionFactory;
return static function (AppScope $app, Config $config): void {
// kinetis/database-bridge already binds the default DB_* connection
// under its own dialect contract.
$app->instance('db.reporting', ConnectionFactory::fromConfig($config, 'reporting'));
};
See Configuration’s “Named connections” for the short version, and Bootstrapping’s “Binding an application service” for the ordinary, non-database case.
Overriding the default Config registration¶
AppScope::boot() registers Config::fromEnvironment() unless you’ve
already registered your own — useful for a test double, or a fully
computed configuration that doesn’t come from the real environment at
all:
use Kinetis\Config\Config;
use Kinetis\Container\AppScope;
$app = new AppScope();
$app->instance(Config::class, new Config(['DB_HOST' => 'test-db']));
$app->boot(); // your registration above is kept, not overwritten
This follows the same registration-before-boot discipline any other
AppScope service does — see Bootstrapping for registering an
application service from bootstrap.php, and Appendix: Container Lifecycle for the
full registration-lock rule.
.env loading mechanics¶
Every framework-owned entry point — Kinetis\Runtime\HttpStartup (the
whole of an application’s public/index.php), bin/kinetis, and
Kinetis\Testing\TestApplication (see Testing) — calls
Kinetis\Config\EnvFile::safeLoad($projectRoot) unconditionally, before
Kinetis\Runtime\AppEnvironment::detect(). APP_ENV itself may be
defined for the first time in .env rather than already set in the real
process environment, so detection has to run after the load.
The load writes through putenv(), $_ENV and $_SERVER, which is what
puts a .env value in reach of the plain getenv() both
Config::fromEnvironment() and AppEnvironment::detect() read. See
Core Concepts for where this sits in HttpStartup’s full boot
sequence.
Config and the AOT cache¶
Config and .env sit outside the AOT compilation Kinetis builds for
production (see Caching & AOT Compilation). That cache’s value is being reproducible
from source alone — delete it, rebuild it, get back the identical
artifact. Environment variables break that: the process that ran
bin/kinetis build and the one serving requests later can legitimately
have different values injected into them. Baking .env into a compiled
cache file would mean a changed value did nothing until someone rebuilt
the cache.
Every key in one place¶
Everything Kinetis and its packages read from the environment, grouped by
subsystem. Keys marked scoped follow the named-connection convention
above — DB_HOST becomes DB_REPORTING_HOST for a connection named
reporting. Application-defined keys (a JWT_SECRET your own bootstrap
reads via Config::required(), for instance) are yours to invent and are
not listed here.
The Default column uses three forms. A literal value — including
(empty) for a list — is what the key falls back to. — means the key
has no default; what leaving it out does is in the Purpose cell.
(unset: …) marks an activation gate: the package or subsystem stays
inert until the key is set, and the cell names what you get until then.
(required for X) is the opposite — once X is selected the key has no
default left, and reading it throws naming the key.
Application (core)¶
Key |
Default |
Purpose |
|---|---|---|
|
|
|
|
— |
Comma-separated |
|
|
Request-body cap in bytes, enforced against declared |
|
(empty) |
Comma-separated addresses and CIDR ranges naming the deployment’s own edge. Empty trusts no peer, and an entry that is neither an address nor a CIDR range is refused at startup. Runtime Adapters owns the forwarded-header policy this configures. |
Security headers (core)¶
Each key below carries its header’s value verbatim, except the three
HSTS keys, which compose Strict-Transport-Security between them. For
the verbatim keys, an empty value omits the header and the literal off
— in any case — omits it too, so a header with a built-in default can be
switched off without sending an invalid value. See Middleware.
Key |
Default |
Purpose |
|---|---|---|
|
|
|
|
|
|
|
— |
|
|
— |
|
|
— |
HSTS max-age in seconds. Unset means the header is not sent; an explicit |
|
|
Appends |
|
|
Appends |
|
— |
|
|
— |
|
|
— |
|
X-Content-Type-Options: nosniff is always sent and is not configurable.
A header already present on the response is never replaced, so one route
can set its own policy and keep it.
Discovery restriction¶
All optional; comma-separated sub-paths relative to each PSR-4 base directory, for large applications that want a bounded scan (see CLI).
Key |
Restricts the scan for |
|---|---|
|
HTTP controllers ( |
|
CLI commands ( |
|
MCP tools and resources ( |
|
Global middleware ( |
|
Event listeners ( |
|
Channel authorization callbacks ( |
These are read through getenv() at discovery time rather than through
Config, so a .env value works and a TestApplication config override
does not.
Database (kinetis/database-bridge) — all scoped¶
Read by kinetis/database-bridge’s ConnectionFactory.
DB_CONNECTION is what the bridge’s package bootstrap gates on, and it gates on
the key being absent, not blank: DB_CONNECTION= activates the package
and then fails on the dialect check.
Key |
Default |
Purpose |
|---|---|---|
|
(unset: no database) |
|
|
|
Server host. |
|
|
Per dialect; must be a valid TCP port (1–65535). |
|
|
Database name. |
|
|
User. |
|
(required for mysql/pgsql) |
Password. An empty value is a valid empty password, not a missing key. |
|
|
|
|
|
Connection charset; identifier characters only. |
|
— |
MySQL collation ( |
|
— |
|
|
— |
CA bundle path for the verify modes. |
|
— |
Client certificate for mutual TLS; requires |
|
— |
Client private key; requires |
|
— |
Seconds; must be a positive integer. Unset means no client-side connect timeout. |
|
— |
Postgres |
|
— |
MySQL protocol compression. On for |
|
|
Async drivers’ pool width; must be at least 1 — per worker thread under FrankenPHP, per worker process under RoadRunner (see Performance tuning). |
|
|
Connections opened at boot instead of first use — load-bearing for the mysqli driver under worker mode; must not be negative. |
Redis (kinetis/cache-redis, kinetis/queue-redis) — all scoped¶
REDIS_CLUSTER, REDIS_URL and REDIS_HOST are checked in that order,
and the first one set decides how the connection is addressed:
REDIS_CLUSTER=true selects the cluster client, otherwise REDIS_URL
wins over REDIS_HOST. A REDIS_URL carrying a password or a database
index wins over REDIS_PASSWORD and REDIS_DATABASE too. With none of
the three set, Redis is off — see Database for what
CacheInterface binds to then.
Key |
Default |
Purpose |
|---|---|---|
|
|
Use Redis Cluster mode. Supported by the cache; |
|
(required for cluster) |
Comma-separated seed nodes for Cluster bootstrap — |
|
— |
Full |
|
— |
Server host. |
|
|
Port; must be a valid TCP port (1–65535). |
|
— |
Password. |
|
|
Database index (single-node only; Cluster has no |
|
|
Operation budget, seconds — connect, reply, and cluster redirects together; must be positive. |
|
|
Connect over TLS. |
|
|
Verify the server certificate, discovered and redirected cluster nodes included. |
|
— |
CA certificate for verification. |
|
|
Key namespace the cache owns; letters, digits, underscores and dashes. |
Queue (kinetis/queue + backend packages)¶
Read by kinetis queue:work and kinetis/queue’s package bootstrap; the
backend-specific keys are scoped by QUEUE_CONNECTION_NAME. Setting
QUEUE_CONNECTION does two things beyond selecting a backend. It decides
which capabilities the bound backend has beyond QueueInterface —
redis, sql, and rabbitmq can clear a queue and sqs cannot, so
kinetis queue:clear refuses under QUEUE_CONNECTION=sqs, see
Queue’s “Clearing is a separate capability”. And it is what makes
a listener marked Kinetis\Events\ShouldQueue actually queue: the
bootstrap binds ListenerInvokerInterface to the queued invoker, where
leaving QUEUE_CONNECTION unset leaves core’s inline default in place
(see Events).
Key |
Default |
Purpose |
|---|---|---|
|
(unset: no queue) |
|
|
|
Which named |
|
|
Worker-level default attempts cap ( |
|
|
Seconds |
|
|
|
|
(required for sqs) |
AWS region. |
|
— |
SQS-compatible endpoint (LocalStack). One origin — scheme, host, optional port — and nothing else; unset leaves AsyncAws its regional table and refuses an ambient |
|
|
Allows an |
|
|
Seconds bounding each SQS request and each credential lookup; must be positive, and set above the longest long poll the application issues (at most a five-second slice). |
|
— |
Queue-name prefix for shared AWS accounts. |
|
(required for rabbitmq) |
|
|
— |
Queue-name prefix. |
AWS credentials are never read from Config — the SQS (and S3) clients
use AWS’s own default credential provider chain.
Migrations (kinetis/migrations)¶
Read by the migrate* commands, which connect through the same DB_*
keys as kinetis/database-bridge.
Key |
Default |
Purpose |
|---|---|---|
|
|
Which named |
File storage (kinetis/storage + kinetis/storage-s3)¶
The gate below is on the unscoped key: installing kinetis/storage alone
registers nothing, and FilesystemOperator binds only once
FILESYSTEM_DRIVER is set. FilesystemFactory::fromConfig(), called
directly, bypasses that gate — it reads the key scoped and falls back to
local. Every other key here is scoped.
Key |
Default |
Purpose |
|---|---|---|
|
(unset: no filesystem) |
|
|
(required for local) |
Local disk root path. Must be non-empty; |
|
(required for s3) |
Bucket name. |
|
(required for s3) |
AWS region. |
|
— |
Key prefix. |
|
— |
S3-compatible endpoint (MinIO) — one origin, addressed path-style. |
|
|
Allow an |
|
|
Seconds per S3 request — connect, idle and transfer. Must be positive. |
Mail (kinetis/mailer) — scoped¶
The gate below is on the unscoped MAILER_DSN; both keys are scoped when
MailerFactory::fromConfig() is called for a named connection.
Key |
Default |
Purpose |
|---|---|---|
|
(unset: no mailer) |
Symfony Mailer transport DSN ( |
|
|
Seconds per API send — idle and total. Must be positive. SMTP ignores it and carries its own timeouts from the DSN. |
Search (kinetis/search-opensearch, kinetis/search-elasticsearch) — scoped¶
Both engine packages read the same keys under their own prefix —
SEARCH_OPENSEARCH_ or SEARCH_ELASTICSEARCH_, written SEARCH_..._
below. The gate is on the unscoped ..._HOST; every key is scoped when
the engine’s fromConfig() is called for a named connection.
Key |
Default |
Purpose |
|---|---|---|
|
(unset: no client) |
One |
|
|
Accept an |
|
|
Seconds per request — idle and total. Must be positive. |
|
|
Largest response body accepted. Must be positive. |
|
— |
Basic-auth user. |
|
— |
Basic-auth password. |
|
|
Verify the server certificate. |
|
— |
Elasticsearch only: an API key, instead of a username and password. |
|
— |
Elasticsearch only: the key’s id, when it is held separately from its secret. |
Sessions (kinetis/session)¶
Key |
Default |
Purpose |
|---|---|---|
|
(unset: no store) |
|
|
|
Seconds a session stays readable from its last write; must be positive. Every write refreshes the browser cookie’s |
|
|
Cookie name. A |
|
|
Cookie |
|
|
Cookie |
|
|
The |
The redis and sql drivers each need their own package installed and
configured, and say which when they are not; Sessions & CSRF lists what
each one requires.
MCP (kinetis/mcp)¶
MCP_DISCOVERY_PATHS, in the discovery table above, also belongs to this
package.
Key |
Default |
Purpose |
|---|---|---|
|
(empty) |
Comma-separated exact |
|
|
Serves |
Telemetry (kinetis/telemetry)¶
Key |
Default |
Purpose |
|---|---|---|
|
(unset: tracing off) |
Collector’s OTLP/HTTP base URL. Without it the provider is a no-op, so telemetry is opt-in per environment rather than per install. |
|
|
The |
|
— |
Export-request headers, |
|
|
Exactly one of |
|
|
Sampling ratio for the two |
Broadcasting (kinetis/broadcasting)¶
BROADCAST_DRIVER and BROADCAST_ALLOWED_ORIGINS are read unscoped. The
Pusher connection keys are scoped, and the package bootstrap builds only
the default connection.
Key |
Default |
Purpose |
|---|---|---|
|
|
|
|
(required for pusher) |
App ID. |
|
(required for pusher) |
App key — also the value a browser client subscribes with. |
|
(required for pusher) |
App secret, used to sign trigger requests and private/presence channel authorizations. |
|
|
Server host the backend publishes to. |
|
|
Server port; must be a valid TCP port (1–65535). |
|
|
Connect over TLS. |
|
(empty) |
Comma-separated exact |
The keys above address the server your backend publishes to. Where the
WebSocket server is reachable from a browser is a separate address, and
no kinetis/broadcasting key holds it — the application owns that value
and hands it to its own client code. The pingpong example reads it from
its own BROADCAST_BROWSER_HOST/BROADCAST_BROWSER_PORT keys through
Config, the same way any application-defined key works. A browser
client needs that address and the app key, nothing else. See
Broadcasting.
See also¶
Configuration — the task-first guide:
.envloading, typedConfigaccess, and named connections.Bootstrapping — registering an application service or global middleware from
bootstrap.php.Appendix: Container Lifecycle —
AppScope’s registration-lock discipline, and howRequestScopedelegates to aConfigit never explicitly registered itself.Caching & AOT Compilation — the AOT cache’s reproducible-from-source invariant.
Appendix: System Layout — the
Kinetis\Confignamespace in the full system map.