This briefs an AI assistant helping a user configure a lazysite installation. For content authoring, see AI briefing - authoring. For view/theme work, see AI briefing - layouts.
A lazysite docroot typically looks like this:
DOCROOT/
lazysite/
lazysite.conf # site config
nav.conf # navigation
layouts/ # layouts, each with nested themes/ (D013)
LAYOUT/themes/THEME/ # theme.json + CSS/assets
auth/
users # user credentials
groups # group memberships
forms/
contact.conf # per-form config
handlers.conf # named dispatch handlers
smtp.conf # SMTP connection settings
cache/ # HTML cache, plugin cache
logs/
templates/
registries/ # registry templates (.tt)
manager/ # manager UI pages (if enabled)
index.md
[content pages]
The main configuration file. Plain text, one key-value pair per line.
site_name: My Site
site_url: ${REQUEST_SCHEME}://${SERVER_NAME}
site_url uses Apache CGI environment variables so the same config
works on any domain.
site_name: My Site
site_url: ${REQUEST_SCHEME}://${SERVER_NAME}
theme: default
nav_file: lazysite/nav.conf
search_default: true
log_level: INFO
log_format: text
manager: enabled
manager_path: /manager
auth_default: none
plugins:
- cgi-bin/lazysite-auth.pl
- plugins/form-handler.pl
- plugins/audit.pl
key: valuekey: ${REQUEST_SCHEME}://${SERVER_NAME}key: url:https://example.com/VERSIONkey: scan:/blog/*.md sort=date desc
scan:/gallery/**/*.md. Each result exposes the built-in fields
(url/title/subtitle/date/tags/path) plus any custom front-matter key
([% p.kind %], [% p.accent %]), quotes stripped. sort=<custom-key>
works and is numeric-aware (so sort=order gives 2 before 10).All keys that are not reserved become TT variables in views and page content.
Define the site navigation as YAML:
- label: Home
url: /
- label: About
url: /about
- label: Docs
children:
- label: Install
url: /docs/install
- label: Authoring
url: /docs/authoring
- label: Resources
children:
- label: GitHub
url: https://github.com/example
Items without url render as non-clickable group headings.
One level of nesting is supported.
Override the path in lazysite.conf:
nav_file: lazysite/docs-nav.conf
Users in lazysite/auth/users:
alice:SHA256HASHHERE
bob:SHA256HASHHERE
Groups in lazysite/auth/groups:
admins: alice
lazysite-admins: alice
editors: alice, bob
Manage with the CLI:
perl tools/lazysite-users.pl --docroot DOCROOT add alice password
perl tools/lazysite-users.pl --docroot DOCROOT group-add alice admins
Or use the manager Users page.
Per-page:
auth: required
auth_groups:
- members
Site-wide default:
auth_default: required
Custom HTTP header names (for external proxy):
auth_header_user: Remote-User
auth_header_groups: Remote-Groups
Three config files work together:
lazysite/forms/FORMNAME.conf - per-form dispatch list (and optional upload
limits):
targets:
- handler: email-delivery
- handler: local-storage # a "file" handler is required to store uploads
# Binary uploads (images, PDFs, ...). Presence of any upload_* key enables files
# on this form; absence = the form accepts no files at all.
upload_max_files: 3 # max files per submission (default 5)
upload_max_kb: 5120 # max size of EACH file in KiB (default 5120)
upload_accept: png, jpg, pdf # allowed extensions (default: any)
How binary uploads work. Author a file field in the page with the file rule
(photo | Photo | file accept:image/* required, multiple for several); the form
auto-switches to multipart/form-data. On submit, the handler parses the files
binary-safe, enforces the upload_* limits (rejecting with a specific message),
and the file handler stores them in a per-submission subdir next to the
FORMNAME.jsonl - FORMNAME.files/<id>/<name> - recording _files (the sanitised
names) and _files_dir in the submission JSON. Filenames are reduced to a safe
basename, so a path like ../../x cannot escape the submission directory. The
JSON never contains the file bytes, only the names.
lazysite/forms/handlers.conf - named handlers:
handlers:
- id: email-delivery
type: smtp
name: Email delivery
enabled: true
from: webforms@example.com
to: admin@example.com
subject_prefix: "[Contact] "
attach_files: false # true = attach uploads + list them under the message
- id: local-storage
type: file
name: Local file storage
enabled: true
path: lazysite/forms/submissions
lazysite/forms/smtp.conf - SMTP connection:
method: sendmail
sendmail_path: /usr/sbin/sendmail
Or for SMTP:
method: smtp
host: mail.example.com
port: 587
tls: starttls
auth: true
username: webforms@example.com
password_file: lazysite/forms/.smtp-password
A multilingual lazysite is a language set: two or more hosts that share a
lang_group, each a first-class domain with its own content root - one host
per language. The engine links them, so a layout gets a ready-made languages
variable (the sibling URLs for the current page) and renders a switcher plus
hreflang automatically.
lang: en # the primary's language
lang_group: providers # the set this host belongs to
content_root: sites/en
alias_hosts: fr.example.com
alias.fr.example.com.lang: fr
alias.fr.example.com.lang_group: providers
alias.fr.example.com.content_root: sites/fr
What an agent can and cannot do:
whoami to see the set (its siblings list gives each
language's host and content root, and which is the source); call the
lang-status control-API action to see exactly which pages are missing or
stale, and re-translate that set.content_root + lang + the shared
lang_group. Only once that plane exists can you populate it.hreflang. The layout receives
languages from the engine and renders them; authoring your own would
duplicate and drift.Plugins are CGI scripts and tools that register themselves with the
manager through a --describe JSON protocol. Auto-discovery scans
cgi-bin/ and tools/ for scripts supporting --describe.
Enable or disable from the manager Plugins page, or pre-enable in
lazysite.conf:
plugins:
- cgi-bin/lazysite-auth.pl
- plugins/form-handler.pl
- plugins/audit.pl
log_level: INFO # ERROR, WARN, INFO, DEBUG
log_format: text # text or json
forward_audit: off # also send audit entries to syslog
forward_diagnostics: off # also send app log events to syslog
syslog_facility: daemon # or local0..local7
Override at runtime with environment variables:
LAZYSITE_LOG_LEVEL=DEBUG
LAZYSITE_LOG_FORMAT=json
Logs go to lazysite/logs/ (when writable) or stderr. Syslog forwarding
is best-effort (never blocks a request); the on-disk logs are the record.
Themes live nested under a layout (the D013 model):
lazysite/layouts/LAYOUT/themes/THEME/, and the theme's theme.json must
list its layout in layouts: [...]. Activate by setting both in
lazysite.conf:
layout: LAYOUT
theme: THEME
After changing the layout or theme, clear the HTML cache so pages regenerate - the manager / control API does this atomically with the activation:
find DOCROOT -name "*.html" -delete
Or use Manager > Cache > Clear all. For the full layout/theme model, see AI briefing - layouts.
tools/lazysite-users.pl, create
groups in lazysite/auth/groups, enable the auth wrapper as the
Apache FallbackResource.auth: required to any page that needs protection, or set
auth_default: required site-wide.ui
capability (setup-manager does this; further grants on the Groups page).form: contact in front matter and a :::form
block.lazysite/forms/contact.conf:targets:
- handler: email-delivery
lazysite/forms/handlers.conf (see Forms above).lazysite/forms/smtp.conf.lazysite/forms/smtp.conf.example to smtp.conf.method: sendmail and verify the local MTA
accepts mail.method: smtp, host, port, TLS, and
authentication. Store the password in lazysite/forms/.smtp-password
with chmod 600.lazysite.conf:manager: enabled
manager_path: /manager
lazysite-admins group with at least one user:perl tools/lazysite-users.pl --docroot DOCROOT group-add alice lazysite-admins
/manager and sign in.