Shape: Static Site

A static site (Hugo, Jekyll, Astro, Eleventy, Next.js static export, etc.) ships as a directory of built HTML / CSS / JS to a static-hosting provider -- Cloudflare Pages, Netlify, Vercel, S3 + CloudFront, GitHub Pages. The release surface is the provider's deployment API; there is no long-running server to deploy.

Why distribution-only, not deployable

Static-site is modeled as a distribution-only environment kind, not as a deployable sub-kind. The rationale: although the deployment goes to a hosted URL (which feels deployable), the workflow shape matches publish -- you push an artifact, the provider serves it, there is no server-side state, no rollback orchestration, no smoke endpoints to verify against. /gaia-publish is the right command; /gaia-deploy would HALT (and rightly so).

Pairing a static-site channel with kind: deployable is rejected by /gaia-config-validate by the cross-section coupling enforcement.

project-config.yaml

environments:
  - id: cdn
    kind: distribution-only

distribution:
  channel: static-site
  registry: https://example.com
  manifest: site.config.json
  release_workflow: gaia-release.yml
  provider: cloudflare-pages
  domain: example.com

The static-site channel requires provider and domain as per-channel sub-fields. Missing either is rejected by /gaia-config-validate.

Provider matrix

  • cloudflare-pages -- git-integrated build + edge deploy; the deploy is triggered by the push, no separate run.sh needed.
  • netlify -- git-integrated build + CDN; the release_workflow handles the netlify-cli invocation.
  • vercel -- git-integrated; can be set to deploy on every commit or only on tag.
  • s3 -- the workflow runs aws s3 sync + aws cloudfront create-invalidation. Credentials via OIDC role.
  • github-pages -- the workflow uses actions/deploy-pages. Fastest path for project documentation.
  • custom -- point at a custom adapter for any other provider (Surge, Render, Fly.io static, etc.).

Publish to your provider

/gaia-publish

The orchestrator dispatches publish-static-site, which invokes the configured release_workflow. The workflow reads provider + domain from the config and runs the appropriate publish command:

  • cloudflare-pages: triggered by git push to the connected branch.
  • netlify: netlify deploy --prod --dir=dist.
  • s3: aws s3 sync dist/ s3://${DOMAIN_BUCKET} --delete + invalidate the matching CloudFront distribution.
  • github-pages: actions/upload-pages-artifact + actions/deploy-pages.

You'll know it worked when

  • /gaia-config-validate exits 0 with provider + domain both present and the env kind: distribution-only (NOT deployable).
  • /gaia-help suggests /gaia-publish on the Phase 5 row.
  • /gaia-deploy --env cdn HALTs with the canonical use /gaia-publish instead guidance.
  • A /gaia-publish run completes; curl https://${DOMAIN}/ returns the new build's content.