Documentation
Contributor Sites
How contributor requests, approvals, subdomain provisioning, access grants, master oversight, and cross-site surfacing work in DesertCMS.
CONTRIBUTOR_SITES.mdContributor sites let the master DesertCMS install operate like a service provider for approved contributors. Each contributor gets a separate DesertCMS instance on a subdomain, while the master admin keeps oversight through the contributor-site queue and settings screens.
Contributor sites are not child accounts inside the master CMS. They have separate configs, SQLite databases, public roots, private data directories, sessions, media originals, generated pages, and admin users.
Admin Screens
Contributor management is split across two settings screens:
- Admin Settings > Contributors configures contributor email settings, the contributor domain root, contributor invitations, and public contributor request review.
- Admin Settings > Blueprints defines reusable contributor subCMS defaults used before invite acceptance or request approval.
- Admin Settings > Contributor Sites shows provisioned or queued contributor sites and provides enable, disable, destroy, and grant-access controls.
The Contributor Requests module is managed from Admin Settings > Modules. When enabled, it adds a page block for public applications and publishes /contributors/.
Required Settings
Set these before sending invites or accepting public requests:
- Contributor domain root: the parent domain for subdomains, such as
example.com. - Send email: the verified Postmark sender address used for invites, approvals, denials, setup emails, and access grants.
- Receiving email for contributor requests: the internal inbox that receives new application notices.
- Postmark server token: the Postmark token for sending transactional email.
For automatic subdomain provisioning, DNS must point contributor subdomains at the OpenBSD server. The practical setup is wildcard DNS:
*.example.com -> server IPv4/IPv6
Without wildcard DNS, create the individual A/AAAA records before the site queue worker can issue TLS for a contributor subdomain.
See Provider Integrations for the full DNS, Postmark, Stripe, Google Search Console, and IndexNow setup checklist.
Contributor Blueprints
Blueprints make contributor provisioning repeatable. Before an invite is sent or a public request is approved, the master admin chooses which blueprint the new subCMS should use.
A blueprint controls:
- enabled modules: Map, Gallery, Shop, Forms, Contributor Requests, and Documentation
- theme mode and light/dark theme presets
- site-wide SEO title and description defaults
- media, post, and page quotas stored on the contributor site row and applied in the contributor subCMS
- default pages created during provisioning
- whether contributor images can appear in the master Gallery
- whether contributor posts can appear in the master Posts page
DesertCMS creates a Standard Contributor blueprint automatically the first time blueprints are used. Admin Settings > Blueprints can edit that default or create additional profiles for different contributor tiers.
When a contributor site is created, DesertCMS stores both the selected blueprint_id and a JSON snapshot on the contributor_sites row and in the queued create job. The snapshot is intentional: changing a blueprint later affects future contributor sites, not sites already queued or created.
The OpenBSD site queue worker applies the snapshot after it creates the contributor database and before the final rebuild. It writes module, theme, shop, SEO, and quota settings into the contributor site's own SQLite database, creates any default pages that do not already exist, and then rebuilds the contributor public output.
Public Request Form
Add a Contributor Form block to any page. The generated form posts to /forms/contributor-request and requires:
- full name
- phone number
- age
- gender
- profile photo
- at least one showcase image
- 150-500 character bio
Accepted upload types are JPEG, PNG, and WebP. Request uploads are stored privately under the master site's data directory until approval.
The request handler rate-limits repeated submissions by HMAC-hashed IP address and stores HMAC-hashed user-agent context. It does not store raw IP addresses for contributor requests.
Request Review
Submitted requests appear under Admin Settings > Contributors > Requests.
The list shows the applicant name, email, status, submitted time, and actions. Review opens the full application with:
- contact details
- age and gender
- bio
- profile photo preview
- showcase previews
- approve and deny forms
Opening a new request marks it as reviewing. Denying a request marks it denied and sends the not-selected email to the applicant. Approving a request marks it approved, publishes the contributor profile image into the master public asset tree, creates a contributor site row, and queues provisioning.
Approvals include a Contributor blueprint selector. The quick approve action on the request list uses the current default blueprint unless the admin chooses another one. The full review page exposes the same selector above the approval note.
Subdomain Naming
Approved requests use the applicant's first name plus last initial as the preferred subdomain:
Alex Smith -> alexs.example.com
If that site id already exists, DesertCMS tries safe fallbacks and numeric suffixes. Invite-created sites keep the older invite behavior, which prefers the first name and then first-name plus last-initial on collision.
Provisioning Queue
The master admin does not create system files directly. Approval queues a create job in site_provisioning_queue. The root-owned site queue worker applies the job.
Admin Settings > Master Control is the operator dashboard for the contributor subCMS fleet. It summarizes active, pending, disabled, and queued sites; checks provider readiness for contributor root DNS, Postmark, Stripe, Google Search Console, and IndexNow; and lists each contributor site's stored domain, owner, status, config path, data path, public root, database file, DNS result, TLS certificate visibility, latest queue job, backup count, disk footprint, last public rebuild, shared DesertCMS version, and alerts.
Recent queue rows include a Review action. The review page shows the queued action, current status, raw job details, any failure text, and the durable step log from site_provisioning_events. The root worker records events as it validates the site row, provisions files, stores OpenBSD paths, writes acme-client and httpd config, validates httpd -n, restarts httpd, issues ACME certificates, rebuilds the site, sends credentials, and activates or disables the contributor site.
Failed jobs keep their original error history. Use Retry from the queue row or job review page after fixing DNS, Postmark, filesystem, or OpenBSD config problems. Retry creates a new queued job copied from the failed job and records a retry event, so the failed attempt remains available for audit.
If existing contributor sites were registered before path metadata was stored, Master Control can repair them from the admin:
- Click Repair stored paths. DesertCMS fills missing OpenBSD-standard paths such as
/etc/desertcms-<site>.conf,/var/desertcms-sites/<site>, and/var/www/htdocs/desertcms-<site>. - After the page reloads, click Create missing backups. DesertCMS creates a first backup for contributor sites that do not already have one.
The path repair only fills empty stored paths. It does not overwrite non-empty custom paths.
Standalone domains such as desertcms.com are not contributor subCMS entries. If the same VPS serves another master CMS instance, list that instance config in the primary config instead:
standalone_master_configs = /etc/desertcms-desertcms.conf
The OpenBSD site queue worker preserves those standalone master configs in generated httpd and acme-client config, while Master Control and public contributor aggregation only use strict subdomains of the configured contributor root.
The worker:
- runs as root from cron or by manual command
- provisions the contributor data directory
- writes
/etc/desertcms-<site>.conf - creates the contributor public root
- initializes the contributor SQLite database
- applies the selected contributor blueprint snapshot
- creates or resets the contributor admin user
- rebuilds the contributor public site
- rewrites
httpdandacme-clientconfig - validates
httpd -n - issues ACME certificates when needed
- restarts or reloads services only after validation
- records step-by-step provisioning events for Master Control review
Install the worker on OpenBSD with:
doas perl /usr/local/www/desertcms/tools/openbsd-apply-site-queue.pl --install-cron
Manual one-off processing:
doas perl /usr/local/www/desertcms/tools/openbsd-apply-site-queue.pl --max-jobs 10
Email Flow
Contributor email uses the master site's Postmark settings.
Request submission:
- stores the request locally
- emails the configured receiving email with the applicant details and review link
Denial:
- marks the request denied
- emails the applicant that they were not selected
Approval:
- marks the request approved
- queues subdomain provisioning
- emails the applicant that the contributor site is being created
Provisioning completion:
- emails the contributor their site URL
- includes admin URL
- includes username
- includes a temporary password
- includes a one-time reset link for choosing permanent credentials
Grant access:
- creates or reactivates an admin user in the contributor site's database
- sends admin URL, username, temporary password, and reset link to the granted email
If Postmark is not configured, the database changes can still be made, but email delivery returns a warning in the admin UI.
Master Oversight
The master CMS controls contributor sites through Admin Settings > Contributor Sites.
Available actions:
- Enable: re-enable a disabled contributor site and refresh webserver config.
- Disable: remove the public host from active service while preserving files and data.
- Destroy: archive the contributor files and mark the site destroyed.
- Grant access: add another admin email to an active contributor site.
Contributor sites do not get access to the master CMS. A contributor admin can only log into their own subdomain's /admin because sessions, users, cookies, and databases are separate per instance.
Contributor admins cannot:
- lock out the master CMS admin
- change master settings
- create master contributors
- manage other contributor sites
- access master private originals
- write into the master database
The master admin can still manage the contributor site's lifecycle because all lifecycle operations are queued in the master database and applied by the root worker.
Filesystem Layout
Typical master paths:
/etc/desertcms.conf
/var/desertcms/desertcms.sqlite
/var/desertcms/originals/
/var/www/htdocs/desertcms-site/
Typical contributor paths:
/etc/desertcms-alexs.conf
/var/desertcms-sites/alexs/desertcms.sqlite
/var/desertcms-sites/alexs/originals/
/var/desertcms-sites/alexs/backups/
/var/desertcms-sites/alexs/themes/
/var/www/htdocs/desertcms-alexs/
The app code is shared from:
/usr/local/www/desertcms/
Contributor configs include ownership metadata:
contributor_site_id = alexs
contributor_domain = alexs.example.com
contributor_owner_name = Alex Smith
contributor_owner_email = alex@example.com
Media uploads on a contributor site use that metadata to record owner context.
Public Contributor Directory
When the Contributor Requests module is enabled, DesertCMS publishes:
/contributors/
Approved contributor profiles appear there with:
- profile image
- name
- bio
- contributor subdomain link
The profile image is copied from the private request upload into the master public asset tree during approval.
Master Gallery And Posts Surfacing
The master renderer can read active contributor site configs and render public contributor content on the master site.
Gallery surfacing:
- reads active contributor databases
- skips sites whose blueprint disabled master Gallery surfacing
- includes contributor media rows whose public path is a generated
/assets/media/<hash>.jpg - renders the image from
https://<contributor-domain>/assets/media/<hash>.jpg - keeps contributor owner metadata in the card text
Posts surfacing:
- reads active contributor databases
- skips sites whose blueprint disabled master Posts surfacing
- includes published contributor posts
- links to
https://<contributor-domain>/posts/<slug>/ - labels cards with contributor owner or domain metadata
This is read-only aggregation. The master renderer reads public metadata and links public derivative URLs; it does not copy private originals and does not let contributor databases write into the master database.
The master Gallery page appears only when the Gallery module is enabled. The contributor directory appears only when the Contributor Requests module is enabled.
Security Boundaries
Contributor isolation relies on:
- separate SQLite databases
- separate private data directories
- separate public roots
- per-site session cookies
- per-site admin users
- root-owned application and worker code
- queue-based system changes
- public-only aggregation from active contributor sites
The _desertcms web user queues work but does not write root-owned app files or system config directly. Root workers validate generated webserver config before service reloads.
Backups And Upgrades
The upgrade worker rebuilds all configured instances after replacing the shared application tree. It runs migrations and rebuilds for /etc/desertcms.conf and /etc/desertcms-*.conf.
Back up the master database and contributor site data before major operational changes:
doas su -m _desertcms -c 'env DESERTCMS_CONFIG=/etc/desertcms.conf perl /usr/local/www/desertcms/bin/desertcms-maint.pl backup'
doas su -m _desertcms -c 'env DESERTCMS_CONFIG=/etc/desertcms-alexs.conf perl /usr/local/www/desertcms/bin/desertcms-maint.pl backup'
Destroy actions archive contributor files before marking the site destroyed. Treat those archives as sensitive because they can include private originals and unpublished content.
Troubleshooting
Request form returns 404:
- Confirm the Contributor Requests module is enabled.
- Rebuild the public site after enabling the module.
- Confirm
/forms/contributor-requestis forwarded to the CGI app in webserver config.
Request notification email is not sent:
- Confirm Send email is a verified Postmark sender.
- Confirm Receiving email for contributor requests is set.
- Confirm Postmark server token is saved.
Approval does not create the subdomain:
- Check Admin Settings > Contributor Sites for a queued or failed job.
- Run the site queue worker manually and read the error.
- Confirm wildcard DNS or the specific subdomain record points at the server.
- Confirm
httpd -npasses.
Contributor setup email is not sent after provisioning:
- Confirm Postmark settings on the master site.
- Confirm the worker can copy email settings into the contributor site.
- Run the site queue worker manually to see the Postmark error.
Contributor images do not appear in the master Gallery:
- Confirm the Gallery module is enabled on the master site.
- Confirm the contributor site status is
active. - Confirm the contributor site's blueprint allows master Gallery surfacing.
- Confirm the contributor site has uploaded media with public derivatives.
- Rebuild the master public site.
Contributor posts do not appear in the master Posts page:
- Confirm the contributor site status is
active. - Confirm the contributor site's blueprint allows master Posts surfacing.
- Confirm the contributor post is published.
- Rebuild the master public site.
Grant access fails:
- Confirm the contributor site is active.
- Confirm the contributor site's config path exists.
- Confirm the email address is valid.
- Confirm Postmark if you expect the grant email to be delivered.