Devani is plain PHP and vanilla JavaScript with no database and no build step. If you can read PHP, you can read all of Devani — the whole application ships as source in every release zip.
The core idea: the filesystem is the data model
A Devani site is a directory tree. Content lives in per-page folders, application state lives in JSON files, and rendering is a template wrapped around a content file. There is nothing else — no ORM, no migrations, no connection pool.
Request flow
- A request hits the web server (nginx in the standard install) and routes to PHP.
devani/page-render.phpruns: firstdevani_check_redirects()(consults the redirect table and 301s before any rendering), then a setup check (a site with no users redirects to the setup wizard), then the page renders — the shared template (header, navigation, footer, global styles) around the page'scontent.html.- Output is clean, semantic HTML. It's dynamic PHP per request, but with no queries and no heavy work, so it's fast bare and caches perfectly behind any CDN.
Application state: JsonStore
devani/JsonStore.php implements the storage layer the rest of the code calls through DevaniDB — a database-shaped API over JSON files in devani/data/storage/. It provides table-like structures with autoincrement ids (users, orders, reviews, MCP API keys, email logs), a settings key/value table, and file locking around writes. seedDefaults() runs on every construct and idempotently back-fills any missing default settings — which is how new settings appear on existing installs after an update.
Storage files are self-defending
Data files are stored with a .php guard (e.g. settings.json.php beginning with an exit sentinel), so a misconfigured server that executes them returns nothing instead of leaking contents. Direct HTTP access to content internals (content.html, meta.json, featured-image.txt) is additionally denied at the web-server level in the standard install.
Why no database is a feature
- Security: no SQL injection surface, no DB credentials to steal.
- Ops: backup = copy the directory; migrate = rsync it somewhere else.
- Portability: no hardcoded paths — everything resolves relative to the install (
__DIR__-based), so a site can move between servers and users freely. - Performance: reading a file the OS has cached beats a query, every time.
Next: Content storage layout.