Test site: DNS moves to the fleet nameservers, no Cloudflare proxy
Deploy test site / deploy (push) Successful in 1m4s

cnxvinyl.lol's zone now lives in cnx-network-clan (ns1/ns2.cnx.network),
pointing straight at s1; mail is on mx1. Live once the nameservers at
Porkbun are switched.

- site/Caddyfile mirrors what s1 runs (checked 2026-10-23): no
  cloudflare_only or security_headers imports; Caddy gets its own certs.
- deploy-site.yml: drop the Cloudflare cache purge step and its secrets.
- site/README.md and digital-factory.md: DNS and certificate notes.
This commit is contained in:
grabowski committed 2026-10-23 11:01:55 +07:00
1 parent 1f532d0c85
commit f6829417a0
4 files changed
+19 -47

No files matched your search

+1 -21
View File
@@ -10,9 +10,6 @@ name: Deploy test site
# VPS_PORT optional, default 22 # VPS_PORT optional, default 22
# VPS_KNOWN_HOSTS optional pinned host key line(s); # VPS_KNOWN_HOSTS optional pinned host key line(s);
# without it the key is scanned per run # without it the key is scanned per run
# CLOUDFLARE_ZONE_ID, CLOUDFLARE_API_TOKEN
# optional; cache purge for cnxvinyl.lol
# (token needs Zone > Cache Purge)
on: on:
push: push:
@@ -111,25 +108,8 @@ jobs:
echo "live on server: $live" echo "live on server: $live"
[ "$live" = "$(cat site/dist/version.txt)" ] || { echo "::error::server holds $live, expected $(cat site/dist/version.txt)"; exit 1; } [ "$live" = "$(cat site/dist/version.txt)" ] || { echo "::error::server holds $live, expected $(cat site/dist/version.txt)"; exit 1; }
- name: Purge Cloudflare cache
env:
CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
run: |
if [ -z "$CLOUDFLARE_ZONE_ID" ] || [ -z "$CLOUDFLARE_API_TOKEN" ]; then
echo "::warning::CLOUDFLARE_ZONE_ID / CLOUDFLARE_API_TOKEN not set; skipping cache purge"
exit 0
fi
response=$(curl -sS -X POST \
"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/purge_cache" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}')
echo "$response"
echo "$response" | grep -q '"success":\s*true' || { echo "::error::Cloudflare cache purge failed"; exit 1; }
- name: Check the public URL - name: Check the public URL
# Warning only: DNS, Cloudflare or a basic_auth login can make this # Warning only: DNS or a basic_auth login can make this
# fail while the files on the server are already correct. # fail while the files on the server are already correct.
run: | run: |
expected=$(cat site/dist/version.txt) expected=$(cat site/dist/version.txt)
+2 -1
View File
@@ -1003,7 +1003,8 @@ Build onward, part-time at launch; add it to the headcount.
- Customer portal domain: the plant's brand domain (new zone on ns1/ns2, - Customer portal domain: the plant's brand domain (new zone on ns1/ns2,
second cert on `web01`) or a name under `cnx.network` for launch. second cert on `web01`) or a name under `cnx.network` for launch.
Internal testing has its own domain, `cnxvinyl.lol` (registered Internal testing has its own domain, `cnxvinyl.lol` (registered
2026-10-02, DNS on Cloudflare): a static demo site built from 2026-10-02 at Porkbun; DNS moved from Cloudflare to the fleet nameservers
ns1/ns2 in October 2026, mail on `mx1`): a static demo site built from
[`site/`](../site/README.md) and served by the buildfor.life web server, [`site/`](../site/README.md) and served by the buildfor.life web server,
not a plant service. The brand domain decision stays open. not a plant service. The brand domain decision stays open.
- Portal availability during a plant WAN outage: accept it (the portal is - Portal availability during a plant WAN outage: accept it (the portal is
+6 -12
View File
@@ -2,22 +2,17 @@
# Static web root /var/www/cnxvinyl.lol, filled by # Static web root /var/www/cnxvinyl.lol, filled by
# .gitea/workflows/deploy-site.yml in the cnx_vinyl repo. # .gitea/workflows/deploy-site.yml in the cnx_vinyl repo.
# #
# Append these blocks to /etc/caddy/Caddyfile on s1. They reuse the # Append these blocks to /etc/caddy/Caddyfile on s1, then
# cloudflare_only and security_headers snippets defined there for
# buildfor.life (buildfor_life_web: infra/caddy/Caddyfile.example); if
# they are missing, validate fails with "File to import not found:
# cloudflare_only" and they have to be copied in from the example first.
# caddy validate --config /etc/caddy/Caddyfile # caddy validate --config /etc/caddy/Caddyfile
# systemctl reload caddy # systemctl reload caddy
# This file mirrors what s1 runs (checked 2026-10-23); keep the two in step.
# #
# Cloudflare: both names proxied (orange cloud), SSL/TLS mode "Full (strict)". # DNS: the cnxvinyl.lol zone lives in the cnx-network-clan repo
# "Flexible" sends plain HTTP to Caddy, which redirects to HTTPS: a loop. # (modules/dns/zones/cnxvinyl.lol.zone, served by ns1/ns2.cnx.network) and
# Same rule as the buildfor.life apex: if a record is ever switched to DNS # points straight at s1, not through Cloudflare. Caddy gets its own
# only, remove "import cloudflare_only" first or that name goes dark. # certificates (HTTP-01/TLS-ALPN on 80/443), so no Cloudflare-only rule.
cnxvinyl.lol { cnxvinyl.lol {
import cloudflare_only
import security_headers
header X-Frame-Options "SAMEORIGIN" header X-Frame-Options "SAMEORIGIN"
# Internal testing: keep every page and file out of search engines. # Internal testing: keep every page and file out of search engines.
header X-Robots-Tag "noindex, nofollow" header X-Robots-Tag "noindex, nofollow"
@@ -35,6 +30,5 @@ cnxvinyl.lol {
} }
www.cnxvinyl.lol { www.cnxvinyl.lol {
import cloudflare_only
redir https://cnxvinyl.lol{uri} permanent redir https://cnxvinyl.lol{uri} permanent
} }
+10 -13
View File
@@ -3,7 +3,7 @@
`https://cnxvinyl.lol/` is the internal test site for this repository: demo `https://cnxvinyl.lol/` is the internal test site for this repository: demo
pages from `docs/` (the [plant operation page](../docs/plant-operation.html) pages from `docs/` (the [plant operation page](../docs/plant-operation.html)
first) and any static site we want to show by link. It runs on the same web first) and any static site we want to show by link. It runs on the same web
server as buildfor.life (Debian VPS `s1`, Caddy behind Cloudflare) and is server as buildfor.life (Debian VPS `s1`, Caddy; not behind Cloudflare) and is
not a plant service; plant systems follow the not a plant service; plant systems follow the
[digital factory plan](../docs/digital-factory.md). Every page and file is [digital factory plan](../docs/digital-factory.md). Every page and file is
sent with `noindex` and `robots.txt` disallows everything; nothing is behind sent with `noindex` and `robots.txt` disallows everything; nothing is behind
@@ -30,8 +30,7 @@ touches `site/`, an HTML page under `docs/`, or the workflow itself, and on
demand (Actions tab, "Run workflow"). It builds, rsyncs `site/dist` into demand (Actions tab, "Run workflow"). It builds, rsyncs `site/dist` into
the idle one of `/var/www/cnxvinyl.lol` and `/var/www/cnxvinyl.lol-prev`, the idle one of `/var/www/cnxvinyl.lol` and `/var/www/cnxvinyl.lol-prev`,
swaps them by rename (the blog's pattern), checks `version.txt` on the swaps them by rename (the blog's pattern), checks `version.txt` on the
server, purges the Cloudflare cache when the token is set, and finally server, and finally checks the public URL (warning only).
checks the public URL (warning only).
The deploy owns the whole web root: anything put into The deploy owns the whole web root: anything put into
`/var/www/cnxvinyl.lol` by hand disappears on the next deploy. Static sites `/var/www/cnxvinyl.lol` by hand disappears on the next deploy. Static sites
@@ -39,15 +38,14 @@ go into `public/` instead.
## One-time setup ## One-time setup
1. DNS in Cloudflare, both proxied (orange cloud): 1. DNS: the zone is in the cnx-network-clan repo
`cnxvinyl.lol CNAME s1.buildfor.life` and `www CNAME cnxvinyl.lol` (`modules/dns/zones/cnxvinyl.lol.zone`, served by `ns1`/`ns2.cnx.network`,
(Cloudflare flattens the apex CNAME, so the site follows s1 if its October 2026; until then it was Cloudflare): apex A/AAAA straight at s1
address changes). SSL/TLS mode **Full (strict)**: "Flexible" makes a and `www CNAME cnxvinyl.lol`. If s1's address changes, change it there.
redirect loop with Caddy.
2. Caddy on s1: append [`Caddyfile`](Caddyfile) to `/etc/caddy/Caddyfile` 2. Caddy on s1: append [`Caddyfile`](Caddyfile) to `/etc/caddy/Caddyfile`
(the comments in it list the checks), `caddy validate`, then (the comments in it list the checks), `caddy validate`, then
`systemctl reload caddy`. Caddy gets the certificate the same way it `systemctl reload caddy`. Caddy gets its own certificates over
does for the buildfor.life apex. HTTP-01/TLS-ALPN (ports 80 and 443 are open on s1).
3. Web root: the deploy user must be able to create and rename 3. Web root: the deploy user must be able to create and rename
directories in `/var/www`, as it already does for the blog. If not: directories in `/var/www`, as it already does for the blog. If not:
`sudo install -d -o <deploy user> /var/www/cnxvinyl.lol /var/www/cnxvinyl.lol-prev` `sudo install -d -o <deploy user> /var/www/cnxvinyl.lol /var/www/cnxvinyl.lol-prev`
@@ -56,9 +54,8 @@ go into `public/` instead.
4. Secrets in Gitea (repo: Settings > Actions > Secrets, or user level so 4. Secrets in Gitea (repo: Settings > Actions > Secrets, or user level so
every repo of the account gets them): `VPS_HOST`, `VPS_USER`, every repo of the account gets them): `VPS_HOST`, `VPS_USER`,
`VPS_SSH_KEY`, optionally `VPS_PORT` (default 22), `VPS_KNOWN_HOSTS` `VPS_SSH_KEY`, optionally `VPS_PORT` (default 22), `VPS_KNOWN_HOSTS`
(output of `ssh-keyscan -p <port> s1.buildfor.life`, pins the host key), (output of `ssh-keyscan -p <port> s1.buildfor.life`, pins the host key).
`CLOUDFLARE_ZONE_ID` and `CLOUDFLARE_API_TOKEN` (Zone > Cache Purge on The names match the blog's workflow, so the blog's
cnxvinyl.lol). The names match the blog's workflow, so the blog's
deploy key and values work unchanged: s1 only checks the key against deploy key and values work unchanged: s1 only checks the key against
the deploy user's `authorized_keys`, whichever repository sends it. the deploy user's `authorized_keys`, whichever repository sends it.
Gitea secrets cannot be read back, so they are entered again here from Gitea secrets cannot be read back, so they are entered again here from