lazysite is a deliberately small engine, but it is built and maintained as a commercial product. It is assessed against an eight-dimension non-functional standard - correctness, code quality, testing, performance, reliability, security, documentation and compliance - under a declared Commercial regime. This page is a factual summary of the architecture and the measures in place, as of the 0.9 line (July 2026).

How it is built

At its core, lazysite does one job: it turns Markdown and a template into an HTML page. That renderer is the whole of what runs when a visitor asks for a page, and it is deliberately small.

Everything else lazysite can do - authoring content, managing domains and themes, the API, WebDAV, accounts and the manager interface - lives in separate components. Those components write files; the renderer only reads them. They share a common set of core libraries, but none of them sits in the path of a public request. The result is that most of the functionality sits outside the render path.

One render path, a separate write plane

What runs when a visitor asks for a page

A request is answered by a single component, the renderer. It reads the content, theme and layout files for the site, runs the Markdown and the template, and returns HTML. It loads no authentication code and no management code to do this; two libraries do the work. This is the only part of lazysite that is always on, public and unauthenticated, and it is kept as lean as possible on purpose.

What runs when a site changes

Changing a site is a different path entirely. An operator or an AI agent authenticates, and the request is handled by one of the write components - the MCP connector, the control API, WebDAV, OAuth, the login service, the manager interface or the command-line tools. Each checks the caller's capabilities and then writes the relevant files. These surfaces are off by default and gated by capability, and once they have written their files they step out of the way. The next visitor is served by the renderer reading the files they left behind.

The filesystem is the interface between the two planes. The write side produces files; the read side consumes them. There is no shared request path and no management code loaded while a page is being served.

Why it is built this way

  • Reduced security surface. The only always-on, unauthenticated, publicly reachable path is a small, read-only renderer. The security-sensitive code - authentication, capability checks, everything that writes - is never between a visitor and a page.
  • Better performance. Because the renderer loads no authentication or management machinery, there is simply less to do on every request.
  • Easier to maintain. Each feature grows in its own component against the shared libraries, without touching the renderer. The renderer stays stable while the rest of the system evolves, and a change to the authoring tools cannot affect how a page is served.

1. Correctness

  • Compile integrity, enforced. Every production Perl file compiles under perl -c, guaranteed by a committed gate (t/lint/04-compile.t) so a non-compiling file cannot pass the suite.
  • One capability resolver. Account capabilities resolve through a single source-of-truth reader consulted identically by the manager, the API, MCP and WebDAV; the deliberate module-free copy on the render hot path is recorded in an architecture decision record (ADR 0001).
  • Path safety. Every filesystem path derived from request input is passed through realpath and verified to sit inside the document root before any file operation.
  • Grounded features. Shipped behaviour is exercised by the test suite, and plugin discovery is honest - each discovered script answers a --describe contract.

2. Code quality

  • Static analysis at severity 3, zero violations (perlcritic), enforced by the suite - plus a separate security-themed pass at the strictest severity 1, also zero.
  • Formatting gate. A house .perltidyrc with a changed-code-only gate: new and edited lines must match the style, without churning the existing tree.
  • Every deviation documented. Each relaxed rule carries a written rationale in the profile and the architecture docs - deliberate, not silently suppressed.
  • Consistent structure. use strict; use warnings everywhere, with shared logic living once in a 28-module library the CGIs consult.
  • Shell held to the same bar. Every shipped shell script passes shellcheck with errors forbidden, and a retired-terms lint fails the build if any doc teaches a mechanism the engine has retired.

3. Testing

  • Around 2,700 automated tests across 266 files, green at every release.
  • A six-tier taxonomy: unit, integration, journey, smoke, lint and tools - the CGIs are exercised as real subprocesses in a real CGI environment.
  • Coverage floors enforced at release: statement 75 and branch 62 across the eight gated CGIs (three documented per-file branch overrides at 60), checked by coverage.sh --check inside release.sh. An unmeasured gated CGI fails the gate rather than silently skipping, so a coverage breach blocks the release rather than being a hand-run reminder.

4. Performance

  • Render-once, serve-cached. Each page is rendered to .html beside its source and served verbatim thereafter, regenerating only on change - fast by default, nothing recomputed per visit.
  • Persistent workers, same code. The processor also runs as a FastCGI worker pool: identical behaviour to CGI mode, with module load and startup paid once per worker rather than per request.
  • Benchmark gate, enforced at release. Key operations are timed against a provenance-stamped baseline (host, Perl version, date); the render benchmark is split into cache-hit and render-miss so the figure is honest.
  • Measured properties on record - token verification is materially cheaper than password verification; module-load floors and cache-hit concurrency speedups are documented and tuned.

5. Reliability

  • Backup and restore, tested. Pre-upgrade backups with configurable retention; content and full-system snapshots; in-manager restore takes a safety snapshot first and clears affected caches; full-system restore even supports cross-domain migration. The round trip is covered by tests.
  • Fail-closed writes. Manager and form writes check for the out-of-space/quota case and remove half-written files on failure - with disk-full injection and concurrent-writer races covered by a dedicated test. WebDAV PUT bodies stream in bounded chunks.
  • Concurrency and locking. Theme/layout activation snapshots under an artifact lock (validate -> snapshot -> flip); WebDAV honours a documented 423/429 retry contract.
  • Graceful degradation. Unparseable state falls back to safe defaults rather than failing; the audit reader is rotation- and truncation-aware.
  • Operational tooling. A health-check "doctor" reports OK/WARN/FAIL with remediation hints, evaluates every check as the web-server identity, offers safe --fix repairs and a DAV-endpoint probe; a logrotate snippet ships with the installer.

6. Security

Security is structured around a STRIDE threat model with OWASP ASVS L1 verification (docs/SECURITY.md), enumerating the assets and trust boundaries and pairing each threat category with a concrete control:

Threat category Control in place
Spoofing Two-signal trust gate with mandatory edge-stripping of client X-Remote-* headers; HMAC-signed session cookie
Tampering Template engine runs with Perl evaluation disabled; layout authoring gated by capability; content-vs-layout capability split
Repudiation Append-only audit trail (who / what / target / origin / outcome, including denied attempts), gated by an audit capability
Information disclosure Secrets server-denied under restrictive modes; analytics aggregated and IP-anonymised; error surface synthesised
Denial of service Per-IP login rate limiting; upload-size gate; bounded-chunk streaming; fail-closed writes; a bad-URL auto-blocker on by default
Elevation of privilege API/MCP token clients confined to a capability subset, never operators; per-file ACLs; manager/remote channel separation

Alongside the model:

  • Adversarial review on the record. An eleven-agent security review (static analysis plus black-box probing, July 2026) produced 29 findings; every critical, high and medium finding was fixed and regression-tested in the following release, and the round, its coverage and its root-cause themes are recorded in a versioned register in the repository. Hardening continued through the 0.9 line, and the hardened line was then certified stable.
  • Off by default. Every machine channel - MCP, OAuth, the control API, token exchange, WebDAV - ships disabled behind its own configuration switch; an operator turns on exactly the surfaces a site uses.
  • A single SSRF guard for outbound fetches (rejects loopback, private, link-local/metadata and CGNAT ranges), groups-only explicit capabilities, a strict SBOM gate (an undeclared dependency fails the release), secrets-lint (every pattern self-tested against a planted fixture) and security-perlcritic gates all green, and a published coordinated vulnerability disclosure policy.
  • Penetration testing declared honestly. The standard gates the security dimension on an external pentest; the first engagement is a launch decision, and the deferral is recorded in an ADR with a named expiry rather than left silent (ADR 0007).

7. Documentation

  • Role-scoped for every audience - user, developer, implementor, operator and policy docs around a canonical feature reference, plus README, upgrade guide and security policy.
  • Eight architecture decision records and architecture docs for security, code quality and performance.
  • Generated, drift-checked references - the capability map, quickstarts and host-dependency list are generated from a single source of truth and golden-tested against drift; CLI tools carry POD that renders to man pages at release.
  • Accessibility - a WCAG 2.1 AA self-assessment of the manager UI and default theme.
  • Ships inside every install - the reference docs (including AI-agent briefings) travel with the product; a commit-keyed CHANGELOG is the single source of truth for what changed.

8. Compliance and supply chain

  • A stable release with a five-year support period. 0.7.0, the first stable release (July 2026), opened a declared five-year support window; stable promotions since (0.8.0, 0.9.x) carry the same discipline, and a compatibility freeze for the stable line is recorded in an ADR (ADR 0008).
  • Three release channels - edge, beta and stable - with per-site channel selection: a site's update_channel names the minimum maturity it accepts, so a fleet can hold production sites to stable while trialling betas elsewhere.
  • Posture of record - a documented Commercial-regime policy structured around licensing, supply chain, the EU Cyber Resilience Act status, support period and data protection; a Declaration of Conformity for the stable line is prepared in draft ahead of signing.
  • Software bill of materials - a CycloneDX sbom.json in every release with per-component SHA-256 hashes and SPDX licence ids, behind a strict gate so it can never drift from the code.
  • Install classification and provenance - every shipped file is classified as code (overwritten) or seed (operator content, preserved), with a provenance stamp distinguishing the two.
  • MIT licensed, with per-component licence ids recorded in the SBOM.

At a glance (0.9 line, July 2026)

Measure State
Test suite 266 files, about 2,700 tests, six tiers, green
Compile sweep Enforced over all production files
Static analysis perlcritic severity 3 zero; security theme severity 1 zero
Formatting House profile, changed-code gate; shellcheck error-free
Coverage Statement 75, branch 62, eight gated CGIs, enforced at release
Benchmark Hot paths within tolerance of a provenance-stamped baseline
SBOM CycloneDX per release, strict gate
Decision records Eight ADRs
Threat model STRIDE + ASVS L1; versioned security-review register
Release channels edge, beta, stable; first stable 0.7.0 with five-year support
Regime Commercial, MIT licensed

These are the disciplines that keep lazysite a small, auditable codebase you can run in production with confidence - and they are the same enforced rules an AI agent publishes through, exactly as a person would.