Documentation
Architecture
How DesertCMS separates static public output, private originals, admin CGI, modules, analytics, comments, ratings, and the shop.
ARCHITECTURE.mdDesert Archive CMS is a hybrid static CMS:
httpdserves public generated files directly./adminis handled by a Perl CGI app throughslowcgi.- SQLite stores users, sessions, content, media metadata, theme files, revisions, backups, and audit logs.
- Photo originals stay outside the webroot. Public pages only reference generated derivatives.
- Production
httpdallows 64 MB admin request bodies for full-resolution JPEG uploads; larger public delivery is still avoided by publishing only downscaled derivatives. - Editable theme files live under
/var/desertcms/themesso application code can remain root-owned. - Publishing also refreshes
sitemap.xml,robots.txt, tag and collection archive pages, andredirects.httpd.conf. - Public analytics are first-party: generated pages post page views to
/analytics/collect, which stores path, referrer, timestamp, HMAC-hashed IP/user-agent values, and locally resolved GeoIP fields in SQLite. - Public comments are first-party: generated post pages load
/comments/thread, post new comments to/comments/create, and check cached-browser reply notices through/comments/notifications. - Public ratings are first-party: generated post pages load
/ratings/summaryand submit one 1-5 star vote per visitor IP hash through/ratings/vote. - The image-rights shop is first-party commerce served as a module from the main site at
/shop. It uses the main deployment config, database, uploaded media records, and public derivatives rather than contributor-site provisioning.
Deployment Boundary
/usr/local/www/desertcms/ application code
/etc/desertcms.conf site config
/var/desertcms/desertcms.sqlite database
/var/desertcms/originals/ private originals
/var/desertcms/backups/ timestamped backups
/var/desertcms/themes/ editable theme files
/var/www/htdocs/desert-archive/ generated public webroot
/var/www/run/desertcms.sock slowcgi socket visible to httpd
/shop shop module route forwarded to the main CGI app
Request Flow
- Public requests are handled by
httpdas static files. /admin,/analytics,/comments,/ratings, and/shoprequests are forwarded toslowcgi.- Shop module requests for
/shop,/shop/checkout,/shop/success,/shop/cancel, and/shop/stripe/webhookare handled by the same CGI app with/etc/desertcms.conf. slowcgirunsbin/desertcms.cgias_desertcms.- The CGI app loads
/etc/desertcms.conf, connects to SQLite, authenticates the admin where needed, and renders HTML.
Security Intent
- Dedicated
_desertcmsuser. - Writable data isolated under
/var/desertcms. - Public generated files isolated under
/var/www/htdocs/desert-archive. - Shop listings reference public display derivatives only; checkout and fulfillment never serve private originals.
- Admin sessions use random tokens stored as SHA-256 hashes.
- Anonymous comment names and reply notifications are browser-cached; the database stores only the display name, comment body, and HMAC hashes for the browser token, IP, and user-agent.
- Passwords use PBKDF2-HMAC-SHA256 with per-password salt.
- Installer-created temporary CMS admins are forced to set a permanent username and password on first login.
- The CMS enforces a single active admin account.
reset-adminis the controlled recovery path: it disables other admins, revokes existing sessions, and forces first-login setup. - CSRF tokens are derived from the session token and app secret.
- Login attempts are rate limited.
- Security headers are applied to admin responses.
- OpenBSD
pledgeandunveilare used when available.
Editor Direction
The editor stores ordered responsive blocks instead of absolute-positioned boxes. That preserves drag-and-drop editing while keeping mobile rendering reliable.
Publishing Artifacts
Every publish or rebuild refreshes the full public surface:
- Page and post HTML.
/posts/index./tags/<slug>/and/collections/<slug>/archive pages./sitemap.xmland/robots.txt.- Static redirect fallback pages.
redirects.httpd.conf, containing OpenBSDhttpdlocation blocks that can be copied into the public server block.
Analytics GeoIP
Visitor locations are resolved locally from analytics_geoip_ranges inside SQLite. Runtime analytics collection does not call a third-party geolocation API. Load a licensed city database with bin/desertcms-maint.pl geoip-import, then run geoip-backfill once if older analytics rows already exist.
The importer supports simple CSV/TSV rows with either network,country,region,city or start_ip,end_ip,country,region,city. It also supports GeoLite2-style city CSV files by passing both the blocks and locations CSV paths.
Shop Commerce
The shop adds shop_listings, shop_orders, and shop_stripe_events tables. Admin pricing lives at /admin/settings/modules/shop, with personal, commercial, and full-rights prices per uploaded photograph. Public checkout is hosted from /shop; the catalog is /shop, checkout posts to /shop/checkout, and Stripe webhooks post to /shop/stripe/webhook.
A paid full-rights webhook marks the order paid, records the full-rights sale timestamp, disables every rights option for that listing, and removes it from the catalog.
Asset Boundary
Admin and default theme assets are local. Editing and social icons are inline SVG, CSS is served by the app or copied into the public webroot, and no CDN-hosted fonts, scripts, icon kits, or stylesheets are required. tools/check-local-assets.pl audits runtime admin/theme files before deployment.