Bootstrap, Config & Database Connection
Validated against code on 2026-09-06.
How a Koovira request boots, where configuration comes from, and how it reaches the database. Everything else in this tree assumes the facts on this page.
Two bootstrap entry points
| Include | Use it for | What it emits |
|---|---|---|
config/common_includes.php | Full pages. The normal front-end bootstrap. | Session, DB handle, plus five platform-wide inline <script> blocks (client lockdown, layout guard, module guard). |
config/baseconfig/config.php | Headless / API / read-only pages. | The narrow bootstrap — just enough to get a DB handle and core defines. No inline JS. |
config/baseconfig/config.php is the minimum that yields a working $conn. The public devportal/index.php deliberately uses the narrow one (it is a logged-out, no-JS page) — a good example of when not to pull the full bootstrap. Validation scripts in _tools/ also use the narrow path.
config/baseconfig/config.php pulls in config/baseconfig/coredb.php, which is where the DB connection and the guarded PDO subclass live.
Environment resolution
coredb.php decides local vs production from the request host ($_SERVER['HTTP_HOST'], defaulting to localhost under CLI):
- Local when the host is
localhost,127.0.0.1,::1, or ends in.local/.test/
.localhost (a trailing :port is stripped before the suffix check — a fix for a real bug where koovira_live_new.test:8899 fell through to the production branch and sent a local visitor to koovira.com; see coredb.php:266-285).
- Production otherwise.
DB credentials and the site URL come from environment variables via kv_env() (loaded by config/baseconfig/env_loader.php + config/global_env.php), never from hard-coded literals. The literal defaults in coredb.php are last-resort only.
Validated local values (this machine, 2026-09-06):
| Define | Value | Source |
|---|---|---|
DB_NAME | koovira_newmasterdb_opt | .env DB_NAME (code default is koovira_new_live) |
DB_HOST | 127.0.0.1 | .env DB_HOST |
DB_PORT | 3306 | .env DB_PORT (code default 3307) |
| engine | MariaDB 10.11.14 | live SELECT VERSION() |
| tables | 1,797 | live information_schema |
Drift caught here: the code defaults (koovira_new_live, port3307) are not the live values —.envoverrides them tokoovira_newmasterdb_opton3306. Always read the live define, never the code default. This is why documentation must be validated, not assumed.
Koovira is MariaDB-only for local dev: coredb.php:563-574 blocks MySQL 8.x unless KV_ALLOW_LOCAL_MYSQL=1.
KVGuardedPDO — the guarded connection
The $conn handed to all code is not a plain PDO; it is KVGuardedPDO (coredb.php:136-232), a PDO subclass with three guarantees:
- No DDL from the web tier.
exec(),query(), andprepare()refuse any statement
starting with CREATE / ALTER / DROP / TRUNCATE / RENAME (comments stripped first) unless kv_schema_mutations_allowed() is true — i.e. KV_SCHEMA_MIGRATION_MODE is set. A blocked prepare() throws PDOException('Schema mutations are disabled for web requests'); a blocked exec()/query() returns 0/false. All schema changes go through the padmin-gated migration system, never through app/cron/deploy code.
- Optional SQL audit. When
KV_SQL_AUDIT_TABLESis on, every statement's verb + touched
tables are appended to a daily JSON log in the temp dir (kv_sql_audit_log()).
- Legacy-column shim.
query()/prepare()rewritecm.id→cm.rowId(a compatibility
patch for a renamed primary key on the customer_master alias cm).
PDO options set at connect (coredb.php:512-519): ERRMODE_EXCEPTION, FETCH_ASSOC default, EMULATE_PREPARES=false, buffered queries, SET NAMES utf8mb4.
Corrected 2026-09-06: this and the citations below were originally497-504; a same-day code change (theKV_QA_TESTDBmoney-test-double branch, ~30 new lines aroundcoredb.php:375) shifted everything after it down. Re-verified against the live file.
Clock alignment (money-critical)
After connecting, coredb.php:554-561 runs SET time_zone = '+05:30' so the DB session clock matches the app's Asia/Kolkata. This is done after connect (not via INIT_COMMAND) because persistent connections reuse pooled handles and INIT_COMMAND only fires on a new one. Revert without a deploy via KV_DB_TIMEZONE_IST=0. Rows written before this cutover (2026-08-18) are UTC; rows after are IST — relevant when reading historical timestamps.
Primary-key convention
Most tables use rowId as the primary key, not id (see the cm.id→cm.rowId shim above, and every SHOW COLUMNS in this tree). Foreign-ish references are by convention (buyer_id, seller_id, company_id, order_id, …) and are not enforced by declared FK constraints on most tables — joins are convention-based. Validate the exact column name per table; do not assume id.
Central includes loaded at bootstrap
coredb.php ends by requiring two platform services so they exist in every DB-touching context:
forms/doaction_helpers.php— shared helpers for the form-action dispatch layer (see the
module code maps; most writes flow through forms/doaction_*.php).
classs/KvFileStorage.php— the central upload sink (kv_store_upload()), FTP transport with
local fallback, wired so uploads are consistent platform-wide.
There is also a kv_payload_store overflow table (coredb.php:618-773): large text columns can be offloaded to kv_payload_store (gzip, dedup by hash) and hydrated back on read via kv_payload_store_hydrate_row(). If a text column reads as NULL but the feature clearly stored something, check kv_payload_store before concluding data loss.
See also
- database.md — schema conventions, how to inspect it.
- module-registry.md — the two module registries and their keys.
- ../METHODOLOGY.md — how to validate a claim.