[FEATURE] heph: podman, podman-kube + process backends #5

Merged
devops merged 1 commit from ep/Jul23-2026/BackendsWave2 into main 2026-07-23 21:13:26 +00:00
Owner

Summary

Extends the pluggable-backend seam (Strategy + registry) from 2 kinds to 5: k8s | compose | podman | podman-kube | process. up.ts and the verb dispatch stay backend-agnostic — each new kind is one registry entry + a validate hook.

Backends

  • podman — engine-parameterize the compose backend into makeComposeBackend(engine, kind). ComposeBackend = docker engine (behavior byte-identical); PodmanBackend = podman engine, project root ~/.heph/podman-compose/. Two-step availability check (podman + compose provider).
  • podman-kube — pure k8s-YAML render run via podman kube play (no cluster/operator/CRDs). Play order is the dependency gate: infra → jobs (waited) → app group. Caddy Deployment w/ hostPort for routing (kube play ignores Ingress); pod-name aardvark DNS parity; DOPPLER_TOKEN injected only in-memory via stdin play (on-disk YAML token-free).
  • process — bare host processes via process-compose. New per-service schema block process: {command, cwd?}. Source services run from the host toolchain (no image build); infra runs via docker run --network host. Per-project unix socket, host-port pool from :18080, host doppler run wrap, Caddy proxy. Fastest inner loop.

Tooling

  • Vendor process-compose v1.120.0 into the Garden-style tools registry (auto-download to ~/.heph/tools/, sha256-verified). ensureToolInteractive gates on an opt-in TTY prompt; non-interactive throws a heph doctor --install-tools hint (never silently downloads).
  • Container engines (docker/podman) are detect-and-guide in heph doctor (enginePrompt), never auto-installed.
  • Shared R5 floor assertComposeSupported(eff, kind) parameterized so rejects name the actually-selected backend.

Tests

Per-backend render/validate suites + backend-swap parity (selection, validate consistency, render parity, CR-vs-local parity, project-dir isolation), backend-contract completeness (all 5 kinds implement the full Backend type), up.ts step-order (isolated child process), IO round-trips incl. the token-never-on-disk guarantee, process-compose registry + engine-prompt. heph 750 tests (690 pass / 60 opt-in skip / 0 fail), operator 147 / 0 fail, tsc + biome clean.

Self-review fixes (adversarial review)

  • kube (major): waitContainersReady counted pod-infra/init-wait helpers as satisfying "ready", returning before app containers started. Now gates on the count of long-running Deployments actually written (drift-proof).
  • process: reserve author-pinned PORTs so a pin inside the pool can't collide; heph owns PORT for non-pinned source services (override to assigned port so app-bind == heph-route); doppler-wrap a source command under sh -c so compound commands stay inside the injected env, with shq-quoted project/config.

Caveats

  • Backends are unit/parity-tested only (podman + process-compose not installed in CI) — no live integration run.
  • Deferred (docs/27): process backend has no Deployment→service manifest translation; kube single-container translate only.

🤖 Generated with Claude Code

## Summary Extends the pluggable-backend seam (Strategy + registry) from **2 kinds to 5**: `k8s | compose | podman | podman-kube | process`. `up.ts` and the verb dispatch stay backend-agnostic — each new kind is one registry entry + a validate hook. ### Backends - **podman** — engine-parameterize the compose backend into `makeComposeBackend(engine, kind)`. `ComposeBackend` = docker engine (behavior byte-identical); `PodmanBackend` = podman engine, project root `~/.heph/podman-compose/`. Two-step availability check (podman + compose provider). - **podman-kube** — pure k8s-YAML render run via `podman kube play` (no cluster/operator/CRDs). Play order is the dependency gate: infra → jobs (waited) → app group. Caddy Deployment w/ hostPort for routing (kube play ignores Ingress); pod-name aardvark DNS parity; `DOPPLER_TOKEN` injected only in-memory via stdin play (on-disk YAML token-free). - **process** — bare host processes via process-compose. New per-service schema block `process: {command, cwd?}`. Source services run from the host toolchain (no image build); infra runs via `docker run --network host`. Per-project unix socket, host-port pool from :18080, host `doppler run` wrap, Caddy proxy. Fastest inner loop. ### Tooling - Vendor **process-compose v1.120.0** into the Garden-style tools registry (auto-download to `~/.heph/tools/`, sha256-verified). `ensureToolInteractive` gates on an opt-in TTY prompt; non-interactive throws a `heph doctor --install-tools` hint (never silently downloads). - Container engines (docker/podman) are **detect-and-guide** in `heph doctor` (`enginePrompt`), never auto-installed. - Shared R5 floor `assertComposeSupported(eff, kind)` parameterized so rejects name the actually-selected backend. ### Tests Per-backend render/validate suites + **backend-swap parity** (selection, validate consistency, render parity, CR-vs-local parity, project-dir isolation), backend-contract completeness (all 5 kinds implement the full `Backend` type), up.ts step-order (isolated child process), IO round-trips incl. the token-never-on-disk guarantee, process-compose registry + engine-prompt. **heph 750 tests (690 pass / 60 opt-in skip / 0 fail), operator 147 / 0 fail**, tsc + biome clean. ### Self-review fixes (adversarial review) - **kube (major):** `waitContainersReady` counted pod-infra/init-wait helpers as satisfying "ready", returning before app containers started. Now gates on the count of long-running Deployments actually written (drift-proof). - **process:** reserve author-pinned PORTs so a pin inside the pool can't collide; heph owns PORT for non-pinned source services (override to assigned port so app-bind == heph-route); doppler-wrap a source command under `sh -c` so compound commands stay inside the injected env, with shq-quoted project/config. ### Caveats - Backends are unit/parity-tested only (podman + process-compose not installed in CI) — no live integration run. - Deferred (docs/27): process backend has no Deployment→service manifest translation; kube single-container translate only. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
[FEATURE] heph: podman, podman-kube + process backends
Some checks failed
ci / check (pull_request) Has been cancelled
integration / integration (pull_request) Has been cancelled
81241aac98
Extend the pluggable-backend seam (Strategy + registry) from 2 kinds to 5:
k8s | compose | podman | podman-kube | process. up.ts and the verb dispatch
stay backend-agnostic — each new kind is one registry entry + a validate hook.

- podman: engine-parameterize the compose backend into
  makeComposeBackend(engine, kind). ComposeBackend = docker engine (behavior
  byte-identical); PodmanBackend = podman engine, project root
  ~/.heph/podman-compose/. Two-step availability check (podman + compose
  provider).
- podman-kube: pure k8s-YAML render (heph/src/backend/kube/) run via
  `podman kube play` — no cluster, no operator, no CRDs. Play order is the
  dependency gate: infra -> jobs (waited) -> app group. Caddy Deployment with
  hostPort for routing (kube play ignores Ingress); pod-name aardvark DNS
  parity; DOPPLER_TOKEN injected only in-memory via stdin play (on-disk YAML
  token-free).
- process: bare host processes via process-compose (heph/src/backend/process/).
  New per-service schema block `process: {command, cwd?}`. Source services run
  from the host toolchain (no image build); infra runs via `docker run
  --network host`. Per-project unix socket, host-port pool from :18080, host
  `doppler run` wrap, Caddy proxy. Fastest inner loop.
- tools: vendor process-compose (v1.120.0) into the Garden-style tools registry
  (auto-download to ~/.heph/tools/, sha256-verified). ensureToolInteractive
  gates on an opt-in TTY prompt; non-interactive throws a
  `heph doctor --install-tools` hint (never silently downloads). Container
  engines (docker/podman) are detect-and-guide in `heph doctor` (enginePrompt),
  never auto-installed.
- shared R5 floor: assertComposeSupported(eff, kind) parameterized so rejects
  name the actually-selected backend.

Tests: per-backend render/validate suites + backend-swap parity (selection,
validate consistency, render parity, CR-vs-local parity, project-dir
isolation), backend-contract completeness (all 5 kinds implement the full
Backend type), up.ts step-order (isolated child process), IO round-trips incl.
the token-never-on-disk guarantee, process-compose registry + engine-prompt.

Self-review fixes (from adversarial review):
- kube: waitContainersReady counted the pod-infra/init-wait helpers as
  satisfying "ready", returning before app containers started. Now gates on the
  count of long-running Deployments actually written (drift-proof).
- process: reserve author-pinned PORTs so a pin inside the pool can't collide;
  heph owns PORT for non-pinned source services (override to the assigned port
  so app-bind == heph-route); doppler-wrap a source command under `sh -c` so
  compound commands (`cd x && bun dev`) stay inside the injected env, with
  shq-quoted project/config.

Backends are unit/parity-tested only (podman + process-compose not installed in
CI); no live integration run. Deferred (docs/27): process backend has no
Deployment->service manifest translation; kube single-container translate only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
devops merged commit b7e5bdffd4 into main 2026-07-23 21:13:26 +00:00
devops deleted branch ep/Jul23-2026/BackendsWave2 2026-07-23 21:13:58 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
dunemask/hephaestus!5
No description provided.