cosmix-webd — multi-vhost HTTPS front door

cosmix-webd is the mesh's web server: multi-vhost HTTPS with automatic ACME certificates, and a server-side-rendered UI that streams updates over Datastar SSE. One binary terminates TLS for many domains and serves the mail, files, and CMS surfaces of the substrate as ordinary web pages.

What it is

A long-running Rust daemon that owns a node's :443 (and optional :80) listeners. It is the human-facing edge of the mesh — where a browser meets the substrate. Vhosts, routes, TLS certificates, and page templates are all configured through Bus verbs and the SPEC-12 property store, not hand-edited config files, so an agent can add a site or rotate a certificate with a structured call.

Pages are server-side rendered. Interactive behaviour comes from Datastar — the server pushes DOM patches over a Server-Sent-Events stream (text/event-stream) instead of shipping a client-side framework. Handler logic for a vhost is written in Mix and evaluated per request, with seams into the mail daemon (jmap()), the files daemon, and the CMS database.

What it does

  • TLS termination + SNI for many vhosts on one listener, backed by a per-vhost certificate resolver.
  • Automatic ACME (Let's Encrypt style) — provisions and renews certificates over the HTTP-01 challenge on the :80 listener, with cooldown and renewal-window gates.
  • SSR web UI — renders mail (JMAP-backed), a dual-pane file manager, and a CMS/PIM, patching the page live over Datastar SSE.
  • Per-vhost Mix handlers — request routing and page logic authored in Mix, with $SIGNALS (the parsed Datastar signal store), $BODY, and session identity injected into scope.
  • Sessions + auth — cookie sessions (cosmix_session), server-side authentication for every write; a loopback-gated dev auto-session for headless local preview.

Running it

/opt/cosmix/bin/cosmix-webd

Runs under systemd as cosmix-webd.service (identity User=cosmix-webd, from the SPEC-10 sysusers fragment). Shared node settings load from /etc/cosmix/node.toml; the per-daemon block, listener addresses, and vhost tree come from /etc/cosmix/webd/config.toml plus the property store. A loopback-only dev listener (default 127.0.0.1:8080) offers a zero-config local preview fenced to localhost.

Shared fonts, icons and emoji

Shared asset publication is opt-in. Set webd.shared_assets_dir in node.conf.mix, or pass serve --assets-dir /srv/cosmix-assets. The value is an absolute installation root containing sets/<set-id>/; it is never discovered from the server account's XDG user data. Installers normally use cosmix_path("share")/assets, with user overrides kept separate.

Known vhosts then serve the same installed bytes at /_cos/assets/<set-id>/<manifest-path>. For example, a page can load /_cos/assets/2026-10-01-core-2/fonts.css; its relative font URLs stay bound to that retained set. The existing per-vhost /assets/ route keeps its own meaning. There is no current URL or directory listing, and only files in the manifest plus fonts.css and manifest.conf.mix are public.

At startup, webd verifies both locked hashes for each published set, up to 32 retained sets. New installations become available after a webd restart. Modified or symlinked files are refused until the installation is corrected and reverified. Versioned responses have correct font/CSS MIME types, nosniff, SHA-256 ETags and a one-year immutable cache policy; failed responses use no-store. HEAD, byte ranges and Last-Modified revalidation are supported. Fonts are served on the page's own origin; cross-origin access is not enabled by default.

For pages on another node or origin, explicitly enable webd.shared_assets_cross_origin: true or serve --assets-cross-origin alongside the asset directory. Shared asset responses then allow anonymous cross-origin reads with Access-Control-Allow-Origin: *; credentials are never enabled.

For an isolated loopback preview, combine the two explicit roots:

cosmix-webd serve --static-dir /srv/preview --assets-dir /srv/cosmix-assets

webd supplies read-only HTTP access to these local files. Each mesh node still installs its own local set for native applications and offline use; the directory path itself does not become a shared mesh filesystem.

Interfaces

Listeners:

  • :443 — TLS vhosts (public edge).
  • :80 — plain HTTP: ACME HTTP-01 challenge + optional redirect / autoconfig.
  • 127.0.0.1:8080 — internal dev/preview listener (opt-in).

Bus verbs (service webd):

VerbPurpose
webd.vhost.addregister a new vhost
webd.props.{list,get,set,delete}property store surface
webd.routes.listlist matched routes
webd.acme.{status,renew}certificate state; force a renewal
webd.tls.{status,reload}TLS resolver state; hot-reload certs
webd.autoconfig.served_domainsmail-client autoconfig domains
webd.session.revokerevoke a session
webd.statsrequest/response counters

Where it fits

Depends on cosmix-lib-daemon (the tls feature: rustls + ACME + SNI), cosmix-lib-props-store (SPEC-12 state), cosmix-lib-config, and the Bus client libraries from bus. It reaches a local or remote cosmix-maild over JMAP for mail, the files daemon for the file manager, and evaluates per-vhost handlers through the embedded mix engine. Every node needs a cosmix-noded broker for the Bus surface.

See also

  • noded — the Bus broker webd registers with
  • maild — the JMAP mail backend behind the web mail UI
  • dnsd — mesh DNS that resolves the vhost names webd serves
  • libraries — cosmix-lib-daemon, cosmix-lib-props-store
  • overview — the daemon family at a glance