Architecture & Zero-Database Data Model
Standalone Qwiki separates application logic from live portal state using flat JSON manifests and filesystem directories. No SQL engine, cache daemon, or build step is required.
Directory Layout
standalone-qwiki/
├── index.php # Front controller, clean URL router & reader UI
├── .htaccess # Apache clean URL routing & .json/.md/content protection
├── qwiki.json # Portal settings, extensions, category tree & chapter metadata
├── users.json # Bcrypt password hashes, roles, 2FA TOTP secrets & personal API keys
├── api/
│ ├── admin.php # Authenticated UI REST actions & extension handler delegation
│ ├── feed.php # Per-category RSS/Atom syndication endpoint
│ ├── llm.php # Key-authenticated LLM/AI Agent retrieval & OpenAPI endpoint
│ ├── publish.php # Headless publishing, category provisioning & media upload API
│ └── search.php # Server-side full-text search across all enabled page types
├── assets/
│ ├── css/ # Core design system + cascading themes (theme-*.css)
│ ├── js/ # Vanilla JS UI modules, soft-lock client & qrcode.min.js
│ └── extensions/ # Self-contained page_type and utility plugins
├── bin/ # Operator CLI tools (reset-2fa.php, sync-subwiki-docs.php)
├── content/ # Document files (.md, .html, .pdf, .form.json) + .locks/ + .logs/
├── data/ # Protected governance state (revisions/, trash/, talk/)
├── lib/Core/ # PHP core classes (Config, Auth, Navigation, DocumentService, etc.)
├── templates/modals/ # Server-rendered modal dialogs
└── uploads/ # Uploaded images, videos & remote transclusion cache
Request & Storage Flow
flowchart TD
Req["HTTP Request (/category/slug or /api/*.php)"] --> Router["index.php or api/*.php"]
Router --> Auth["Auth & LlmAccess (Session / API Key / Share Key)"]
Auth --> Config["Config::load() loads qwiki.json & users.json"]
Config --> Nav["Navigation (Tree, Visibility & Status Gating)"]
Nav --> DocSvc["DocumentService (SHA-256 Check, Revision Snapshot, Atomic Write)"]
DocSvc --> Files["content/ & data/ (revisions, trash, locks, logs)"]
Nav --> ExtMgr["ExtensionManager (assets/extensions/*/manifest.json)"]
Core Data Contracts
qwiki.json: Stores global settings (title,logoText,theme,defaultBook,requireLoginToView,twoFactorPolicy,extensions,smtp,llmKeys,publishApiKey,revisions), and the recursivebooksarray containing categories (type: "folder"), hyperlinks (type: "link"), and chapters (slug,docId,title,type,file,status,shareKey,publicShareable,visibility).- Immutable
docId:Navigation::ensureDocIdForChapter()assigns a persistentdocIdper chapter so revision history (data/revisions/{docId}/) survives title renames, slug updates, and category moves. - Unified
DocumentService::replaceContent(): Routes UI edits, file replacements, form/HTML saves, revision restores, and headlessapi/publish.phpoverwrites through one pipeline with SHA-256 change detection (skipping duplicate revision snapshots when bytes are unchanged), HTMLdata-qwiki-statussynchronization, and active soft-lock migration on relocation. - Protected
data/Isolation:data/revisions/,data/trash/, anddata/talk/carry dual Apache 2.2/2.4.htaccessdeny rules and are blocked from front-controller routing inindex.php. Core self-updates preservedata/while full backups include it.