Add SQLite groundwork: Lib\Db, migrations, and a project-owned data dir

Lib\Db (novaconium/lib/Db.php) is a thin, no-ORM PDO wrapper — lazy-connect
like Lib\Csrf, Db::query() as the only query-running helper (prepared
statements only, no interpolation shortcut, per Lib\Input's existing
security stance). Plain .sql migrations under App/migrations/, applied in
filename order and tracked in an auto-created schema_migrations table, run
automatically on first connection or via novaconium/bin/migrate.php.

Data lives in a new top-level data/ directory rather than novaconium/ or
public/ — outside public/ so it's never web-accessible, and outside
novaconium/ since that directory gets wholesale-replaced by the "Updating
the framework" workflow, which would otherwise destroy it on every update.

New config keys: db_driver (only 'sqlite' implemented), db_path,
db_migrations_dir. Documented at /admin/docs/database and in AGENTS.md.
Closes the "SQLite groundwork" backlog item in novaconium/ISSUES.md.
This commit is contained in:
code
2026-07-14 03:33:38 +00:00
parent 672f997d8b
commit a3b996719a
13 changed files with 314 additions and 47 deletions
+29
View File
@@ -130,6 +130,35 @@ management"); don't extend this class toward multi-user/session-based
auth — that's a separate, larger feature that
will replace it.
`Lib\Db` (`novaconium/lib/Db.php`) is the SQLite groundwork tracked in
`novaconium/ISSUES.md` — a thin, no-ORM PDO wrapper, `Lib\` (not `App\`) so
a project can override it via `App/lib/Db.php` like any other `Lib\` class.
`Db::query(string $sql, array $params = [])` (prepare+execute) is the only
query-running helper — never add a string-interpolation shortcut; see
`Lib\Input`'s doc-comment, which already commits this project to
parameterized queries as the sole SQL-injection defense. Connection is
lazy (opened on first `Db::query()`/`Db::connection()` call, same shape as
`Lib\Csrf`'s lazy session start), and migrates automatically at that point:
plain `.sql` files under `App/migrations/` (`config['db_migrations_dir']`),
filename-ordered, tracked in an auto-created `schema_migrations` table, run
once each. `novaconium/bin/migrate.php` runs the same migration step
explicitly (e.g. from a deploy script) without serving a request first.
**`config['db_path']` must stay outside both `public/` (would be
web-accessible) and `novaconium/`** — unlike `cache_dir`/`contact-log.txt`,
which are disposable, a SQLite file is data a project can't afford to lose,
and `novaconium/` gets wholesale-replaced by the "Updating the framework"
workflow (`/admin/docs/getting-started`: `rm -rf novaconium && cp -r
<new-novaconium>`). The default (`data/novaconium.sqlite`) lives in a new
top-level `data/` directory instead — project-owned like `App/`, gitignored
per-file (`*.sqlite`/`-journal`/`-wal`/`-shm`, with a tracked `.gitkeep` so
the directory exists in a fresh clone) rather than wholesale like
`public/cache/`, since a project might reasonably want other non-DB files
there later. Only `App/migrations/` is scanned for now (the framework ships
no core tables of its own yet) — if a future framework feature needs a
shipped migration, extend this to the same App-over-novaconium two-root
scan `Overlay.php` already does for pages/lib, don't invent a second
mechanism.
## Running it
```