Deploy
Release surfaces
Section titled “Release surfaces”@lemn-ltd/brand-contract,@lemn-ltd/ui,@lemn-ltd/brand-runtime, and@lemn-ltd/brand-studiopublish to GitHub Packages in dependency order.ui.le-mn.comserves this Starlight documentation.portal.ui.le-mn.comserves the public Catalog and Access-protected Admin from Workerlemn-ui-portal.schemas.ui.le-mn.comserves 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.
Required gates
Section titled “Required gates”Before any mutation:
pnpm validatepnpm checkpnpm testpnpm buildpnpm audit --prodpnpm changeset statusThe 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.
Authentication and authorization
Section titled “Authentication and authorization”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.
Deployment sequence
Section titled “Deployment sequence”- 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, andpnpm buildon it. - Publish Brand Contract, UI, Brand Runtime, then Brand Studio; an existing version must have identical immutable integrity or the release stops.
- Install the exact published packages in clean external consumers and run their checks/builds.
- 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.
- Activate that exact Portal candidate transactionally and repeat public and protected smoke checks; a failure restores and re-verifies the captured Portal baseline.
- Deploy Docs last with the same immutable release identity.
- 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.
Post-deploy evidence
Section titled “Post-deploy evidence”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.