From 0e62b55236b0282118bea9800c6cfcdfd55c280c Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Wed, 5 Aug 2026 11:36:47 -0700 Subject: [PATCH 1/2] Record the transport retest and what #33 still owes --- TODO.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/TODO.md b/TODO.md index e65030c..f2475c9 100644 --- a/TODO.md +++ b/TODO.md @@ -15,7 +15,7 @@ The site is built, gated in CI, and deployed to staging by pipeline. It is not y | GitHub repo | public, both rulesets active, `configure.sh check` exits 0 | | Release pipeline | proven end to end. `1.0.17-g4b2def3ee9` is the newest, a prerelease from `develop` | | Fleet conformance | cataloged in the hub registry, audited, and carrying the current canonical | -| Deploy pipeline | `deploy-site.yml` is dispatchable and has deployed staging from CI end to end | +| Deploy pipeline | `deploy-site.yml` is dispatchable and has deployed staging from CI end to end, through a transport retested against the real host | | VPS staging | live at `blog.vps.insanegenius.net`, behind the auth gate, serving a pipeline release | | VPS production | reachable at `blog.insanegenius.net`, answering `200` unauthenticated, still serving `0000-placeholder`. DNS for the public name is still on the old platform | | Operations | not started. Neither the periodic log review nor the off-host log pull has run on its cadence | @@ -27,8 +27,7 @@ The site is built, gated in CI, and deployed to staging by pipeline. It is not y ## Next, in dependency order -- **Retest the deploy transport against the real host**, which is [#33][issue-33] and is joint work with whoever holds the server. The transport now pins `StrictHostKeyChecking`, `UserKnownHostsFile`, and `BatchMode`, so it fails closed where it previously failed open, and a stale `DEPLOY_SSH_KNOWN_HOSTS` stops a deploy rather than being tolerated. Staging first, since a broken transport blocks the rollback path as well as the deploy. -- **Declare what the VPS keeps.** `hugo.deploy.retention` asks for a retention count declared at the destination, and this repo's deploy credential is write-only by design so the prune belongs to the host. The ownership is recorded in `OPERATIONS.md`, the count is not, and the "ten releases" beside it describes `deploy/make-release.sh` on the local mirrors rather than the containers on the VPS. Confirm the host's timer and its count, then write it next to the ownership line. +- **Finish [#33][issue-33] by deploying production once.** Three of its four checks passed against the real host on 2026-08-05, and the evidence is on the issue: staging deployed end to end through the pinned transport, the stored `DEPLOY_SSH_KNOWN_HOSTS` verified against the live host key on both environments, `BatchMode` proven not to fight the forced command, and the production ref gate confirmed to refuse a dispatch from a non-default branch. The host side answered the rest, including that the runner reaches sshd directly and that one key is installed rather than the former pair. What remains is the fourth check, which is a production deploy, and that is the item two lines below rather than a transport question. - **Prove a rollback through the pipeline.** A forced mid-deploy failure, then a flip back to the previous release, verified by `EXPECT_RELEASE` rather than by the transport exiting zero. The server side has been measured at well under a second by hand; what is unproven is that a **pipeline** run leaves the site serving when its deploy fails part way. - **Deploy production once, to a name that is not the live one.** The host side is done and the interim name is live: `blog.insanegenius.net` answers `200` unauthenticated, on a Let's Encrypt certificate issued 2026-08-07, serving `0000-placeholder`. The VPS agent calls this M7a, and M7b is the `.com` cutover. Three items belong to this repo before the first production deploy, all detailed in the VPS agent's §19 and §20. That file is not in the repository, so pull it first per [`OPERATIONS.md`](./OPERATIONS.md) "The Channel Between the Two Sides": - **`HUGO_BASEURL` on the `production` environment is set to `https://blog.insanegenius.net/`**, done 2026-08-07. It held `https://blog.insanegenius.com/`, the live WordPress address, which is what the workflow both builds with and points the live check at, so a deploy would have baked the old platform's address into every canonical tag, feed link and `sitemap.xml` and then run 1,245 requests at the live site to verify it. **Setting it back to `.com` at M7b is the other half and is not done.** From b473a488caacec3a40f6e147f8f9b7fffe9f4d05 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Thu, 6 Aug 2026 16:39:51 -0700 Subject: [PATCH 2/2] Retire the #33 entry and hold the main publish for the soak The retest issue closes on the prerelease dispatch rather than on a production deploy, so the entry naming a production deploy as its fourth check is answered rather than outstanding. #33 has since closed, on 2026-08-06, which confirms it. The main publish is added as its own item, gated on the soak rather than on the mechanism, which is proven. 1.0.11 is still the newest release from main and still predates every deploy change, so the next release from main is the first that would describe a site serving its public address. Rebased onto develop and reconciled with what landed while this sat open. The hub row this branch added for ProjectTemplate#567 is dropped. That issue closed on 2026-08-05, a day before this branch was last touched, so the row was already wrong when written. Its claim has since been falsified outright: it said an agent cannot trip the production ref gate because the permission classifier refuses a production-labeled dispatch, and a production deploy was dispatched and completed on 2026-08-08. The `Deploy production once` bullet keeps develop's version rather than this branch's. This branch still described that environment as never having run, and it has now served a release. The `Declare what the VPS keeps` bullet goes, which the earlier commit dropped without saying why. It asked for the host's timer and its count to be written beside the ownership line, and `OPERATIONS.md` now names `blog-prune-releases.timer`, that it runs daily, and that it keeps ten bundles per environment. The item is answered rather than abandoned. The main publish item pointed at "the daily 404 watch above", which moved below it into Recurring operations, so it names that section instead. A pointer by position is the same defect a review already flagged on this branch once. Co-Authored-By: Claude Opus 5 (1M context) --- TODO.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index f2475c9..bbaf6e1 100644 --- a/TODO.md +++ b/TODO.md @@ -27,7 +27,6 @@ The site is built, gated in CI, and deployed to staging by pipeline. It is not y ## Next, in dependency order -- **Finish [#33][issue-33] by deploying production once.** Three of its four checks passed against the real host on 2026-08-05, and the evidence is on the issue: staging deployed end to end through the pinned transport, the stored `DEPLOY_SSH_KNOWN_HOSTS` verified against the live host key on both environments, `BatchMode` proven not to fight the forced command, and the production ref gate confirmed to refuse a dispatch from a non-default branch. The host side answered the rest, including that the runner reaches sshd directly and that one key is installed rather than the former pair. What remains is the fourth check, which is a production deploy, and that is the item two lines below rather than a transport question. - **Prove a rollback through the pipeline.** A forced mid-deploy failure, then a flip back to the previous release, verified by `EXPECT_RELEASE` rather than by the transport exiting zero. The server side has been measured at well under a second by hand; what is unproven is that a **pipeline** run leaves the site serving when its deploy fails part way. - **Deploy production once, to a name that is not the live one.** The host side is done and the interim name is live: `blog.insanegenius.net` answers `200` unauthenticated, on a Let's Encrypt certificate issued 2026-08-07, serving `0000-placeholder`. The VPS agent calls this M7a, and M7b is the `.com` cutover. Three items belong to this repo before the first production deploy, all detailed in the VPS agent's §19 and §20. That file is not in the repository, so pull it first per [`OPERATIONS.md`](./OPERATIONS.md) "The Channel Between the Two Sides": - **`HUGO_BASEURL` on the `production` environment is set to `https://blog.insanegenius.net/`**, done 2026-08-07. It held `https://blog.insanegenius.com/`, the live WordPress address, which is what the workflow both builds with and points the live check at, so a deploy would have baked the old platform's address into every canonical tag, feed link and `sitemap.xml` and then run 1,245 requests at the live site to verify it. **Setting it back to `.com` at M7b is the other half and is not done.** @@ -38,6 +37,7 @@ The site is built, gated in CI, and deployed to staging by pipeline. It is not y - **At the cutover this stops being a gap and becomes a loss, which is the half neither side had checked.** The live `.com` blog **serves a `robots.txt` today, carrying a `Sitemap:` line**. Since this site emits none, M7b is not a return to a previous state, it is a move from having crawl directives to having none on a site that has had them for years, and the sitemap pointer goes with them. The VPS agent measured this from the outside in §23.3, will not put a file in this repository's bundle, and has made it a decision that blocks step 1 of the M7b checklist rather than one discovered after it. The minimum that preserves today's behavior is `User-agent: *`, no `Disallow`, and the `.com` sitemap line, since every `Disallow` the old platform serves names a WordPress path this site does not have. **Deliberately undecided**, and the three options are to preserve today's behavior, to write what this site actually wants, or to keep emitting nothing and accept the loss. - **Nothing checks that media survived the trip to the server.** The VPS agent noticed in §24.3 that a 3,095-request gate run fetched no image at all, and asked whether `golden-media-legacy.txt` is wired in. It is, but only at build time, in `check-url-parity.py`, against files on disk. The live check requests pages and redirects and never an image, so a media tree lost **between the build and the server**, a partial upload, is caught by neither: the build passed before the loss and the live gate never asks. On a site whose value is eighteen years of posts with images in them, that is the gap worth closing rather than the one that was suspected. A handful of media URLs in the live check would close it, chosen to cover both trees rather than to be exhaustive, since the build gate already proves the set. - Lower the `blog` A-record TTL to 60s a day ahead, then flip it to the VPS, unproxied. +- **Publish a release from `main`, once the pipeline has soaked.** `1.0.11` is the newest release from `main` and was cut on 2026-08-01, ahead of every deploy change, so the next one is the first that would describe a site actually serving its public address. The mechanism is proven and is not what this waits on: it waits on the switchover being trusted rather than merely green, which is what the log review under **Recurring operations** establishes and no gate can. A release cut before that names a state that has not held yet. - Add the weekly non-blocking external-link-check workflow, which is the one gate that cannot be blocking because it fails on other people's outages. - Decommission WordPress.com only after **30 clean days**, and downgrade to free rather than deleting, which keeps the media reachable as a safety net and preserves the ability to re-export. Do not start sooner: the conversion fetched media over HTTP from the live site. @@ -159,7 +159,6 @@ The deploy root is deliberately absent from this table. The rsync destination is -[issue-33]: https://github.com/ptr727/Blog/issues/33 [issue-53]: https://github.com/ptr727/Blog/issues/53 [migration-post]: ./content/posts/2026/08/01/moving-this-blog-from-wordpress-to-hugo.md [pr-30]: https://github.com/ptr727/Blog/pull/30