Skip to content
Version: Current

Edge, Caddy and TLS

Last verified: 2026-11-05 (repository inspection in this worktree; no live host or Caddy state mutated). Scope: tcpsystems/docs owns the static Astro build and the publish path for /srv/docs/site/ and the three root indexes. Fleet edge configuration, Caddy service management, certificates, Nomad state, DNS records and origin listeners are owned outside this repository.

Surface Owner Source of truth
Starlight pages, build output, asset prefix tcpsystems/docs astro.config.mjs, src/content/docs/, tools/deploy.sh
/srv/docs/site/ subtree tcpsystems/docs publish path tools/deploy.sh with DOCS_BASE=/site/
/srv/docs/llms.txt, /srv/docs/sitemap.xml, /srv/docs/docs.json Regenerated from a mirror of the served root tools/deploy.sh + tools/live-site/manifest.mjs
Served root outside /site/ Other generators/owners README.md: Starlight seed, docs-intel page set, acumen-vnext tools/docsgen
Caddy vhosts, TLS certificates, bind addresses, Access/WAF, Nomad jobs Fleet-config / edge / operations owners Not in this repository; verify with the owning runbook before changing

The coexistence rule is absolute: publishing docs must not replace the whole /srv/docs root. A root sync would delete pages this repository does not own.

  • Node 22.23.4 or newer for the pinned toolchain (README.md, package.json engines, and tools/ci.sh checks).
  • Clean dependencies: npm ci (npm lockfile) and pnpm-lock.yaml kept in parity when dependencies change (AGENTS.md, tools/ci.sh).
  • SSH access to the deploy target only when an owner explicitly requests a publish. This card does not deploy.
  • For live verification, use the target host’s loopback/public verification path appropriate to the environment. Do not probe restricted public endpoints from a runner that is under a no-probe rule.

Deploy coexistence: /srv/docs root and /site/

Section titled “Deploy coexistence: /srv/docs root and /site/”

tools/deploy.sh enforces the boundary:

  1. Builds with DOCS_BASE=/site/ so assets and internal links carry the prefix.
  2. Refuses to publish if dist/index.html does not reference ${BASE}_astro/ when a prefix is used.
  3. Backs up /srv/docs/site/ and the three root files into /root/backups/docs-deploy-<UTC>/.
  4. Runs rsync --delete --delete-excluded scoped to /srv/docs/site/, excluding sitemap-*.xml emitted by the build.
  5. Mirrors the full served root, regenerates llms.txt, sitemap.xml and docs.json, and publishes only those root files.
  6. Runs a manifest drift gate and HTTP 200 verification.
Terminal window
# Safe local plan: builds and shows rsync changes, writes nothing.
bash tools/deploy.sh --dry-run
# Owner-requested publish only (not run in this card):
bash tools/deploy.sh

DOCS_BASE=/ is owner-only. Use it only when the explicit decision is to make this build the root front door.

The docs repository does not store Caddyfiles or certificates. Verify TLS and routing at the edge owner surface:

Terminal window
# Verify the advertised contract paths (run from an allowed environment):
curl -sS -o /dev/null -w '%{http_code} %{url_effective}\n' https://docs.tcp.systems/
curl -sS -o /dev/null -w '%{http_code} %{url_effective}\n' https://docs.tcp.systems/site/
curl -sS -o /dev/null -w '%{http_code} %{url_effective}\n' https://docs.tcp.systems/llms.txt
curl -sS -o /dev/null -w '%{http_code} %{url_effective}\n' https://docs.tcp.systems/sitemap.xml
curl -sS -o /dev/null -w '%{http_code} %{url_effective}\n' https://docs.tcp.systems/docs.json
# TLS certificate chain (read-only):
openssl s_client -connect docs.tcp.systems:443 -servername docs.tcp.systems </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
# On the edge host, owned by fleet/Caddy operators (examples; paths may differ):
systemctl is-active caddy
caddy validate --config /etc/caddy/Caddyfile
sha256sum /etc/caddy/Caddyfile
ls -la /srv/docs /srv/docs/site

Expected outcomes:

  • Contract URLs answer 200.
  • Certificate dates are valid and the name matches docs.tcp.systems.
  • /srv/docs/site/ contains only this repository’s built subtree.
  • Root files outside /site/ remain present when owned by other generators.
Symptom First checks Safe action
/site/ 404 or asset 404 Confirm DOCS_BASE=/site/, check ${BASE}_astro/ references in dist/index.html, inspect /srv/docs/site/ contents Re-run --dry-run; restore subtree from backup if a partial publish occurred
Root index missing or stale Check /srv/docs/llms.txt, /sitemap.xml, /docs.json timestamps and manifest check output Regenerate from a mirror of the served root; restore root files from backup if needed
TLS/certificate error Check openssl s_client dates/issuer and Caddy service state on the edge host Escalate to edge/Caddy owner; do not edit fleet Caddy from this repo
Wrong content served from root Confirm whether content belongs to docs-intel or acumen-vnext tools/docsgen Do not run a whole-root sync from docs; coordinate with the owning generator
Deploy refuses asset prefix tools/deploy.sh cannot find ${BASE}_astro/ reference Fix build/base configuration, rerun npm run build, then --dry-run

tools/deploy.sh prints and records rollback commands. The backup contains subtree.tar.gz, llms.txt, sitemap.xml, docs.json, Caddyfile and Caddyfile.sha256.

Terminal window
# Restore subtree and root indexes (owner/operator action on target host):
ssh <target> "tar xzf /root/backups/docs-deploy-<UTC>/subtree.tar.gz -C /srv/docs && \
cp -a /root/backups/docs-deploy-<UTC>/{llms.txt,sitemap.xml,docs.json} /srv/docs/"
# Remove only the subtree if it did not exist before the deploy:
ssh <target> "rm -rf /srv/docs/site"

Safety checks before and after a publish:

  • Run bash tools/ci.sh --skip-deploy and keep the output as evidence.
  • Use --dry-run to inspect the delete scope; every deletion must be under /srv/docs/site/ unless the root index files are intentionally regenerated.
  • Never print or commit credentials. SSH keys, tokens and provider secrets are out of scope for docs content.
  • Do not claim a live route is fixed without a dated HTTP/TLS check from an allowed environment.