Skip to content

Deploy

  • @lemn-ltd/brand-contract, @lemn-ltd/ui, @lemn-ltd/brand-runtime, and @lemn-ltd/brand-studio publish to GitHub Packages in dependency order.
  • ui.le-mn.com serves this Starlight documentation.
  • portal.ui.le-mn.com serves the public Catalog and Access-protected Admin from Worker lemn-ui-portal.
  • schemas.ui.le-mn.com serves the public schema contract from that same Worker.

Package identity, source commit, generated provenance/notices/SBOM, and deployed build identity must all refer to the same approved release.

Before any mutation:

Terminal window
pnpm validate
pnpm check
pnpm test
pnpm build
pnpm audit --prod
pnpm changeset status

The release workflow must also prove:

  • exact provider registry pins and one active implementation per capability;
  • compatible package dependency order and clean GitHub Packages installs;
  • provider, brand contract, SSR, accessibility, interaction, visual, and bundle conformance applicable to the change;
  • Cloudflare configuration dry-runs and least-privilege credential preflight;
  • public/protected route behavior, Access origin validation, protected Admin assets, and deployed build identity;
  • rollback or recovery metadata before shifting production traffic.

Do not represent an earlier commit’s checks as evidence for the release SHA.

GitHub Actions supplies its job token for package publication. Cloudflare credentials are resolved only inside the protected production environment and must be scoped to Worker lemn-ui-portal, the owned domains, Access resources, and the exact required actions. Production environment inputs use UI_PORTAL naming. Never place tokens, account keys, auth headers, or secret values in repository files, command arguments, build artifacts, logs, or documentation.

The exact protected Environment inputs are PRODUCTION_CLOUDFLARE_API_TOKEN, PRODUCTION_UI_PORTAL_ACCESS_CLIENT_ID, and PRODUCTION_UI_PORTAL_ACCESS_CLIENT_SECRET secrets, plus the protected PRODUCTION_CLOUDFLARE_ACCOUNT_ID, PRODUCTION_UI_PORTAL_ACCESS_AUDIENCE, and PRODUCTION_UI_PORTAL_HEALTH_ACCESS_AUDIENCE variables. The account ID is an exact 32-character lowercase hexadecimal value. Each audience is an exact 64-character lowercase hexadecimal value, the two values must differ, and release automation injects them into the candidate without logging them or storing them in Wrangler config. The Cloudflare token needs only Account Lemn DEV / Workers Scripts: Edit and Zone le-mn.com / Zone: Read plus Workers Routes: Edit. The Access pair belongs to the fresh health-only service identity and is sent only to /health/deep by the release smoke; it is never used for an Admin business capability.

The Catalog, minimal /health, and non-cacheable /release.json receipt are anonymous and read-only. The receipt exposes only the public package name and immutable build identity required to verify a candidate; it contains no operational diagnostics. The edge protects /admin, /admin/*, /api/admin/*, and Admin-only assets with Cloudflare Access. The Worker cryptographically verifies the Access assertion at origin. Service identity is least privilege and may call only /health/deep.

Release smoke reads the project Zero Trust manifest as its posture authority. When Access is enabled it requires the health service assertion and a 200 from /health/deep. When Access is disabled by that manifest it instead requires origin 401/403 for both Admin and deep health—even when service-token headers are present—and verifies the exact candidate through /release.json. It never opens a protected origin route merely to complete a deployment.

  1. Prepare or resume the immutable release commit, verify that exact checked-out SHA, run a frozen install, and rerun pnpm validate, pnpm check, pnpm test, and pnpm build on it.
  2. Publish Brand Contract, UI, Brand Runtime, then Brand Studio; an existing version must have identical immutable integrity or the release stops.
  3. Install the exact published packages in clean external consumers and run their checks/builds.
  4. Upload an inactive UI Portal candidate for that release identity, then smoke public Catalog routes and machine endpoints, unauthenticated Admin denial, authenticated Admin behavior, protected assets, System branding, blocks, provider read models, schema v1, and build identity.
  5. Activate that exact Portal candidate transactionally and repeat public and protected smoke checks; a failure restores and re-verifies the captured Portal baseline.
  6. Deploy Docs last with the same immutable release identity.
  7. Record the release receipt only after every required check passes.

Use staged/traffic-safe Worker versions where the deployment workflow supports them. Any observed concurrent deployment, identity mismatch, integrity mismatch, or failed smoke stops the rollout rather than overwriting external state.

For the UI Portal, capture the baseline with wrangler deployments list --json, upload an inactive candidate, and verify protected health through the scoped Cloudflare Access service identity before activation. A failure after activation runs wrangler rollback <captured-version-id> --yes and re-verifies the baseline. Credentials are Environment-scoped only. There is one production Worker and no deployed development or staging Worker.

If the final Docs deployment fails after the Portal has activated, rerun the same protected-main workflow instead of creating a new release. Release preparation resumes the already-pushed release commit, the exact-SHA frozen install and full gates run again, published packages are accepted only after immutable-integrity verification, and the Portal rollout revalidates the already-owned release before Docs is deployed again. Re-running never rolls the Portal back merely because the final Docs mutation failed, and it never publishes a sibling version for the same trigger SHA.

If an interrupted first bootstrap leaves a predecessor candidate on the target Worker, the next protected-main release settles it before starting its own upgrade. Automatic recovery accepts only one self-consistent predecessor: an exact active version at 100% traffic, or one orphan candidate while the Worker has no deployment. Its deployment message when present, candidate metadata, tag, release ID, Access-audience hash, historical Wrangler fingerprints, and Git ancestry must agree. It reuses the protected Environment credentials, does not rebuild or upload the predecessor, resumes an exact release-owned per-hostname transition, and refuses ambiguous absence or concurrent domain drift. After the predecessor passes candidate and active smokes, the same invocation continues with the current release.

Retired domains and route families receive no redirects or tombstone Workers. Negative verification must prove they no longer resolve to an active Lemn UI deployment.

Capture without secrets:

  • package names, exact versions, and registry integrity;
  • Git commit and deployment/version identifiers;
  • domain mappings and Access boundary results;
  • public and protected smoke status;
  • SSR first-response evidence showing critical scoped CSS, scope attributes, signed projection hash, and matching mode identity before hydration;
  • rollback/fallback verification and any remaining explicit exception.

This runbook applies PAT-UI-PROVIDER-FIRST-001, PAT-UI-SSR-BRANDING-001, PAT-SEC-SECRETS-001, PAT-CLOUDFLARE-WRANGLER-CONFIG-001, PAT-OPS-LEAST-PRIVILEGE-001, and PAT-TEST-EVIDENCE-001.