Skip to content

EnvironmentProvider port + Cloudflare ephemeral-environment provider (Workers for Platforms + Containers); K8s/Daytona optional #142

Description

@khaliqgant

Part of Epic: Factory Verification Environments (#141). This is the core provisioning primitive.

Why

The Factory needs to create and destroy isolated environments on demand to verify features. Follow the repo's existing port + provider seam (src/ports/fleet.ts with RelayFleetClient/InternalFleetClient implementations) so the environment substrate is swappable and testable. The primary provider is Cloudflare (the org's substrate — see #146); K8s/Daytona are optional providers behind the same port for stacks that can't run on Cloudflare.

What

Add an EnvironmentProvider port (src/ports/environment.ts) and a Cloudflare implementation (src/environments/cloudflare-provider.ts):

  • provision(spec): Promise<Environment> — create a Workers for Platforms dispatch namespace for the environment, wire per-env bindings/secrets, and (per the stack descriptor Verification-stack descriptor contract + deployer: repos declare their full stack, Factory brings it up #143) deploy the environment's Workers and Cloudflare Containers into it.
  • Environment carries { id, dispatchNamespace, endpoints, bindings, status, createdAt, ttl }.
  • status(id) — readiness of the namespace's Workers/Containers.
  • endpoints(id) — resolve the environment's reachable URLs (Worker preview URL / Container ingress) so callers (E2E driver, load harness) can hit them.
  • destroy(id) — delete the dispatch namespace + Container instances; idempotent.
  • TTL reaper — mirror src/orchestrator/reaper.ts (identity-checked) to delete environments past TTL or whose owning run died, so namespaces/Containers can't leak.
  • Config schema entry (mirror src/config/schema.ts) for the Cloudflare account/API-token (read via a Resource.*-style secret, not process.env), default limits, TTL.
  • Keep the Cloudflare API/wrangler dependency isolated behind the provider (the port stays substrate-agnostic). Leave a seam for an optional KubernetesEnvironmentProvider / DaytonaEnvironmentProvider implementing the same port later.
  • Export from src/index.ts.

Acceptance

  • EnvironmentProvider port with a Cloudflare provider; unit tests with a faked Cloudflare API/miniflare.
  • Provision creates a dispatch namespace + per-env bindings; destroy is idempotent (namespace + Containers gone).
  • Reaper deletes past-TTL / orphaned environments.
  • Exported from src/index.ts; config schema covers account/token + limits + TTL; port leaves room for K8s/Daytona providers.

✅ End-to-end verification — Factory success criteria (REQUIRED)

"Compiles" is not done. Run the provider against a REAL Cloudflare dispatch namespace and observe an environment come up, be reachable, and get reaped. CI uses miniflare for the fast path and a real dispatch namespace for the E2E.

Run it

npm run build
npm test                                   # unit (faked Cloudflare API / miniflare)

# E2E against a real dispatch namespace (creds from factory-test-infra)
CLOUDFLARE_API_TOKEN=... npx tsx test/e2e/environment-provider.e2e.ts

The E2E must (against real Cloudflare):

  1. provision() → assert the dispatch namespace exists, per-env bindings are set, and status() reports ready.
  2. Deploy a trivial Worker (or Container) into it and assert endpoints() resolves to a reachable URL (curl returns 200).
  3. destroy() → assert the namespace + Container instances are gone.
  4. Provision with a short TTL, run the reaper, assert the environment is deleted without an explicit destroy (proves leaks are cleaned).

Deliverable

  • test/e2e/environment-provider.e2e.ts running provision→reachable→destroy→reap against a real dispatch namespace in CI, one command.
  • Fails if provisioning, endpoint resolution, or reaping is broken (no false green).

Anchor files

  • src/ports/fleet.ts (port pattern) → new src/ports/environment.ts
  • src/fleet/relay-fleet-client.ts (provider-impl pattern) → new src/environments/cloudflare-provider.ts (+ later optional kubernetes-provider.ts / daytona-provider.ts)
  • src/orchestrator/reaper.ts (reaper pattern), src/config/schema.ts, src/index.ts
  • Cloudflare/miniflare posture precedent: relaycast-cloud, cloud's miniflare-sage + test:integration

Metadata

Metadata

Assignees

No one assigned

    Labels

    factory-readyApproved for the Factory to pick up and implementfactory:in-progressFactory agents are working on this issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions