Skip to content

fix(public): enforce the top-level real-404 deploy proof in the release lane - #104

Merged
nish3451 merged 19 commits into
mainfrom
fix/soft404-bundle-proof-lane1-20260812
Aug 13, 2026
Merged

fix(public): enforce the top-level real-404 deploy proof in the release lane#104
nish3451 merged 19 commits into
mainfrom
fix/soft404-bundle-proof-lane1-20260812

Conversation

@nish3451

@nish3451 nish3451 commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Finding (verified live 2026-08-12)

Every unknown URL on tinystudio.in returns HTTP 200 with the full homepage
(soft-404): curl -s -o /dev/null -w '%{http_code}' https://tinystudio.in/no-such-page
200, body is the homepage. Reproduced for /no-such-page, /no-such-page/,
/no-such-page.html, /a/b/c/d, and /promptly/not-real, plus on the Pages
project hostname itself.

Root cause

The live site is still the stale June-20 bundle (the deploy lane has never
succeeded - repo gh secret list is empty and every Deploy public site run
fails with "Missing CLOUDFLARE_API_TOKEN"). That bundle predates PR #34 and has
no top-level 404.html, so Cloudflare Pages automatically assumes
single-page-application rendering and matches every unknown path to / with
HTTP 200 (documented Pages behavior). The fix from PR #34 is correct and on
main but has never been deployed.

What this PR changes

The soft-404 guard chain had one unguarded link: the release lane's deploy-proof
set (NEUTRAL_PROOFS in prepare-public-deploy-bundle.mjs) asserted the three
page proofs but not the real-404 proof, even though "real 404s" is one of the
four documented live proofs in deploy-public-site.yml. The top-level
public/404.html is the exact file whose presence terminates Pages' SPA
fallback.

  • Adds a top-level real-404 proof to NEUTRAL_PROOFS (title marks "not found",
    not the homepage title, noindex meta present), so the release lane itself
    fails closed at bundle-prepare time if 404.html is ever lost or regresses
    into a homepage clone - instead of only being caught by the post-deploy live
    check after publishing.
  • test-public-deploy-bundle.mjs picks the proof up automatically via its
    NEUTRAL_PROOFS iteration (verified: "ok top-level real 404 page (PR fix(public): serve a real 404 page so unknown URLs stop returning the homepage #34)").

Verification

  • npm run check (the lane's pre-deploy gate): all 9 suites green, 0 failures.
  • test-public-soft-404.mjs: 19 checks, 0 failures.
  • test-public-deploy-bundle.mjs: 63 checks, 0 failures, new proof asserted.

Remaining blocker for the live fix (not fixable from this repo)

Closing the finding on the live site requires the deploy lane to actually run,
which needs a Cloudflare Pages:Edit token that does not exist anywhere reachable:
repo secrets are empty, the fleet token (fleet-console/cf.env) has Workers +
DNS but no Pages:Edit (verified: Pages API returns auth error), Pages OAuth on
the VPS expired 2026-08-04, and no ~/.inish-cf-token drop-file exists. One-time
provisioning (dashboard, ~2 min) is documented in
scripts/publish-public-site.mjs (PROVISION_MESSAGE) and PR #85
(fix/lane1-deploy-lane-dormant-until-pages-token) keeps the lane visibly
dormant meanwhile. Once both secrets exist, the next main merge deploys the
bundle (which carries 404.html), and the post-deploy + nightly live checks
verify the soft-404 is gone.

Summary by CodeRabbit

  • Bug Fixes

    • Improved deployment validation to confirm that the top-level 404 page displays an appropriate “not found” title.
    • Ensured the 404 page excludes homepage-specific content and includes noindex metadata for search engines.
  • Tests

    • Updated deployment checks to verify that the real 404 page remains included in the final bundle.

…se lane

The soft-404 guard chain had one unguarded link: the release lane's
deploy-proof set (NEUTRAL_PROOFS) did not assert the top-level 404 page,
even though 'real 404s' is one of the four documented live proofs. The
top-level public/404.html is what terminates Cloudflare Pages'
single-page-application fallback (without it every unknown URL is served
as the homepage with HTTP 200 - the soft-404 PR #34 fixed).

Adding it to the proof set makes the lane itself fail closed if 404.html
is ever lost or regresses into a homepage clone, instead of only being
caught by the post-deploy live check after publishing.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Free

Run ID: 7226696b-56f6-4120-b675-1e95dda724f7

📥 Commits

Reviewing files that changed from the base of the PR and between 3d1a956 and 372a43b.

📒 Files selected for processing (2)
  • scripts/prepare-public-deploy-bundle.mjs
  • scripts/test-public-deploy-bundle.mjs

📝 Walkthrough

Walkthrough

The deploy bundle now verifies that the top-level 404.html page has a not-found title, excludes the homepage title, and includes noindex robots metadata. Test documentation includes this proof.

Changes

Deploy proof validation

Layer / File(s) Summary
Top-level 404 proof and test documentation
scripts/prepare-public-deploy-bundle.mjs, scripts/test-public-deploy-bundle.mjs
The deploy proof checks 404.html for not-found and noindex markers, rejects the homepage title, and documents the new neutral proof.

Estimated code review effort: 2 (Simple) | ~10 minutes

Mergeability Score: ⚪ Minimal · up to 372a4

This change adds a release-lane safeguard ensuring the required real-404 page is present before deployment; no actionable merge-blocking risk remains beyond normal checks and review.


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

Comment @coderabbitai help to get the list of available commands.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@nish3451
nish3451 merged commit d54c91f into main Aug 13, 2026
2 checks passed
nish3451 pushed a commit that referenced this pull request Aug 13, 2026
…le with main after PRs #135/#104/#99/#112/#100/#105)

The PR #130 branch had already been reconciled with origin/main through
c617364 (PRs #131 + #133/#134/#132/#116). Since then origin/main moved to
d4f3ef4 with six more merges (#135 #104 #99 #112 #100 #105). Pull them in
so PR #130 can be reviewed against current main without weakening the
capture/rollback/reverification implementation or the PR #131 fail-closed
missing-secret gate.

The merge resolved cleanly:
- PR #130 capture/rollback logic (publish-public-site.mjs canonical_deployment,
  rollbackTo, restored identity re-verify, accept-again) is intact.
- PR #130 test file (scripts/test-pages-release.mjs) is intact and wired
  into the npm ci/test scripts.
- PR #131 fail-loud missing-secret workflow step in
  .github/workflows/deploy-public-site.yml is preserved byte-for-byte.
- The new origin/main changes touch operator surfaces and the deploy
  bundle prep; none of them modify the publish-public-site.mjs rollback
  contract or the Pages token gate, so there were no conflict markers.
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