Domain delegation
Delegate a domain to a team - allow by group, confine by user.
Domain delegation
On a multi-site instance, each domain can name who may manage it (SM154/SM155/SM165). Access lives on the domain, with two separate controls: an allow-list of groups (additive) and a set of user locks (subtractive). The Domains page shows a domain's whole access picture in one place and is the only place access is edited.
Configuration
Two per-domain keys in lazysite.conf, editable from the Domains page,
the control API (domain-set) or the lazysite-domains CLI - all gated
by the manage_domains capability:
alias.clienta.com.content_root: sites/clienta
alias.clienta.com.allowed_groups: clienta-editors, agency-leads
alias.clienta.com.locked_users: alice, clienta-bot
allowed_groups- The groups permitted to manage this domain. A user is allowed a domain when they belong to one of its allowed groups. A non-default domain with an empty allow-list is operator-only.
locked_users- Accounts confined to this domain. A locked user reaches only the domain(s) they are locked to, intersected with what their groups allow - a lock narrows, never grants.
The effective set resolves to a list of content-root scopes and feeds the same confinement checks on every channel: manager UI, control-API token, MCP, WebDAV and the processor's file-browser rooting. A user with no lock and no allow-entry is a general editor, as before. A locked user whose effective set is empty is confined to nothing - never silently unconfined.
The sub-user ceiling
A created account can never out-reach its creator: effective scope is
intersected up the created_by chain at resolve time, and disjoint
scopes resolve to deny-all. An MCP agent minted by a delegated user is
therefore confined to a subset of that user's own domains by
construction.
Pre-DNS preview
The domain-preview action renders a registered domain's home page
server-side under its own Host, with auth cleared (an anonymous public
render), so a team can prepare and debug a new domain before DNS or TLS
is live. Only registered hosts can be previewed.
The Domains panel checks
domain-check verifies a registered domain is live on this install,
running four ordered checks the panel renders in order:
- DNS resolves - the name has an address
- Points to this server - a resolved address matches this install's
public IP (discovered from
canonical_ip, or by resolving the install's own domain; indeterminate behind an unknown proxy/NAT) - HTTPS certificate valid - a trusted certificate covers the host; a trusted certificate that does not cover the host is reported as a coverage gap, listing the names it does cover
- Serves this lazysite - an HTTPS request lands back on this
instance, verified by the
/.well-known/lazysite-instance.jsonmarker
The outbound probes are SSRF-guarded: the check refuses to connect when any resolved address is non-public (loopback, RFC1918, link-local and the metadata endpoint, CGNAT, IPv6 ULA), keyed on the resolved IPs so DNS rebinding is closed too, and only operator-registered hosts may be probed at all.
Notes
- Adding a domain validates the host as a strict DNS name and the
content root against traversal and the reserved
lazysite/area; values are rejected if they could smuggle a second conf line - Removing a domain leaves its content directory in place by default;
purgeremoves it, confined to the docroot - The Groups and Account pages mirror domain access read-only - the
earlier group-side binding (
dav_scope/home_domain, SM155) was replaced by this domain-owned model - DNS, the web-server alias and TLS are preconditions handled by the operator or panel; lazysite never touches them