Record completed internal CMS rollout

This commit is contained in:
Kevin
2026-09-04 22:25:57 -04:00
parent dc3ec3ff30
commit 5652339676
5 changed files with 234 additions and 142 deletions
+67 -32
View File
@@ -1,24 +1,66 @@
# Happy Tails Astro pilot
# Happy Tails Astro sites and internal CMS
This repository is a static Astro pilot for `3031023.xyz`. The existing
Cloudflare Pages site remains the rollback target while this branch is tested.
This repository backs the production Happy Tails sites at `3031023.xyz` and
`blog.3031023.xyz`. Both are static Astro builds deployed as separate
Cloudflare Workers. The retained Cloudflare Pages project remains the primary
site rollback target.
The planned preview-only editing experiment is documented in the [Decap CMS
Gitea pilot](docs/decap-cms-pilot.md). It does not authorize CMS activation, a
production merge, or a domain change.
The Decap CMS workflow is operational for internal SloanOps use. It is not a
public feature or a client-editing offer. The approved internal editor is
`sloanops.admin`; do not invite additional editors without a separate access,
support, and security review.
## Repository layout
- `sites/www/` is the primary-site content location; it serves `3031023.xyz`.
- `sites/blog/` is the separate `blog.3031023.xyz` sample site.
- `sites.json` is the operator-controlled manifest. It is not public intake.
- `sites/www/` contains primary-site Markdown and site data.
- `sites/blog/` contains the Journal Markdown and site data.
- `public/admin/` contains the shared, no-index Decap admin shell and config.
- `public/images/uploads/` is the only CMS-writable media directory.
- `sites.json` is the operator-controlled build manifest, not public intake.
- `src/` contains shared Astro presentation code.
- `schemas/` and `fixtures/` provide the Phase 1 JSON contract proof.
- `schemas/`, `scripts/`, and `fixtures/` enforce the content and deployment
contracts.
Blog posts and primary-site FAQs are now static Markdown collections with
validated frontmatter and draft filtering. Production CMS publishing, public
sign-up changes, n8n changes, and onboarding-worker changes remain out of
scope; the linked Decap plan is a preview-only editing experiment.
## Internal CMS workflow
Use `https://3031023.xyz/admin/` for normal internal editing. Decap commits
content and approved raster media directly to the protected `cms` staging
branch; it never writes to `main`.
1. Make and review content changes on `cms`.
2. Confirm the `cms` Worker version previews and content validation results.
3. Open a pull request from `cms` to `main`.
4. Merge only after the required CMS validation, both site builds, and SloanOps
review pass.
5. Relevant `main` changes deploy the production primary and/or blog Worker.
6. Synchronize the resulting `main` merge commit back into `cms` through a
protected-branch pull request before the next editing cycle.
Operator-authored CMS application or configuration work also starts from the
current `cms` state. Use a short branch and a pull request into protected
`cms`, then promote the reviewed `cms` state to `main`. Direct protected-file
pushes to `cms` and all direct pushes to `main` are intentionally rejected.
Only the stable CMS and production admin origins are registered with Gitea.
Logging in from a branch-specific Worker preview alias therefore produces
`Unregistered Redirect URI`; this is expected. Never solve it with a wildcard
OAuth redirect or wildcard CORS origin. Use branch previews to inspect rendered
site output and an approved admin origin to test editing.
See the [CMS operating record](docs/decap-cms-pilot.md) and
[Gitea settings](docs/decap-cms-gitea-setup.md) for the complete boundaries.
## Content and media behavior
The CMS exposes primary FAQs, services, team members, fixed primary page
images, and Journal posts. Draft content is excluded from pages and sitemaps.
Journal publish and optional updated dates use Decap's supported `datetime`
widget in date-only UTC mode; new posts default the publish date to the current
UTC date.
CMS image sources must be approved JPG, PNG, WebP, or AVIF files in the flat
uploads directory and may be at most 25 MiB. Every build re-encodes public
copies to at most 1920 pixels and 500 KiB and strips embedded metadata.
## Local checks
@@ -37,25 +79,18 @@ Each selected site builds to `dist/<site-key>/`. The Worker configurations use
only static assets and a custom 404 page. They contain no custom domains,
routes, account IDs, or secrets.
`npm run verify:cms` checks the fixed Gitea branch/backend, the four Markdown
content contracts, the fixed page-image record, the bounded raster-media boundary, the pinned admin
shell, and the stable secret-free pull-request status context. The live steps
remain unapplied; see the [Gitea pilot settings](docs/decap-cms-gitea-setup.md)
for the exact review checklist.
`npm run verify:cms` checks the fixed Gitea backend and `cms` branch, all CMS
content contracts, media boundaries, the pinned admin shell, supported
widgets, and the stable secret-free pull-request status context.
## Preview deployment
The Gitea workflows upload scoped non-production preview versions of the
production Workers from every non-`main` branch. Cloudflare assigns each branch
a readable `workers.dev` preview alias without changing production traffic.
Relevant primary-site pushes to `main` deploy `3031023-xyz-www`, and relevant
blog pushes to `main` deploy `3031023-xyz-blog`, automatically. Both production
workflows deploy static assets only.
It uses the existing `CF_API_TOKEN` and `CF_ACCOUNT_ID`. The token retains the
Pages permissions needed for the `main` rollback target and now also permits
the non-production Worker preview deployment.
Routine jobs do not configure DNS or custom domains.
Non-`main` pushes upload no-index Cloudflare Worker versions with readable
branch aliases; they do not move production traffic. Relevant `main` pushes
deploy `3031023-xyz-www` and/or `3031023-xyz-blog` automatically after building
and verifying the selected site. Routine workflows contain no DNS, custom
domain, route, account ID, or token configuration.
Production hostname attachment and the `www` redirect are manual,
operator-controlled cutover steps. See `docs/pilot-baseline.md` for the
rollback record.
The production domain bindings, `www` redirect, retained Pages rollback, and
recovery steps are recorded in the [primary cutover runbook](docs/cutover-runbook.md)
and [blog production runbook](docs/blog-production-runbook.md).
+15 -7
View File
@@ -19,13 +19,21 @@ Phase 6 adds two validated Markdown collections:
homepage.
Both collections require frontmatter and support `draft: true`. Draft entries
are excluded from the rendered pages and sitemap. This is a Git-backed pilot;
it does not add a CMS or client-editing access.
are excluded from the rendered pages and sitemap. The internal Decap CMS now
edits these collections through the protected `cms` staging branch; client and
public editing remain disabled.
Images must live under `public/images/`, be referenced with descriptive alt
text where rendered, and remain at or below the 500 KiB pilot limit. The build
will reject malformed site JSON and oversize source images.
Journal `pubDate` and optional `updatedDate` fields use Decap's supported
`datetime` control in date-only UTC mode. New posts default `pubDate` to the
current UTC date. Repository verification rejects the unsupported `date`
widget so the missing-control failure cannot silently return.
CMS images must live in the flat `public/images/uploads/` directory and be
referenced with descriptive alt text where rendered. Approved source rasters
may be at most 25 MiB; each build produces a metadata-free public copy no
larger than 1920 pixels or 500 KiB. The build rejects malformed site JSON,
disallowed image signatures, nested upload paths, and oversized sources.
Each selected build also generates `sitemap.xml` and `robots.txt`. Preview
builds disallow indexing; a future approved production build must set
`DEPLOY_ENV=production` to publish crawlable robots rules and the sitemap URL.
builds disallow indexing; production workflows set `DEPLOY_ENV=production` to
publish crawlable robots rules and the sitemap URL.
+42 -33
View File
@@ -1,9 +1,9 @@
# Decap CMS: Gitea settings
Status: the preview-only pilot was accepted for promotion on 2026-09-04. The
repository now targets the permanent `cms` staging branch. Update the exact
CORS origin and PKCE OAuth redirect only after its first preview deployment
confirms the predicted alias. Nothing has been merged to `main`.
Status: applied and verified for internal use on 2026-09-04. The repository
targets the permanent `cms` staging branch, the CMS application is deployed
from `main`, and `sloanops.admin` is the approved internal editor. Client or
public access remains out of scope.
The repository implementation targets these fixed public values:
@@ -14,7 +14,8 @@ The repository implementation targets these fixed public values:
| Gitea base URL | `https://git.sloanops.com` |
| Gitea API root | `https://git.sloanops.com/api/v1` |
| Stable CMS origin | `https://cms-3031023-xyz-www.ksloan90.workers.dev` |
| Exact OAuth redirect URI | `https://cms-3031023-xyz-www.ksloan90.workers.dev/admin/` |
| Production admin origin | `https://3031023.xyz` |
| Exact OAuth redirects | `https://cms-3031023-xyz-www.ksloan90.workers.dev/admin/` and `https://3031023.xyz/admin/` |
| Required status context | `CMS content validation / cms-content-validation (pull_request)` |
The pilot and permanent branch names are intentionally slash-free. Decap
@@ -24,10 +25,10 @@ presented as an empty collection. The permanent `cms` branch is still a
non-production staging branch: changes reach `main` only through the protected
pull-request flow.
The Workers alias was confirmed after the first approved preview deployment.
If Cloudflare later assigns a different stable origin,
update the repository configuration, CORS origin, and OAuth redirect URI
together through review before enabling login.
The stable Worker alias and production admin route were confirmed. If either
origin changes, update repository configuration, CORS, and OAuth together
through review. Branch-specific preview aliases are intentionally not
registered and will return `Unregistered Redirect URI` during login.
## Runner cache settings
@@ -53,24 +54,23 @@ action branches on every job; npm packages remain keyed separately by
## Applied CORS policy
The tracked Gitea service in
`/home/sloan/gitRepos/SloanOps/infrastructure/docker-compose.yml` contains
these entries in the existing `gitea.environment` list and was deployed
through the normal infrastructure change process:
`/home/sloan/gitRepos/SloanOps/infrastructure/docker-compose.yml` was deployed
with exact origins for the internal CMS applications. The two origins relevant
to this repository are represented by these settings:
```yaml
- GITEA__cors__ENABLED=true
- GITEA__cors__ALLOW_DOMAIN=https://cms-3031023-xyz-www.ksloan90.workers.dev
- GITEA__cors__ALLOW_DOMAIN=https://cms-3031023-xyz-www.ksloan90.workers.dev,https://3031023.xyz
- GITEA__cors__METHODS=GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS
- GITEA__cors__MAX_AGE=10m
- GITEA__cors__ALLOW_CREDENTIALS=false
- GITEA__cors__HEADERS=Authorization,Content-Type
```
This is one exact origin, no wildcard, no cookie-bearing cross-origin request,
and only the API methods and non-safelisted request headers used by the Gitea
backend. Do not add the production domains, the journal preview, arbitrary
Workers preview hosts, or localhost. Gitea's same-origin login page continues
to use its own session cookie; Decap API calls use the OAuth bearer token.
These are exact origins, not wildcards, and API requests do not carry cookies.
Do not add arbitrary Worker preview hosts or localhost. Gitea's same-origin
login page continues to use its own session cookie; Decap API calls use the
OAuth bearer token.
After an approved Gitea deployment, verify a preflight without including a
token or cookie:
@@ -94,11 +94,11 @@ CORS response were verified:
- Application name: `3031023.xyz Decap CMS`.
- Confidential client: **unchecked**. The browser client must use PKCE.
- Redirect URI: exactly
`https://cms-3031023-xyz-www.ksloan90.workers.dev/admin/`, including the
trailing slash.
- Do not add wildcards, localhost, either production hostname, or the journal
preview as additional redirects.
- Redirect URIs: exactly
`https://cms-3031023-xyz-www.ksloan90.workers.dev/admin/` and
`https://3031023.xyz/admin/`, including trailing slashes.
- Do not add wildcards, localhost, the journal hostname, or branch-specific
Worker preview aliases as additional redirects.
- Copy only the public client ID into `backend.app_id` in
`public/admin/config.yml` through a reviewed commit.
- Do not store or use a client secret in the repository, Actions, Cloudflare,
@@ -158,6 +158,11 @@ merge, administrator, or bypass allowlists.
| Protection bypass allowlist | Enabled only for the named SloanOps operator user/team |
| CMS/client account | Write access, but absent from every allowlist |
Do not assume a local Git identity can use the operator bypass. A direct
protected-file push was rejected during the final Journal repair sync, and the
successful path was a short branch followed by PR #11 into `cms`. Prefer that
reviewable path for all operator-authored CMS code and configuration changes.
Enter this exact single value in **Unprotected file patterns**:
```text
@@ -197,18 +202,22 @@ Workflows, source, scripts, package files, schemas, manifests, Wrangler
configuration, `site.json` files, and the CMS application therefore cannot be
altered by choosing another branch name.
## Verification before an editor invitation
## Routine verification and promotion
1. Run `npm ci`, `npm run verify:cms`, and both site build/verifier pairs.
2. Create `cms` from the reviewed baseline only after approval.
3. Apply both protection rules together and confirm `main` is priority 1 and
`*` is priority 2.
4. Use the same non-operator account intended for Decap to prove allowed
Markdown edits succeed and every protected-path/direct-`main` attempt fails.
5. Open, but do not merge, a pull request into `main`. Confirm Gitea reports
the exact required status context above and blocks merge without both its
successful result and a current SloanOps approval.
6. Confirm both branch previews remain no-index and production is unchanged.
2. For CMS application/configuration work, branch from current `cms`, open a
pull request back into `cms`, and review the no-index version previews.
The required CMS status is scoped to pull requests into `main`, so the local
full check is mandatory before this staging merge.
3. Open `cms` to `main` and confirm Gitea reports the exact required status
context above and blocks merge without its successful result and current
SloanOps approval.
4. Merge deliberately, then confirm each affected production Worker workflow
succeeds and smoke-test the public routes.
5. Merge the resulting `main` commit back into `cms` through another
protected-branch pull request if the histories diverge.
6. Confirm previews remain no-index and never expand OAuth/CORS for a temporary
branch alias.
Do not treat a Decap UI restriction as authorization. Gitea's first-match
branch rules are the enforcement boundary, and the pull-request build is the
+84 -50
View File
@@ -1,19 +1,21 @@
# Decap CMS Gitea pilot and promotion
Status: the preview-only pilot was accepted for promotion to the permanent
`cms` staging branch on 2026-09-04. Repository configuration now targets the
predicted stable `cms` preview origin. CORS and OAuth must be changed together
only after that first preview confirms the alias. Nothing has been merged to
`main`, and production remains unchanged.
Status: completed and operational for internal SloanOps use as of 2026-09-04.
The reviewed CMS application is deployed from `main`, Decap writes only to the
protected `cms` staging branch, and the final Journal date-control repair is
present on both branches. Production deployments from the approved merge
completed successfully.
This remains an internal capability for `sloanops.admin`, not a public feature
or a client-editing offer. No client account has been invited.
Updated: 2026-09-04
## Purpose
Use the `3031023.xyz` demonstration repository to determine whether
Decap CMS can provide a useful editing interface for all sites owned under one
root domain without allowing an editor to publish directly to production or
modify deployment code.
Record and operate the Decap CMS editing interface for both sites in the
`3031023.xyz` demonstration repository without allowing an editor to publish
directly to production or modify deployment code.
The pilot covers both the primary Happy Tails site and its journal subdomain.
That matches the SloanOps repository boundary: one private repository per root
@@ -21,8 +23,9 @@ domain, with independently built sites beneath `sites/`. A future client who
owns the root-domain site also owns the hosted subdomains represented in that
repository.
This plan does not authorize a production merge, domain change, contact-form
activation, repository invitation, or removal of a rollback resource.
The completed internal rollout does not authorize public discovery, a client
repository invitation, contact-form changes, a broader OAuth grant, or removal
of a rollback resource.
## Current evidence
@@ -41,6 +44,13 @@ activation, repository invitation, or removal of a rollback resource.
writes directly to the configured branch.
- The Gitea backend does not support Git LFS. Pilot media must remain small,
ordinary repository assets.
- The approved `sloanops.admin` account can authenticate at the production
admin route, load all collections, and write through the `cms` branch.
- Gitea rejects protected-file pushes to `cms` and direct pushes to `main`;
operator code/configuration changes therefore use pull requests as well.
- Arbitrary branch-preview hostnames are deliberately absent from the OAuth
redirect and CORS allowlists. Login from one returns `Unregistered Redirect
URI`; rendered branch previews remain usable without expanding OAuth.
References:
@@ -56,8 +66,8 @@ recorded in [Decap CMS pilot: Gitea settings](decap-cms-gitea-setup.md).
## Repository implementation
The reviewed pilot implementation is the baseline for the permanent `cms`
staging branch:
The reviewed production implementation and permanent `cms` staging branch
provide the following controls:
- `public/admin/` provides a permanently no-index admin shell and one shared
configuration for both site builds.
@@ -68,6 +78,10 @@ staging branch:
- All four Markdown content collections and every persisted frontmatter field
are modeled with the current Astro/Zod bounds. Markdown controls remain
text-first; embedded images and general file widgets are absent.
- Journal publish and optional updated dates use the supported Decap
`datetime` widget in date-only UTC mode. New posts default to the current UTC
date, and the verifier rejects the unsupported `date` widget that caused the
original create/update failures.
- A fixed image-only Markdown record controls the primary home, About,
Services, and Contact page imagery without exposing either `site.json`.
- Team and page-image fields accept optional bounded uploads. Editors can add,
@@ -94,11 +108,11 @@ staging branch:
repository cache without trusting a mutable tag or refreshing GitHub on every
job.
The pilot dependencies were verified on `cms-pilot`. The same protection rules
apply to `cms`; its exact-origin CORS and OAuth values are the remaining
operator-controlled promotion step.
The pilot dependencies were verified on `cms-pilot`, promoted to the permanent
`cms` branch, and deployed to `main`. Exact-origin CORS, PKCE OAuth, branch
protection, previews, validation, and production deployment are all applied.
## Proposed pilot architecture
## Operating architecture
```text
Decap at /admin/
@@ -126,16 +140,17 @@ is acceptable because both sites belong to the same root-domain repository and
the same owner. One Decap configuration exposes clearly labeled Primary Site
and Journal collection groups.
Predicted preview URLs for branch `cms`:
Confirmed stable preview URLs for branch `cms`:
- `https://cms-3031023-xyz-www.ksloan90.workers.dev/admin/`
- `https://cms-3031023-xyz-www.ksloan90.workers.dev/`
- `https://cms-3031023-xyz-blog.ksloan90.workers.dev/`
Confirm the aliases from the successful Gitea Actions run rather than treating
these predicted URLs as authoritative.
The production admin entry is `https://3031023.xyz/admin/`. Use the stable
`cms` admin origin or the production admin entry for interactive CMS testing;
do not use arbitrary branch aliases for OAuth login.
## Initial Decap scope
## Internal Decap scope
Allow editing and creation only within these collections:
@@ -248,34 +263,30 @@ Preview deployment remains branch-specific and may use the existing Worker
workflows. Cloudflare credentials stay scoped to the deployment step; content
validation does not need them.
Because Decap writes to a fixed `cms` branch, SloanOps creates the pull
request in Gitea. After an approved merge, synchronize the CMS branch with
`main` before the next editing cycle. Do not automate production merge.
Because Decap writes to a fixed `cms` branch, SloanOps creates the pull request
from `cms` to `main` in Gitea. Internal CMS application/configuration changes
start from the current `cms` state, enter protected `cms` through an
operator-reviewed pull request, and are then promoted with the staged content.
After an approved `main` merge, synchronize its merge commit back into `cms`
through a protected-branch pull request before the next editing cycle. Do not
automate production merge.
The accepted pilot is promoted by replacing the disposable branch with the
permanent, slash-free `cms` branch. It remains a preview/staging branch and
retains the same pull-request, validation, and approval path into `main`.
## Gitea and Decap setup
## Routine operating sequence
1. Add a non-confidential Gitea OAuth application for the exact preview
`/admin/` redirect URI or URIs. Store only the public client ID in Decap.
2. Enable Gitea API CORS for the exact approved preview origin or origins.
Allow only required methods and headers, including `Authorization` and
`Content-Type`; do not use a wildcard origin.
3. Create `cms` from the reviewed repository baseline.
4. Review the pinned Decap admin assets beneath `public/admin/`. Do not replace
the exact version and integrity value with a floating range.
5. Configure the Gitea backend for `SloanOps/3031023-xyz` and branch
`cms`.
6. Model every persisted frontmatter field with limits matching
`src/content.config.ts`. Do not let a CMS save silently remove an existing
field.
7. Apply and verify the `main` and fallback branch protections before inviting
or testing a non-operator editor.
8. Add the secret-free pull-request validation workflow.
9. Push only the review branch and confirm both no-index preview builds before
any CMS login test.
1. Edit through an approved admin origin and confirm the commit lands only on
`cms` in an allowed content or media path.
2. Confirm the affected no-index Worker version preview succeeds.
3. Run the local CMS verifier and both site build/verifier pairs before any
operator-authored CMS application or configuration PR into `cms`.
4. Review the complete staged diff and open `cms` to `main`.
5. Require the stable secret-free validation status and SloanOps approval.
6. Merge deliberately and confirm the affected production Worker jobs pass.
7. Synchronize the resulting `main` merge commit back into protected `cms`
before another editing cycle.
## Test matrix
@@ -305,13 +316,36 @@ not record OAuth tokens or Cloudflare credentials.
| DC-19 | Refresh, sign out, reauthenticate, and let a session expire | Session behavior is understandable and no unsaved-content loss is observed. |
| DC-20 | Review commit attribution and audit history | Gitea identifies the editor and the exact content/media changes. |
Do not merge DC-15 during the initial infrastructure and editing test. A later
harmless demo-content merge requires explicit approval and repeats both public
site checks after deployment.
The initial no-merge restriction applied only while the pilot was being
evaluated. Promotion is now allowed through the protected workflow above; each
release still requires explicit review and repeats both public site checks.
## Acceptance criteria
## Completion record — 2026-09-04
Accept the pilot only when:
- PR #1 launched the no-index Decap shell and Gitea-backed collections.
- PRs #2 and #3 tightened image filename validation and added bounded,
metadata-stripping build optimization.
- PRs #4 and #5 synchronized the reviewed implementation into `cms` and then
promoted it to `main`.
- Disposable Journal, page-image, and FAQ edits exposed workflow and schema
behavior; the test content was reverted and the temporary FAQ was removed.
- PR #9 consolidated the rollback history and resynchronized the branches.
- Failed Journal content runs were traced to Decap's unsupported `date` widget.
PR #10 replaced it with date-only `datetime` controls, defaulted new publish
dates, and added a verifier regression guard.
- PR #11 synchronized the final repair into protected `cms` after the direct
protected-file push was correctly rejected.
- CMS validation and both site builds passed locally. The PR #10 required
validation passed, and production blog run 342 plus primary run 343 both
completed successfully.
- The deployed Journal editor was opened at `3031023.xyz/admin/`; Publish date
displayed a populated UTC date and Updated date displayed an optional date
control. No post was saved during this final smoke test.
## Operating acceptance boundary
The internal pilot was accepted against these controls. Re-evaluate them before
any client rollout:
- both site families are understandable in one CMS;
- content edits preserve every modeled field and pass Astro validation;
@@ -329,7 +363,7 @@ account control over executable deployment files.
## Rollback and cleanup
If the pilot is rejected or paused:
If the internal CMS is paused:
1. Revert or delete disposable changes on `cms`.
2. Delete the two disposable preview aliases/versions if desired; do not delete
@@ -341,7 +375,7 @@ If the pilot is rejected or paused:
6. Remove or retain branch protections deliberately; never weaken `main` as
cleanup.
## Implementation estimate
## Historical implementation estimate
Expected hands-on implementation time: **6–9 hours**, normally one focused
working day. Budget up to two elapsed days if OAuth/CORS behavior or Gitea
+26 -20
View File
@@ -1,24 +1,30 @@
# Preview deployment and later cutover controls
# Worker version previews
The preview workflow deploys static output only. It has two deliberately
separate Worker names:
Non-`main` pushes build and verify the affected site, then upload a
non-production version of its production Worker with a readable branch alias.
They do not deploy the version to production traffic.
| Site key | Preview Worker | Future hostname |
| --- | --- | --- |
| `www` | `3031023-xyz-www-preview` | `3031023.xyz` |
| `blog` | `3031023-xyz-blog-preview` | `blog.3031023.xyz` |
| Site key | Versioned Worker | Stable `cms` alias | Production hostname |
| --- | --- | --- | --- |
| `www` | `3031023-xyz-www` | `cms-3031023-xyz-www.ksloan90.workers.dev` | `3031023.xyz` |
| `blog` | `3031023-xyz-blog` | `cms-3031023-xyz-blog.ksloan90.workers.dev` | `blog.3031023.xyz` |
`workers.dev` is the only preview destination. Neither Wrangler configuration
contains a route, custom domain, zone ID, account ID, or token. Cloudflare
credentials remain Gitea repository secrets. The preview workflow uses the
existing `CF_API_TOKEN`, which has Account Workers Scripts edit/write for this
pilot plus the retained Pages rollback permissions. It uses `CF_ACCOUNT_ID` as
the account selector. Do not grant DNS edit permissions for routine preview
deployment.
The workflows derive the preview alias from the branch name, build with
preview robots rules, run output verification, and call `wrangler versions
upload`. They use the existing `CF_API_TOKEN` and `CF_ACCOUNT_ID` repository
secrets but contain no DNS, routes, custom-domain mutations, account IDs, or
tokens in source.
The primary Worker must pass its public preview test matrix before an operator
records the exact Pages deployment/DNS state and requests production-cutover
approval. A production deployment should use a separately named approved
Worker or an explicitly promoted Worker version; it must not be an ordinary
push job. Attaching `3031023.xyz`, configuring the `www.3031023.xyz` redirect,
and attaching `blog.3031023.xyz` each remain manual approval-boundary actions.
Only the stable CMS and production admin origins are registered with Gitea
OAuth/CORS. A CMS login attempt from a temporary branch alias is expected to
fail with `Unregistered Redirect URI`; do not add a wildcard. Review rendered
site output on the branch alias and use `https://3031023.xyz/admin/` or the
stable `cms` admin origin for editing tests.
Relevant pushes to `main` use separate production workflows and `wrangler
deploy` for the affected Worker after a production build and verifier pass.
The existing domain bindings do not change during routine deployments.
The older `*-preview` Wrangler configurations and Workers are retained as
rollback artifacts. Current branch workflows use Worker version previews of
the production-named Workers instead.