Skip to content

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

Open
agent-relay-code[bot] wants to merge 3 commits into
mainfrom
factory/142-agentworkforce-factory-5a9a545c
Open

142: EnvironmentProvider port + Cloudflare ephemeral-environment provider (Workers for Platforms + Containers); K8s/Daytona optional#194
agent-relay-code[bot] wants to merge 3 commits into
mainfrom
factory/142-agentworkforce-factory-5a9a545c

Conversation

@agent-relay-code

@agent-relay-code agent-relay-code Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

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

Fixes #142


Summary by cubic

Adds a Cloudflare-backed EnvironmentProvider for ephemeral verification environments (Workers for Platforms + Containers), with endpoint resolution and a full provision → ready → destroy → reap lifecycle. Fixes #142.

  • New Features

    • CloudflareEnvironmentProvider with provision, status, endpoints, and destroy; per-environment bindings/secrets, Workers for Platforms dispatch namespace, Container ingress; idempotent teardown and a TTL reaper for past-TTL/orphaned envs.
    • CloudflareEnvironmentConfigSchema (Resource.*-only credentials, defaults/limits); wired into FactoryConfigSchema.environments.cloudflare; provider, types, and constants exported from src/environments/index.ts and main index.
    • Tests: fake Cloudflare API, Miniflare runtime, and real E2E at test/e2e/environment-provider.e2e.ts; new workflow cloudflare-environment-provider.yml; run via npm run test:e2e:cloudflare with CLOUDFLARE_API_TOKEN and optional CLOUDFLARE_ACCOUNT_ID.
  • Dependencies

    • Added wrangler@4.113.0, @cloudflare/containers@0.3.7, miniflare@4.20260721.0; Node engine set to >=22 and CI updated to Node 22; overrides include undici@7.28.0 and esbuild@0.28.1.

Written for commit 987427a. Summary will update on new commits.

Review in cubic

@agent-relay-code

Copy link
Copy Markdown
Contributor Author

Verdict: Clean. Preserves the EnvironmentProvider port contract, verifies owned metadata identity before every destroy/reap, fails closed on ambiguous owner/namespace state, and registers all three features with executable tier-1 and tier-5 procedures.

maintainability review · .agentworkforce/workforce/personas/maintainability.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant