diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6719c9d..d87477a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -35,6 +35,10 @@ jobs: required_files=( public/index.html public/404.html + public/_headers + public/_redirects + public/robots.txt + public/sitemap.xml ) for file in "${required_files[@]}"; do @@ -60,6 +64,9 @@ jobs: fi done + python scripts/validate-h3-url-map.py + python scripts/validate-h3-hub-cutover.py + echo "Validated static deployment output: public/" - name: Deploy to Cloudflare Pages diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..c8baaad --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,27 @@ +name: Validate Hub + +on: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + validate: + name: Validate static Hub + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Checkout source + # actions/checkout v6.0.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + persist-credentials: false + + - name: Validate H3 migration contracts + run: | + set -euo pipefail + python scripts/validate-h3-url-map.py + python scripts/validate-h3-hub-cutover.py diff --git a/README.md b/README.md index 66698cb..ac1db18 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,8 @@ validation target. - [Architecture](docs/architecture.md) - [Privacy model](docs/privacy-model.md) - [Deployment transparency](docs/deployment.md) +- [Search metadata](docs/seo.md) +- [H3.5 cutover runbook](docs/migrations/h3.5-cutover-runbook.md) - [H2.2 quality assurance](docs/h2.2-qa.md) The static site can be previewed by serving `public/` with any local static file server. diff --git a/docs/architecture.md b/docs/architecture.md index 043f135..114a216 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -44,11 +44,11 @@ of the circular CSS brand mark used by Web Utilities. The 1200×630 social previ specifically for the Hub and uses its catalog language and visual system; it is not a copy of the Web Utilities social image. -The social preview asset is ready, but host-dependent `og:image` and `twitter:image` metadata -is intentionally deferred until a separately reviewed production-domain migration establishes -the permanent Hub origin. Canonical and `og:url` metadata is deferred for the same reason. This -avoids coupling the public identity to a temporary validation hostname or claiming -`securetools.app` before migration. +H3.4B prepares the reviewed social preview at its final absolute +`https://securetools.app/assets/brand/social-preview.png` URL together with self-referencing +Hub canonical and `og:url` metadata. These source changes remain inactive while the H3.4B +pull request is unmerged. Pages validation aliases are covered by hostname-specific noindex +headers so they do not become alternate public identities after coordinated H3.5 activation. ## Product disclosure contract diff --git a/docs/deployment.md b/docs/deployment.md index bc5263c..935012d 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -41,9 +41,9 @@ permissions. ## Current validation deployment -The current deployment target is the Cloudflare Pages-managed `secure-tools-hub.pages.dev` -site. This endpoint is for validating the Hub deployment path and does not mean that the Hub -has become the Secure Tools production site. +The current deployment target is the Cloudflare Pages-managed +`https://secure-tools-hub-53i.pages.dev` site. This endpoint is for validating the Hub +deployment path and does not mean that the Hub has become the Secure Tools production site. ## Future production domain @@ -53,3 +53,20 @@ perform the future production migration. Any production domain migration and related redirects must be handled as a separate, explicitly reviewed milestone. + +## Prepared H3.5 deployment contract + +The unmerged H3.4B branch prepares static `public/_headers` and `public/_redirects` +artifacts. The hostname-specific header rules keep the stable and immutable +`*.secure-tools-hub-53i.pages.dev` aliases non-indexable without applying noindex to the +future custom domain. The redirects contain only the 18 explicit H3.1 legacy Web Utilities +paths and preserve each path on `https://tools.securetools.app`; the Hub root and all Hub +routes are excluded. + +Cloudflare Pages path redirects do not implement the future `www → apex` domain redirect. +That change requires a Cloudflare zone/account-level Redirect Rule or Bulk Redirect plus +proxied DNS during the coordinated H3.5 window. The exact activation and rollback order is +documented in [the H3.5 cutover runbook](./migrations/h3.5-cutover-runbook.md). + +The H3.4B pull request does not attach a custom domain, change DNS, activate a `www` rule, +modify Search Console, or deploy from its feature branch. diff --git a/docs/migrations/h3-migration-contract.md b/docs/migrations/h3-migration-contract.md index af59426..fd2c92d 100644 --- a/docs/migrations/h3-migration-contract.md +++ b/docs/migrations/h3-migration-contract.md @@ -158,3 +158,15 @@ Every gate requires recorded evidence and an explicit go/no-go decision. A failu 5. **H3.6 — Search Migration Monitoring:** submit host-specific sitemaps and monitor indexing, redirects, availability, and rollback signals. No later phase is authorized by this document alone. + +## H3.4B preparation + +[Issue #14](https://github.com/SecureToolsProject/hub/issues/14) prepares the Hub-owned H3.5 +artifacts on an unmerged branch: final Hub metadata, sitemap and robots files, +hostname-specific Pages-alias noindex headers, and the 18 explicit redirects derived from this +inventory. The executable sequence and rollback requirements are in +[h3.5-cutover-runbook.md](./h3.5-cutover-runbook.md). + +These files do not authorize or perform a merge, deployment, apex or `www` DNS change, +custom-domain attachment, zone redirect, Search Console operation, or Secure_Tools PR #73 +merge. H3.5 must coordinate those operations only after all preflight gates pass. diff --git a/docs/migrations/h3.5-cutover-runbook.md b/docs/migrations/h3.5-cutover-runbook.md new file mode 100644 index 0000000..d538334 --- /dev/null +++ b/docs/migrations/h3.5-cutover-runbook.md @@ -0,0 +1,165 @@ +# H3.5 coordinated cutover runbook + +Status: prepared by H3.4B; do not execute from the preparation pull request. + +This runbook coordinates the final architecture: + +```text +https://securetools.app +→ Secure Tools Project Hub + +https://tools.securetools.app +→ Secure Tools Web Utilities +``` + +The reviewed Hub preparation includes canonical and social metadata, a Hub-only sitemap, +production robots policy, Pages-alias noindex headers, and 18 explicit legacy redirects. +Secure_Tools PR #73 owns the separately reviewed Web Utilities SEO preparation. Neither +preparation pull request may be merged outside the H3.5 change window. + +## Preconditions and ownership + +- A named operator owns the Cloudflare, GitHub, DNS, TLS, HTTP, and rollback decisions. +- A second reviewer confirms recorded snapshots and the go/no-go decision. +- No Search Console change occurs until the HTTP, TLS, canonical, and redirect gates pass. +- Do not use Search Console Change of Address: this is a partial migration and the apex + remains active as the Hub. +- Preserve the existing Secure_Tools GitHub Pages workflow, repository `CNAME`, and custom + domain during initial stabilization unless a separately reviewed change requires removal. + +## A. Preflight and snapshots + +1. Confirm Hub H3.4B and Secure_Tools PR #73 are clean, reviewed, current with each repository's + `main`, and unmerged. +2. Record the exact head SHA of both preparation PRs and the current `main` SHA of both + repositories. +3. Verify all Hub repository validation and all Secure_Tools validation. +4. Verify HTTPS and expected content on: + - `https://secure-tools-hub-53i.pages.dev`; + - `https://tools.securetools.app`; + - `https://secure-tools-web-bridge.pages.dev`; and + - the existing `https://securetools.app` GitHub Pages production. +5. Snapshot, without exposing credential values: + - every current apex DNS record, type, value/target, proxy state, and TTL; + - the current `www` record, target, proxy state, and TTL; + - the Secure_Tools GitHub Pages custom-domain and HTTPS-enforcement state; + - Hub Pages project production branch, stable hostname, deployment, custom domains, and + analytics/Functions/Workers state; + - Web Utilities Pages project, tools custom domain, deployment, and indexing-header state; + - active CAA records and certificate-authority restrictions. +6. Store the snapshot in the H3.5 evidence record and explicitly identify the exact DNS values + to restore. Do not continue without a usable rollback record. +7. Recheck existing production immediately before the first mutation. Any unexplained DNS, + TLS, route, asset, or functional regression is a no-go. + +## B. Normalize `www` before the apex move + +1. Create a Cloudflare zone/account-level Redirect Rule or Bulk Redirect for: + + ```text + https://www.securetools.app/* + → 301 https://securetools.app/* + ``` + +2. Configure the Cloudflare-proxied placeholder DNS record required by the selected redirect + mechanism. Pages `_redirects` cannot implement this domain-level redirect. +3. Enable path and query-string preservation. Never send `www` to + `tools.securetools.app`. +4. While the apex still serves the existing Web Utilities production, validate representative + `www` paths and queries return one 301 hop to the equivalent working apex URL. +5. Confirm this removes the direct `www` dependency on GitHub Pages without changing the + apex origin. Roll back the `www` record and rule from the snapshot if validation fails. + +## C. Activate the Hub repository preparation + +1. Merge the reviewed H3.4B pull request using the repository's established merge method. +2. Wait for the automatic `main` deployment to `secure-tools-hub`. +3. Verify the immutable deployment and `secure-tools-hub-53i.pages.dev`: + - all 10 Hub routes and representative assets return 200; + - canonical, `og:url`, `og:image`, and `twitter:image` use + `https://securetools.app`; + - the Hub-only sitemap has 10 unique URLs and robots references it; + - stable and immutable Pages hostnames return + `X-Robots-Tag: noindex, nofollow`; + - all 18 legacy paths return a single 301 to the same path on + `tools.securetools.app`; and + - `/` renders the Hub and does not redirect. +4. Do not attach the apex yet. A deployment or validation failure is a no-go. + +## D. Activate the Web Utilities SEO identity + +1. Merge Secure_Tools PR #73 only after step C passes. +2. Wait for its automatic Cloudflare Pages deployment. +3. Validate all 19 H3.1 routes and representative assets on + `https://tools.securetools.app`. +4. Confirm the tools custom domain is indexable, all canonical and `og:url` values + self-reference the tools host, social images use that host, and its sitemap and robots + contain only Web Utilities URLs. +5. Confirm stable and immutable `secure-tools-web-bridge.pages.dev` hostnames remain + `noindex, nofollow`. +6. Confirm local file processing, privacy disclosures, and network behavior are unchanged. + Any mismatch blocks the apex move. + +## E. Attach the apex to the Hub + +1. Reconfirm the step A DNS snapshot, rollback values, existing GitHub Pages health, and CAA + compatibility. +2. In Cloudflare use **Workers & Pages → secure-tools-hub → Custom domains → Set up a + domain**, then enter `securetools.app`. +3. Because the apex is in a Cloudflare-managed zone, expect Pages association to change its + DNS relationship. Do not make unrelated DNS changes. +4. Wait for the custom domain to report Active and for a valid certificate covering + `securetools.app`. If CAA blocks issuance, stop and use a separately reviewed CAA change; + do not weaken certificate policy ad hoc. +5. Validate: + - `/` returns the Hub with 200 and no redirect; + - every Hub route, asset, sitemap, and robots response is correct; + - all 18 legacy paths return exactly one 301 to the identical + `tools.securetools.app` path; + - representative query strings survive the redirect; + - no Hub route collides with a redirect; and + - no loop, chain, mixed content, or certificate error exists. + +## F. Post-cutover isolation + +1. Revalidate the Hub apex, Hub Pages aliases, tools custom domain, tools Pages aliases, and + `www → apex` redirect. +2. Confirm the apex is indexable and Pages aliases remain non-indexable. +3. Confirm Web Utilities functionality and local-first network behavior on the tools host. +4. Keep the old GitHub Pages workflow, custom-domain configuration, `CNAME`, and known-good + deployment available as rollback infrastructure where practical during stabilization. +5. Record workflow runs, GitHub Deployments, immutable URLs, DNS state, headers, route matrix, + TLS evidence, and the final go/no-go decision. + +## G. Search activation + +Only after A–F pass: + +- submit `https://securetools.app/sitemap.xml` to the root property; +- monitor `https://tools.securetools.app/` separately and submit its sitemap to the tools + URL-prefix property; +- retain the domain property if already used for aggregate observation; and +- do not use Change of Address for this partial migration. + +Search Console changes and ongoing migration monitoring belong to the later monitoring phase, +not H3.4B. + +## Immediate rollback + +Rollback is triggered by a critical TLS, DNS, availability, route, redirect, canonical, +privacy, or functional failure that cannot be corrected safely inside the change window. + +1. Stop additional activation and preserve evidence. +2. Restore the snapshotted apex DNS relationship so `securetools.app` returns to the previous + GitHub Pages Web Utilities production. +3. Restore the previous `www` DNS/redirect behavior if the normalized rule prevents the + known-good service from being reached. +4. Confirm GitHub Pages custom-domain and HTTPS state match the snapshot. +5. Revalidate production routes, assets, TLS, and representative functions. +6. Revert or redeploy the Hub and Web Utilities SEO changes as needed to avoid conflicting + canonical, sitemap, robots, or indexing signals. +7. Leave `tools.securetools.app` available on the parallel Cloudflare deployment unless it is + itself the failure source. Do not destroy the bridge merely to restore the apex. + +Rollback completion requires recorded DNS, TLS, HTTP, and functional evidence. Cleanup of old +infrastructure is a later, separately reviewed decision. diff --git a/docs/seo.md b/docs/seo.md new file mode 100644 index 0000000..810d28e --- /dev/null +++ b/docs/seo.md @@ -0,0 +1,55 @@ +# Hub search metadata + +Status: H3.4B preparation only. The pull request must remain unmerged until the coordinated +H3.5 cutover. + +## Final canonical identity + +The final Hub origin is `https://securetools.app`. Each of the 10 public Hub routes has one +self-referencing absolute canonical and matching `og:url`. Open Graph and X image metadata +uses the existing 1200×630 PNG at +`https://securetools.app/assets/brand/social-preview.png`. + +`404.html` is an error document, not a canonical Hub route. It is excluded from the sitemap +and does not receive a canonical URL. + +## Crawler files + +- `public/sitemap.xml` lists exactly the 10 Hub canonical URLs. +- `public/robots.txt` allows crawling and references + `https://securetools.app/sitemap.xml`. +- No Web Utilities route, tools host, legacy redirect source, or Pages hostname appears in the + Hub sitemap. + +The static `public/_headers` contract applies +`X-Robots-Tag: noindex, nofollow` only to: + +```text +https://secure-tools-hub-53i.pages.dev/* +https://:version.secure-tools-hub-53i.pages.dev/* +``` + +The future `securetools.app` custom domain does not match those patterns and remains +indexable. No Worker or Pages Function is required. + +## Legacy path ownership + +`public/_redirects` contains exactly the 18 explicit 301 mappings in +`docs/migrations/h3-url-map.csv`. Every destination is the same path on +`https://tools.securetools.app`. The Hub root and all Hub routes are excluded; no wildcard +can swallow future Hub content. + +These redirects become reachable on the apex only after H3.5 attaches the custom domain to +the Hub Pages project. The H3.4B feature branch does not deploy or activate them. + +## Validation and search activation + +`scripts/validate-h3-hub-cutover.py` checks route metadata, social image dimensions, +sitemap, robots, and Pages-alias isolation. `scripts/validate-h3-url-map.py` compares the +redirect artifact directly with the H3.1 inventory and rejects duplicates, wildcards, wrong +hosts, changed paths, non-301 status, root redirects, and Hub-route collisions. + +Search Console remains unchanged during preparation. After H3.5 HTTP, TLS, canonical, and +redirect validation, submit the Hub sitemap to the root property and monitor Web Utilities +through the tools URL-prefix property. Do not use Change of Address for this partial +migration. diff --git a/public/_headers b/public/_headers new file mode 100644 index 0000000..21bffbe --- /dev/null +++ b/public/_headers @@ -0,0 +1,5 @@ +https://secure-tools-hub-53i.pages.dev/* + X-Robots-Tag: noindex, nofollow + +https://:version.secure-tools-hub-53i.pages.dev/* + X-Robots-Tag: noindex, nofollow diff --git a/public/_redirects b/public/_redirects new file mode 100644 index 0000000..f7d2f37 --- /dev/null +++ b/public/_redirects @@ -0,0 +1,18 @@ +/about/ https://tools.securetools.app/about/ 301 +/privacy/ https://tools.securetools.app/privacy/ 301 +/tools/pdf/ https://tools.securetools.app/tools/pdf/ 301 +/tools/pdf/images-to-pdf/ https://tools.securetools.app/tools/pdf/images-to-pdf/ 301 +/tools/pdf/merge/ https://tools.securetools.app/tools/pdf/merge/ 301 +/tools/pdf/split/ https://tools.securetools.app/tools/pdf/split/ 301 +/tools/pdf/organize/ https://tools.securetools.app/tools/pdf/organize/ 301 +/tools/pdf/to-images/ https://tools.securetools.app/tools/pdf/to-images/ 301 +/tools/pdf/metadata/ https://tools.securetools.app/tools/pdf/metadata/ 301 +/tools/image/ https://tools.securetools.app/tools/image/ 301 +/tools/image/converter/ https://tools.securetools.app/tools/image/converter/ 301 +/tools/image/resize/ https://tools.securetools.app/tools/image/resize/ 301 +/tools/image/compress/ https://tools.securetools.app/tools/image/compress/ 301 +/tools/image/metadata/ https://tools.securetools.app/tools/image/metadata/ 301 +/tools/privacy/ https://tools.securetools.app/tools/privacy/ 301 +/tools/scan/ https://tools.securetools.app/tools/scan/ 301 +/tools/media/ https://tools.securetools.app/tools/media/ 301 +/tools/image-to-pdf/ https://tools.securetools.app/tools/image-to-pdf/ 301 diff --git a/public/index.html b/public/index.html index 9e69d83..dbbf7af 100644 --- a/public/index.html +++ b/public/index.html @@ -4,14 +4,22 @@ + + + + + + + +
- Current site: securetools.app - currently serves Web Utilities. A later ecosystem migration may move this product, - but no future subdomain is linked or implied here. + Web application: tools.securetools.app + hosts the parallel Web Utilities deployment and becomes the product's canonical + public endpoint during the coordinated H3.5 cutover.