Skip to main content

Document metadata

Status
Review candidate
Approval
Pending
Version
1.0
Classification
PUBLIC
Owner
Lightning IT Documentation Maintainers
Approver
Lightning IT Product Owners
Audience
delivery engineers, repository maintainers, production approvers, security reviewers
Last reviewed
Next review
(Semiannual)

CI/CD and Cloudflare deployment architecture

This planning record is the implementation handoff for #40. It does not change Cloudflare, DNS, credentials, workflows, branches, or production. Issue #38 must approve this architecture before #40 executes it.

Architecture states

StateVerified or approved position
CurrentStatic Docusaurus build, Pagefind, build/, Cloudflare Pages previews/production, develop integration, main stable; workflows validate previews and production.
Transition#40 pins the deployment client, introduces immutable artifact upload/promotion, reconciles Git-connected settings, and verifies the custom domain.
TargetOne validated candidate artifact is uploaded unchanged to preview or production through separated protected environments and externally accepted.

Repository evidence shows designed behavior, not current dashboard, DNS, certificate, credential, or production-revision state. #40 begins with read-only control-plane preflight and records safe observations privately.

End-to-end context

Trust boundaries separate untrusted authoring/PR input, validation, preview, protected promotion, Cloudflare account/zone control, public DNS/TLS, and external acceptance. Production credentials exist only at the protected promotion boundary.

Components and sources of truth

Component/configurationResponsibility/source of truthAccountable role
content and site sourcetracked repository at exact Git commitDocumentation Maintainers
dependency/tool versions.node-version, package.json, package-lock.jsonRepository Maintainers
site configurationdocusaurus.config.ts, sidebars.ts, static/, src/, scripts/Documentation Maintainers
validation policytracked npm scripts and pinned GitHub workflowsRepository Maintainers
candidate artifactretained workflow artifact plus SHA-256 manifestBuild and Release Owner
public evidenceschema-governed Evidence Center recordsEvidence Owner
Pages project desired statetracked public-safe contract; restricted IDs/secrets outside GitDelivery Engineer
Pages effective stateCloudflare control plane, reconciled against desired stateDelivery Engineer
DNS/custom domain/TLS stateauthoritative Cloudflare zone/control planeDNS and TLS Owner
preview admissionGitHub preview environment and workflow policyPreview Approver
production admissionprotected production GitHub environmentProduction Approver
acceptance resultexternal checks plus named human usability reviewProduction Acceptance Owner
rollback decisionincident/change record and previous accepted artifactProduction Approver

Reproducible build contract

ItemExact target
repository/rootlightning-it/documentation, repository root
integration/stabledevelop / main
Node/npmNode 24.18.0 from .node-version; npm 11.x constrained by package.json
frameworkDocusaurus 3.10.2; React 19.2.7; TypeScript 6.0.3
searchPagefind 1.5.2
deployment clientWrangler 4.114.0, pinned exactly as a development dependency by #40
installnpm ci in repository root
validationnpm run validate:release
production buildnpm run build
local previewnpm run serve -- --port 3000
outputbuild/
site configdocusaurus.config.ts; url=https://docs.l-it.io, baseUrl=/
static inputsstatic/; docs and generated public sources validated before build
generated outputsdeployment marker, Pagefind under build/pagefind/, CSP hashes, license/NOTICE data

Clean checkout exit criteria: install exits zero without lockfile changes; validation/build exit zero; no tracked diff; build/ contains HTML, assets, Pagefind, _headers, _redirects, robots.txt, sitemap, 404, manifest, deployment marker, and notices; no secret or restricted sentinel; second clean build has the same semantic artifact digest.

The candidate archive is named documentation-site-<full-commit>-<sha256>.tar.gz. Canonical manifest order, normalized archive ownership/mode/time, and SHA-256 make it deterministic. The workflow retains archive, manifest, CycloneDX SBOMs, public provenance, and validation summaries under the #30 retention class. The archive itself is uploaded once and never rebuilt for promotion.

Required build inputs are tracked source, exact commit, locked toolchain, and NODE_ENV=production. Optional public inputs are BASE_URL only in explicit test harnesses and synthetic expected commit/URL values. Production build content must not depend on secrets, Cloudflare identifiers, current time, network-fetched private data, or mutable external sources.

Cloudflare Pages contract

SettingApproved target
projectlightning-it-documentation
ownerDelivery Engineer role
deployment methodDirect Upload from GitHub Actions using the retained immutable build/ artifact
repository connectionGit integration disabled for production after verified cutover; GitHub remains source
root/outputrepository root / build
install/buildperformed in GitHub with commands above; Cloudflare performs no rebuild
runtime/compatibilitystatic Pages; no Functions and no compatibility date/flags
production branch labelmain
preview branchsanitized pr-<number>-<short-commit>
preview URLdeployment-specific *.pages.dev; never treated as stable/canonical
production custom domaindocs.l-it.io
retentionpreserve current and at least two prior accepted production deployments; previews expire per approved #30 policy

Direct Upload is selected because Cloudflare Git builds would rebuild after approval and break build-once promotion. #40 pins Wrangler and uploads the already validated directory:

npm exec -- wrangler pages deploy build \
--project-name lightning-it-documentation \
--branch "<approved-branch-label>" \
--commit-hash "<full-git-oid>" \
--commit-dirty=false

The workflow records the returned deployment ID/URL privately and a safe deployment reference publicly. Account and zone identifiers remain restricted. Dashboard drift is detected by a read-only preflight comparing project name, production branch, domains, build mode, Functions absence, preview policy, and deployment identity to this contract. Drift blocks mutation and produces an owned reconciliation action.

Environment and secret inventory

NameScopeClassificationSource/storageMinimum permission/owner
CLOUDFLARE_API_TOKENpreviewrestrictedGitHub preview environmentPages deploy to named project; Delivery Engineer rotates/revokes
CLOUDFLARE_ACCOUNT_IDpreviewrestricted hereGitHub preview environmentaccount selector only; Delivery Engineer
CLOUDFLARE_API_TOKENproductionrestrictedGitHub protected production environmentPages deploy/rollback to named project only
CLOUDFLARE_ACCOUNT_IDproductionrestricted hereGitHub protected production environmentaccount selector only
EXPECTED_COMMITverificationpublicworkflow exact headnone
BASE_URLverificationpublicapproved preview/production URLnone

Preview and production tokens are distinct. Neither receives DNS, zone-setting, certificate, user, billing, Workers, KV, or unrelated-project authority. DNS and domain binding use a separate short-lived operator credential outside ordinary CI. Secrets are provisioned out of band, masked, never echoed, rotated at least annually and on owner/provider/exposure change, revoked before replacement removal, and audited through GitHub/Cloudflare logs. Break-glass use requires two-person authorization, time-bounded credential, private incident record, immediate revocation, and public-safe follow-up if appropriate.

Forks receive no environment secrets and may run public validation only. Same-repository previews require publication-safe content checks and preview environment admission. Production requires protected production environment approval by an authorized role, independent from the author by default. Under the documented single-maintainer exception, the authorized maintainer may make their own protected-environment deployment decision only after the exact revision has passed independent Copilot review, every finding and required check is resolved, and the exact documentation and artifact digests are bound to the approval evidence.

DNS and TLS contract

The authoritative l-it.io zone and zone ID remain restricted. The DNS/TLS Owner performs:

  1. verify no conflicting docs record and inventory unrelated l-it.io and portal.l-it.io records without changing them;
  2. bind docs.l-it.io as a Pages custom domain for project lightning-it-documentation;
  3. create one proxied CNAME: host docs, target lightning-it-documentation.pages.dev, TTL Auto;
  4. wait for Cloudflare domain validation and managed certificate issuance;
  5. require SSL/TLS Full (strict), minimum TLS 1.2, TLS 1.3 enabled, and Always Use HTTPS;
  6. verify canonical HTTPS host, certificate chain/SAN/expiry, IPv4 and IPv6 reachability through Cloudflare anycast, and no mixed content; and
  7. preserve existing DNSSEC and compatible CAA policy. A CAA change requires separate zone-wide impact review.

No explicit A/AAAA record is added. Cloudflare proxy supplies address families. HTTP and any Pages production alias redirect one hop to https://docs.l-it.io/<same-path-and-query> where supported. Unknown hosts are not accepted as canonical.

Ordinary rollback never changes DNS. Domain rollback removes or restores only the docs binding/record after recording its exact prior value, waits for propagation/certificate state, and re-verifies unrelated records. It is used only for domain/control-plane failure, not content rollback.

Delivery stages

StageActor/contextGate and output
localcontributorNode 24, npm ci, scoped/full validation, clean diff
pull requestuntrusted GitHub runnerformat, lint, content, types, tests, security/dependency/license
candidatetrusted read-only builderfull release validation; deterministic build/, archive/digest/SBOM/provenance
preview uploadpreview environmentexact artifact upload; isolated non-indexed URL
preview acceptanceautomation + reviewercontent identity, safe headers, UI/accessibility, limitations
approvalauthorized humanexact issue/PR/commit/document/artifact digests
stable promotionprotected PR to mainrequired checks, no artifact change, production environment approval
production uploadproduction environmentsame retained artifact upload to production branch
external acceptanceunauthenticated runner/humanDNS/TLS/routes/search/a11y/security/source revision evidence
evidence publicationEvidence Ownerreviewed public-safe records; restricted details withheld
rollbackProduction Approverprior accepted artifact redeployed; acceptance repeated

Concurrency keys are project plus environment. A newer preview cancels an older preview for the same PR. Production never cancels an in-flight upload or acceptance automatically; it serializes, times out at bounded documented limits, and requires operator disposition. Retries reuse the same artifact and are bounded. Any failed gate stops later stages; failed production upload leaves the last accepted Pages deployment serving.

Quality-gate matrix

GatePRcandidatepreviewproduction
format, lint, spelling, typeyesyesderivedderived
metadata, routes, links, anchors, navigationyesyessmokerepresentative
generated content/migrationyesyesidentityidentity
unit, site, browser, a11yyesyesyesyes
Lighthouse/performanceboundedyesyesrepresentative
secret/content safetyyesyessentinelsentinel
CodeQL/dependency/license/audityesyesderivedderived
SBOM/provenance/reproducibilitynoyesdigestdigest
approval/exact artifactnocandidatelabelrequired
DNS/TLS/headers/cache/robotsnonopreview policyfull

Edge and static behavior

static/_headers is canonical for CSP, frame protection, MIME sniffing, referrer, permissions, and cache policy; generated CSP hashes are validated. Target caching:

  • HTML, deployment marker, manifest, sitemap, robots, redirects, and search metadata: revalidate (no-cache or short bounded max-age);
  • Pagefind index shards: bounded cache with versioned integrity;
  • fingerprinted JS/CSS/images/fonts: public immutable one-year cache;
  • 404: short cache.

static/_redirects owns reviewed one-hop redirects. Docusaurus owns canonical metadata and sitemap. Production robots.txt permits intended public indexing and references the canonical sitemap. Every preview sends X-Robots-Tag: noindex, nofollow, noarchive, includes no production canonical claim, and is excluded from sitemap discovery. Custom 404 returns the provider-supported 404 status with accessible navigation.

No mandatory runtime third-party script, server function, or external search request is added. A failed upload or acceptance leaves the prior deployment serving; cache purge is scoped and follows successful acceptance only.

Preview and demonstration

Eligible same-repository PRs receive a deployment-specific URL only after classification and required validation. Forks get no deployment. A handoff contains preview URL, full source commit, artifact digest, check summary, publication classification, limitations, roadmap/issue, expiry, and the text “preview; not accepted production.”

Preview URLs are public and therefore may contain only publication-approved material. They use non-indexing headers, no sitemap registration, no stable links from production, and bounded retention. The deployment marker safely exposes full source commit and artifact digest; it excludes account, zone, credential, runner, and private deployment identifiers.

Production acceptance

An unauthenticated runner outside GitHub/Cloudflare sessions plus a named human executes:

  • DNS CNAME resolution, IPv4/IPv6 reachability, certificate/SAN/chain/expiry, TLS 1.2/1.3, HTTP-to-HTTPS and canonical-host one-hop redirects;
  • expected 200/404 behavior, representative product/cross-product routes, hashed assets, no mixed content, and no console errors;
  • deployment marker commit/artifact digest equality to approved evidence;
  • headers/CSP, cache classes, robots, sitemap canonical URLs, redirects, and custom 404;
  • Pagefind query, keyboard navigation, result focus/labels, and no restricted sentinel;
  • desktop/mobile responsive layout, zoom/reflow, skip link, landmarks, heading order, keyboard operation, visible focus, contrast, reduced motion, Mermaid accessible rendering, and representative screen-reader review; and
  • Cloudflare deployment state and previous accepted rollback candidate through authorized control-plane inspection.

Machine results and human checklist become #30 evidence with source commit, artifact/deployment safe IDs, observation time, owner, method, results, and limitations. Any mandatory failure marks acceptance failed, prevents the production claim/roadmap completion, and triggers rollback or an owned bounded remediation decision.

Rollback and recovery

Ordinary rollback:

  1. stop new production promotions;
  2. identify the immediately previous accepted artifact and evidence;
  3. obtain Production Approver authorization;
  4. redeploy that retained artifact to the production branch without rebuild or DNS change;
  5. repeat external acceptance and record new deployment/rollback evidence; and
  6. open remediation for the failed candidate while preserving its evidence.

If direct redeploy is unavailable, create a reviewed revert/promotion from the previous accepted commit and build a newly identified recovery artifact; never pretend it is the original artifact.

FailureRecovery
custom domain/DNSrestore recorded docs binding/CNAME only; verify propagation/certificate
certificatekeep Pages alias available for operator diagnosis; repair binding/CAA safely; no insecure fallback
credentialrevoke, provision least-privilege replacement, audit use, retry same artifact
artifact unavailableuse previous retained accepted artifact or reviewed recovery rebuild
Cloudflare outagestop mutation; retain evidence; serve last available deployment; escalate
bad cache/headerrollback artifact/config; scoped purge after accepted recovery

Recovery-time objectives are assumptions until a Service Owner approves an SLO: ordinary artifact rollback target 30 minutes; credential/domain recovery target 4 hours; provider-wide outage follows provider recovery. These are planning targets, not public availability commitments.

Implementation command and configuration matrix

Actor/systemLocation/control planeCommand or settingNon-secret inputSecret referenceExpected/verificationFailure/rollbackEvidence
builderclean repository rootnode --version; npm --versionNode 24.18.0/npm 11noneexact supported versionsstoptoolchain result
builderrepository rootnpm cilockfilenonezero exit, no diffdiscard workspaceinstall/lock digest
validatorrepository rootnpm run validate:releasesource commitnoneall gates passstopcheck/run IDs
builderrepository rootnpm run buildvalidated sourcenonecomplete build/stopbuild manifest
builderrepository rootdeterministic archive/digest command implemented by #40build/, commitnonearchive, SHA-256, reproducible matchstopartifact/digest
preview deployerGitHub preview environmentWrangler Pages deploy commandproject, preview branch, commitpreview token/accountdeployment-specific URL and matching commitkeep prior preview; stopsafe deployment record
verifierpublic networknpm run test:previewpreview URL/commitnonepreview checks and noindex passdelete/expire previewpreview acceptance
approverGitHub/approval recordexact-digest approvalissue/PR/commit/artifact/docsnoneauthorized immutable recordno promotionapproval evidence
promoterprotected main PRGitHub mergeaccepted commit/artifactnonerequired checks and branch ancestrystopmerge commit
production deployerGitHub production environmentWrangler Pages deploy commandproject, main, commitproduction token/accountproduction deployment references same digestredeploy previous accepted artifactdeployment evidence
DNS/TLS ownerCloudflare zonePages custom domain + proxied CNAMEdocs, Pages target, Auto TTLshort-lived DNS credentialactive domain/cert, unrelated diff emptyrestore exact prior docs stateprivate change + safe summary
verifierpublic networkwait:production, test:production, browser/TLS/DNS checkscanonical URL/commitnoneall acceptance gates passrollbackacceptance evidence
rollback operatorprotected productionredeploy retained previous artifactprior accepted digestproduction token/accountprior digest serving and acceptedescalate recoveryrollback evidence

Issue #40 must implement missing deterministic archive/upload orchestration and exact read-only drift checks using reviewed scripts rather than ad hoc shell logic. Commands receive explicit validated values; unresolved variables, wildcards, secret output, and broad control-plane mutation are prohibited.

Decisions, risks, and #40 handoff

Accepted target decisions:

  • Direct Upload, not Cloudflare Git rebuild, enforces immutable promotion.
  • develop integrates; protected main represents stable production intent.
  • preview and production use distinct environments/tokens and admission rules.
  • ordinary rollback redeploys an accepted artifact without DNS change.
  • the canonical host is HTTPS docs.l-it.io; Pages aliases are non-canonical.
  • current target project is lightning-it-documentation; restricted IDs stay outside public artifacts.

Required implementation ADRs record Direct Upload/cutover, environment separation, artifact canonicalization, DNS/custom-domain state, cache/header policy, and rollback/SLO assumptions. They may not reverse the accepted architecture without returning to the architecture gate.

Known risks are dashboard drift, Git-integration double deployment, overly broad tokens, Cloudflare upload retention, DNS/CAA interaction, nondeterministic archives, stale previews, and provider outage. Preflight, direct-upload cutover, least privilege, retained accepted artifacts, exact DNS diff, reproducibility, expiry, and recovery controls address them.

Issue #40 is implementation-ready only after #38 approval and must trace each change to #15, #35, and #40; record PR/commit/check/artifact/deployment/acceptance evidence; and leave no open architecture choice. Restricted account, zone, token, audit, and control-plane observations stay private.