domain() !== null && $this->domain() !== ''; } /** * Render the Caddy site block for the configured domain, mirroring the shape of * docker/caddy/Caddyfile: a global `email` block + a site address that triggers * automatic Let's Encrypt TLS, with the same Reverb (/app/*, /apps/*) and app * upstreams and the same conservative hardening header set. * * With a domain set: `https://` → auto-TLS on :443, HTTP auto-redirect. * Without a domain: `:80` → plain HTTP on the bare IP (no cert issued). */ public function renderCaddyConfig(): string { $domain = $this->domain(); $email = $this->email(); $siteAddress = $domain ? 'https://'.$domain : ':80'; // Caddy needs a non-empty ACME email even in bare-IP mode, where no cert is // issued so the value is simply unused — mirror the compose default. $acmeEmail = $email ?: 'admin@localhost'; return << -> auto Let's Encrypt, :443, HTTP auto-redirect # :80 -> plain HTTP on the bare IP, no cert { \temail {$acmeEmail} } {$siteAddress} { \t@reverb path /app/* /apps/* \treverse_proxy @reverb reverb:8080 \treverse_proxy app:80 \tencode zstd gzip \theader { \t\tX-Content-Type-Options nosniff \t\tX-Frame-Options DENY \t\tReferrer-Policy strict-origin-when-cross-origin \t\t-Server \t} } CADDY; } /** * Write the rendered site config into the app's storage dir. This is a staging * file the operator can mount/copy into Caddy's /config — it does NOT reload the * live proxy (separate container). Returns the absolute path written. */ public function writeCaddyConfig(): string { $dir = storage_path('app/caddy'); if (! is_dir($dir)) { @mkdir($dir, 0775, true); } $path = $dir.'/'.self::CONFIG_FILENAME; file_put_contents($path, $this->renderCaddyConfig().PHP_EOL); return $path; } /** Absolute path writeCaddyConfig() targets (no write performed). */ public function configPath(): string { return storage_path('app/caddy/'.self::CONFIG_FILENAME); } /** * Operator note: the app cannot reload Caddy. After writing, reload is an infra * step inside the caddy container. */ public function reloadHint(): string { return 'caddy reload --config /config/'.self::CONFIG_FILENAME; } }