Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
27 changes: 27 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
10 changes: 5 additions & 5 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
23 changes: 20 additions & 3 deletions docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.
12 changes: 12 additions & 0 deletions docs/migrations/h3-migration-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
165 changes: 165 additions & 0 deletions docs/migrations/h3.5-cutover-runbook.md
Original file line number Diff line number Diff line change
@@ -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.
55 changes: 55 additions & 0 deletions docs/seo.md
Original file line number Diff line number Diff line change
@@ -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.
5 changes: 5 additions & 0 deletions public/_headers
Original file line number Diff line number Diff line change
@@ -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
18 changes: 18 additions & 0 deletions public/_redirects
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,22 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Secure Tools is an ecosystem of independently maintained, local-first and privacy-conscious software projects.">
<link rel="canonical" href="https://securetools.app/">
<meta name="theme-color" content="#f7f7f5">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Secure Tools">
<meta property="og:title" content="Secure Tools — Local-first software ecosystem">
<meta property="og:description" content="Secure Tools is an ecosystem of independently maintained, local-first and privacy-conscious software projects.">
<meta property="og:url" content="https://securetools.app/">
<meta property="og:image" content="https://securetools.app/assets/brand/social-preview.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="Secure Tools — Local-first software ecosystem">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Secure Tools — Local-first software ecosystem">
<meta name="twitter:description" content="Secure Tools is an ecosystem of independently maintained, local-first and privacy-conscious software projects.">
<meta name="twitter:image" content="https://securetools.app/assets/brand/social-preview.png">
<meta name="twitter:image:alt" content="Secure Tools — Local-first software ecosystem">
<title>Secure Tools — Local-first software ecosystem</title>
<link rel="icon" href="/assets/brand/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="/assets/css/main.css">
Expand Down
Loading