FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix(nginx): serve favicon/security.txt docroot-relative (cutover follow-up #2) by mousebrains · Pull Request #199 · mousebrains/kayak_python · GitHub

fix(nginx): serve favicon/security.txt docroot-relative (cutover follow-up #2) - #199

Merged
mousebrains merged 2 commits into
mainfrom
static-aliases-docroot-relative
Jun 15, 2026
Merged

fix(nginx): serve favicon/security.txt docroot-relative (cutover follow-up #2)#199
mousebrains merged 2 commits into
mainfrom
static-aliases-docroot-relative

Conversation

Copy link
Copy Markdown
Owner

Serve favicon / security.txt docroot-relative (follow-up #2 from the cutover)

The /favicon.ico and /.well-known/security.txt locations aliased the absolute /home/pat/public_html/static/…. The 4C cutover re-pointed only the root (it's rendered from host.yaml); these two aliases were left untouched, so they serve fine now but would 404 the moment public_html is removed in the post-cutover cleanup. This fixes them before that cleanup.

Change

Both become try_files /static/<file> =404; — the same docroot-relative idiom the apple-touch-icon block right below already uses (its comment even says "Same shape as the /favicon.ico block above" — now literally true). try_files resolves under whatever root is set to.

location = /favicon.ico {
-    alias /home/pat/public_html/static/favicon.ico;
+    try_files /static/favicon.ico =404;
     ...
location = /.well-known/security.txt {
-    alias /home/pat/public_html/static/security.txt;
+    try_files /static/security.txt =404;

Why docroot-relative beats hardcoding the cache path

PR #198's review (finding #3) anticipated this follow-up and assumed it would hardcode /var/cache/kayak/docroot/static/… — which would re-introduce drift on the alias lines and need a new normalize_rendered mask. The try_files approach avoids all of that:

Verification

  • The build copies web/static/{favicon.ico,security.txt} into <docroot>/static/ (_deploy_site_assets), so try_files resolves them; the source files exist in src/kayak/web/static/.
  • try_files /static/… =404; is copied verbatim from the in-production apple-touch-icon block.
  • tests/test_host_render_serving.py (reads the committed snippet) — 11/11 pass; the root-line assertions are untouched (the change only converts two non-root alias lines).
  • nginx isn't in CI; the host runs nginx -t at deploy.

Deploy step (after merge)

Re-point the live file to match (keeps config-drift green and fixes the 404 risk), then reload:

git -C /home/pat/kayak pull
sudo sed -i \
  -e 's#^\(\s*\)alias /home/pat/public_html/static/favicon.ico;#\1try_files /static/favicon.ico =404;#' \
  -e 's#^\(\s*\)alias /home/pat/public_html/static/security.txt;#\1try_files /static/security.txt =404;#' \
  /etc/nginx/snippets/levels-common.conf
sudo nginx -t && sudo systemctl reload nginx
curl -sS -o /dev/null -w '%{http_code}\n' https://levels.wkcc.org/favicon.ico   # expect 200

🤖 Generated with Claude Code

… public_html

The /favicon.ico and /.well-known/security.txt locations aliased the absolute
`/home/pat/public_html/static/…`, which the 4C cutover did NOT re-point (only the
`root` is rendered). They serve fine now but would 404 the moment `public_html` is
removed in the post-cutover cleanup.

Switch both to `try_files /static/<file> =404;` — the same docroot-relative idiom
the apple-touch-icon block right below already uses ("Same shape as the
/favicon.ico block above"). This resolves under whatever `root` is set to, so it:
  - follows the rendered docroot automatically (survives public_html → cache);
  - stays GENERIC — no host-specific path, so the template and the live file are
    identical, i.e. config-drift (PR #198) needs no mask for these lines;
  - sidesteps the per-server-block `set $kayak_home` fragility the PATH LITERALS
    NOTE describes (try_files needs no variable, just the existing `root`).

The build copies web/static/{favicon.ico,security.txt} into <docroot>/static/, so
try_files finds them. Updated the PATH LITERALS NOTE accordingly (the `root` line
is now the one literal, host-rendered path).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copy link
Copy Markdown
Owner Author

Adversarial review — PR #199 (serve favicon/security.txt docroot-relative — cutover follow-up #2)

Reviewed on the live host. CI still running (nginx isn't in CI anyway; I verified the claims directly). Clean — approve. This is a better solution than my #198 finding #3 assumed: try_files /static/… (docroot-relative) instead of hardcoding the cache path, which sidesteps the whole drift-mask problem. Just two minor notes.

Verified

  • Proven idiom, verbatim. The apple-touch-icon block (line 115) already runs try_files /static/icon-180.png =404; in production — this copies that exact shape, so it's the lowest-risk change possible. The block's comment ("Same shape as the /favicon.ico block above") is now literally true.
  • Root inheritance is correct. Neither the /favicon.ico nor the /.well-known/security.txt block sets its own root/alias, so try_files /static/… resolves under the server's rendered root (the docroot) — survives public_html → /var/cache/kayak/docroot and any future docroot move.
  • The files actually resolve. The live /var/cache/kayak/docroot/static/ has favicon.ico + security.txt, both with the inherited + www-data ACL — so try_files finds and serves them post-cutover.
  • It moots fix(drift): exempt the 4C host-rendered nginx/FPM lines from config-drift #198 finding Cleanup: FK + reach.name fixes, smarter USGS tz, slow markers, scripts retire #3. Because try_files /static/… is generic (no host path), the template and the live file are identical after deploy → config-drift needs no new mask for these lines. Strictly cleaner than the hardcoded-cache-path approach I'd anticipated. Good call.
  • Completeness + accuracy. No absolute public_html aliases remain (only the rendered root + the doc comment reference it); the PATH LITERALS NOTE is updated correctly (the root is now the one literal, host-rendered path); test_host_render_serving.py is unaffected (only non-root lines changed).

1. (Low) The deploy is a coordinated repo+live change — sequence the sed with the pull

After this merges, the repo template has try_files but the live file still has the old alias until the deploy sed runs. Two consequences if the sed is skipped: the 404-risk fix isn't applied, and — once #198 re-arms config-drift — it would flag these two lines (template try_files ≠ live alias), and #198's normalize_rendered does not mask them. So the deploy sed in the PR body isn't optional; run it with the git pull. I confirmed it targets the current live lines (97/310) and produces exactly the template text, so it lands clean and keeps config-drift green. (Minor cross-PR ordering note vs #198, not a defect here.)

2. (Low, nice-to-have) A one-line guard test

Nothing asserts the conversion (nginx-not-in-CI is the repo's model, and nginx -t at deploy is the real gate). A cheap regression lock: assert the committed snippet contains no alias .*public_html line — that pins the "no absolute public_html aliases" invariant so a future edit can't silently reintroduce one. Optional.

Verdict

Approve — small, correct, and the docroot-relative try_files is the right idiom (follows the rendered root, stays generic, no drift mask). Just land the live-file sed alongside the pull.

— Claude Opus-4.8(1M)/xhigh

mousebrains added a commit that referenced this pull request Jun 15, 2026
…add tests

- #1 (the conscious trade-off): document the KNOWN LIMITATION inline — masking the
  whole line means config-drift no longer verifies the *content* of `root` /
  `open_basedir` between deploys (a manual tamper of just those lines wouldn't be
  flagged). Note that the deployer's serving-path gate still verifies them at
  deploy time, and record the verify-against-`render-serving` upgrade as the
  follow-up that closes the continuous-monitoring gap.
- #2: comment the FPM-pool-path duplication (RENDER_NORMALIZED + MANIFEST) so a
  PHP-version bump updates both, not just one.
- #4: add tests/test_scripts/test_config_drift.py — sources the script in a new
  lib mode (KAYAK_DRIFT_LIB=1, early-return before the /etc manifest walk) and
  drives normalize_rendered directly, locking the surgical mask: the docroot
  `root`/`open_basedir` are exempt, but the ACME `root /var/www/certbot;` stays
  byte-checked (a tampered ACME root still differs). Guards the certbot exclusion
  against a future sed edit.

#3 (the favicon/security.txt aliases re-introducing drift) is mooted by PR #199,
which makes those aliases docroot-relative (try_files) rather than hardcoding the
cache path — so they stay generic and need no mask.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…view #2)

A one-line regression lock requested in review: assert the committed
levels-common.conf has no `alias ...public_html...` line. Pins the cutover
follow-up #2 invariant — the favicon/security.txt locations serve docroot-relative
via `try_files /static/... =404;`, so a future edit can't silently reintroduce an
absolute public_html alias (which would 404 after public_html cleanup and diverge
from the live file, since config-drift doesn't mask alias lines).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copy link
Copy Markdown
Owner Author

Thanks — both addressed.

#2 (guard test) — added (f9d12a4). test_no_absolute_public_html_static_aliases in TestRendererPinnedToCommittedConfig asserts the committed levels-common.conf has no alias …public_html… line, so a future edit can't silently reintroduce one. 12/12 pass.

#1 (deploy is a coordinated repo+live change) — acknowledged, and it's a real cross-PR ordering constraint with #198. The deploy sed in the PR body is mandatory, and the sequence matters:

  1. merge fix(nginx): serve favicon/security.txt docroot-relative (cutover follow-up #2) #199 → git -C /home/pat/kayak pull (template now has try_files);
  2. run the live-file sed + nginx -t + reload before re-arming config-drift;
  3. then fix(drift): exempt the 4C host-rendered nginx/FPM lines from config-drift #198's git pull + systemctl start kayak-config-drift.timer.

If the timer were re-armed while the live file still had the old alias, config-drift would flag those two lines (template try_files ≠ live alias, and #198's normalize_rendered deliberately doesn't mask alias lines — they're generic now, not rendered). Running the sed with the pull keeps it green. You confirmed the sed targets the live lines (97/310) and produces exactly the template text, so it lands clean.

Net: #199 (favicon/security.txt) and #198 (drift mask) should deploy together — favicon sed first, then re-arm the timer.

mousebrains merged commit 1d2442e into main Jun 15, 2026
9 checks passed
mousebrains deleted the static-aliases-docroot-relative branch June 15, 2026 03:47
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant


Back | FazBrowse Home | New Git URL