openova/.github/workflows/cloudflare-worker-leases-build.yaml
e3mrah 7ca4abddd2
feat(continuum): K-Cont-4 — Cloudflare Worker source + tofu wiring for lease witness (#1101) (#1159)
* feat(continuum): K-Cont-4 — Cloudflare Worker source + tofu wiring for lease witness (#1101)

Implements the server side of the Cloudflare KV lease-witness pattern
that K-Cont-3's CFKVClient (in core/controllers/continuum/internal/
witness/cloudflarekv/) speaks to. The Worker fronts a Cloudflare
Workers KV namespace with read-then-CAS-write semantics enforced via
the If-Match header — exact contract per K-Cont-3 #1158 report (item d)
and the canonical-seams "Cloudflare KV Worker contract" entry.

Routes:
  GET    /lease/<slot-url-encoded>  → 200 + LeaseState | 404 | 401
  PUT    /lease/<slot>              → 200 + LeaseState | 412 + state | 401
  DELETE /lease/<slot>              → 204 | 412 | 401

All 7 K-Cont-3 trap behaviors verified by 46 vitest tests:
  1. If-Match: 0 = first-acquire-on-empty-slot
  2. Generation increments unconditionally (incl. Release)
  3. 412 includes current state body
  4. TTL eviction is server-authoritative in stamping (Worker doesn't
     auto-evict — controller's IsHeldBy decides)
  5. X-Holder mismatch on DELETE returns 412 (stale region can't
     evict new primary)
  6. Bearer token validation against env-bound allow-list
  7. Optional X-Lease-Slot header logged for KV granularity

Files:
  products/continuum/cloudflare-worker/{package.json, tsconfig.json,
    wrangler.toml, vitest.config.ts, .eslintrc.cjs, .gitignore,
    DESIGN.md, src/{index,auth,kv,types}.ts,
    src/handlers/{get,put,delete}.ts,
    test/{handlers,contract,env.d}.ts}
  infra/cloudflare-worker-leases/{versions,variables,main,outputs}.tf
    + README.md
  .github/workflows/cloudflare-worker-leases-build.yaml
    (event-driven, NO cron — push-on-paths + PR + workflow_dispatch)

Tests: 46/46 vitest pass (handlers 37 + contract 9). ESLint clean.
tsc --noEmit clean. wrangler deploy --dry-run produces 9.47 KiB
bundle.

Per the brief: tofu module ships ready for operator action — no
auto-deploy. Operator runbook in DESIGN.md §"Operator runbook —
deploy a new Sovereign".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(continuum/cf-worker-tofu): K-Cont-4 — adopt CF v5 inline secret_text binding (was v4 separate resource)

`tofu validate` failed on `cloudflare_workers_secret` — that resource
was REMOVED in cloudflare/cloudflare v5 (it consolidated into the
inline `bindings = [...]` array on `cloudflare_workers_script` with
`type = "secret_text"`). Same security guarantee — encrypted at rest
in CF, never visible via dashboard read API once written. `tofu fmt`
also wanted versions.tf alignment + the .terraform.lock.hcl pinning
the resolved cloudflare/cloudflare v5.19.1 (mirrors infra/hetzner/
which commits its lock file).

Per Inviolable Principle #5 the bearer token value still flows from
TF_VAR_bearer_tokens_csv extracted at apply time from a K8s
SealedSecret — never inlined here.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: hatiyildiz <hati.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 08:01:44 +04:00

103 lines
3.5 KiB
YAML

name: cloudflare-worker-leases — build + test + lint
# Slice K-Cont-4 of EPIC-6 (#1101). Verifies the OpenOva Continuum
# lease-witness Worker source at `products/continuum/cloudflare-worker/`
# and the OpenTofu module at `infra/cloudflare-worker-leases/`.
#
# Per CLAUDE.md "every workflow MUST be event-driven, NEVER scheduled":
# this workflow is push-on-merge + pull-request-on-touch + manual
# dispatch. NO cron triggers.
#
# This workflow does NOT auto-deploy the Worker. Per the K-Cont-4 brief
# "DO NOT auto-deploy — operator manually runs tofu apply for the lease
# witness deploy". The `wrangler deploy --dry-run` step verifies the
# Worker compiles + bundles correctly without writing to Cloudflare.
on:
push:
branches: [main]
paths:
- 'products/continuum/cloudflare-worker/**'
- 'infra/cloudflare-worker-leases/**'
- '.github/workflows/cloudflare-worker-leases-build.yaml'
pull_request:
paths:
- 'products/continuum/cloudflare-worker/**'
- 'infra/cloudflare-worker-leases/**'
- '.github/workflows/cloudflare-worker-leases-build.yaml'
workflow_dispatch:
jobs:
worker-test:
name: Worker — npm ci + test + lint + build:dryrun
runs-on: ubuntu-latest
timeout-minutes: 10
defaults:
run:
working-directory: products/continuum/cloudflare-worker
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
# Node 20 is the LTS that matches @cloudflare/workers-types
# 4.20240909+ tooling. Pin minor to keep CI deterministic.
node-version: '20'
cache: 'npm'
cache-dependency-path: products/continuum/cloudflare-worker/package-lock.json
- name: npm ci (clean install from lockfile)
run: npm ci
- name: ESLint
run: npm run lint
- name: TypeScript typecheck
run: npm run typecheck
- name: Vitest — handler + contract suites
# @cloudflare/vitest-pool-workers spawns a per-test workerd
# runtime with in-memory KV. No network, no CF account needed.
run: npm test
- name: Wrangler build dry-run
# `wrangler deploy --dry-run --outdir=dist` compiles + bundles
# the Worker WITHOUT pushing to Cloudflare. Catches syntax
# errors, missing imports, oversized bundles. The `dist/`
# output is what `infra/cloudflare-worker-leases/main.tf`
# reads as the script content at apply time.
run: npm run build:dryrun
tofu-validate:
name: OpenTofu — fmt + validate
runs-on: ubuntu-latest
timeout-minutes: 5
defaults:
run:
working-directory: infra/cloudflare-worker-leases
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install OpenTofu
uses: opentofu/setup-opentofu@v1
with:
# Match infra/hetzner/'s pin (see infra/hetzner/.github/workflows/
# infra-hetzner-tofu.yaml). Bump in lockstep.
tofu_version: 1.8.5
- name: tofu init (backend=false — module-local checks only)
run: tofu init -backend=false
- name: tofu fmt -check
run: tofu fmt -check -recursive
- name: tofu validate
# `validate` requires `init` to have downloaded the cloudflare
# provider plugin (above). Validates HCL syntax + provider
# schema conformance — won't catch runtime issues like a wrong
# account_id but catches every authoring error.
run: tofu validate