## Why Several integration scenarios on genesis need real HTTPS, not `rejectUnauthorized: false` shortcuts: WebRTC transports (`drive-webrtc`, `transport-webrtc`) refuse non-secure contexts; HTTPS edges (`server-caddy-imgproxy`, future Caddy/nginx fixtures); SMTPS/IMAPS variants of `testing-integration-mailpit`; the HTTPS path of `testing-integration-garage-s3`. Today every consumer either rolls its own self-signed certs (and a per-suite trust dance) or skips the secure variant entirely. We already have a working `step-ca` deployment at `../certs/` (PostLocker CA: `smallstep/step-ca` + Caddy/LE + a Hono API + a download portal). The CA core is reusable for tests; the public-ops layers (Caddy/LE, REST API, SQLite key DB, ACME, domain-policy) are not. This change ports the CA nucleus into the existing `testing-integration-*` plugin family so any fixture can opt into a real PKI chain with one line. ## What Changes ### New plugin package — `@suaveplan/testing-integration-step-ca` - Lives at `packages/dev/testing-integration-step-ca` (mirrors the `testing-integration-*` plugin convention from `CLAUDE.md`). - Implements `ServiceFixture` from `@suaveplan/testing-integration` with image `smallstep/step-ca` pinned to a digest. - `StepCaConfig` is `{ caUrl: string; rootCertPem: string; rootCertPath: string; fingerprint: string; provisionerName: string; provisionerPassword: string }`. - Env-bridge passthrough mode (5 vars, all required): `STEPCA_URL`, `STEPCA_ROOT_CERT_PEM`, `STEPCA_ROOT_CERT_PATH`, `STEPCA_FINGERPRINT`, `STEPCA_PROVISIONER_PASSWORD`. Partial set (4 of 5) falls through to container mode. Passthrough `stop()` is a no-op. - Container mode: starts step-ca, runs an init+configure sequence equivalent to `../certs/scripts/init-ca.sh` + `configure-ca.sh` but trimmed to the parts a test harness needs: 1. `step ca init --deployment-type standalone --name --dns --address :8443 --provisioner --password-file ` 2. read `/home/step/certs/root_ca.crt` → `rootCertPem` 3. `step certificate fingerprint /home/step/certs/root_ca.crt` → `fingerprint` 4. `step crypto jwk create` (EC P-256, ES256, sig) for the JWK provisioner, password-encrypted 5. `jq` insert the JWK provisioner into `ca.json` 6. write `rootCertPem` to a host tmpfile and expose its path as `rootCertPath` - Health: `GET https://localhost:/health` with TLS verification disabled (the root isn't trusted yet on the host); injectable `fetchFn`. Network errors / non-`ok` → `false`. Default timeout `STEPCA_HEALTH_TIMEOUT_MS = 60_000`, poll `STEPCA_HEALTH_POLL_INTERVAL_MS = 1000`. - Public helper `issueCert({ commonName, sans?, ttl? }) → { certPem, keyPem, chainPem, serial }` — shells `step ca certificate` against the JWK provisioner inside the container via the injected `execFn` (default `container.exec`). Returns leaf + chain PEMs and the serial. - Errors prefixed `INTEGRATION.STEP_CA.*` via `@suaveplan/error`. Injectable `containerFactory`, `execFn`, and `fetchFn` for unit-testability of the fixture itself (mirrors the garage-s3 contract). - Pre-publish gate: 100% Istanbul coverage per `openspec/AGENTS.md` §7. ### Out of scope — explicitly NOT ported - Caddy / Let's Encrypt public TLS (tests don't need DNS or public certs). - Hono OpenAPI cert-management service (`api/`), the SQLite API-key DB, and bearer-scope auth. - Web download portal (`web/`) and the `step` CLI binary mirror. - ACME provisioner and domain-policy enforcement (`API_ALLOWED_DOMAINS`). The fixture issues leaves directly through the JWK provisioner using `step ca certificate` — no REST hop, no auth layer, no policy enforcement (test code knows what names it wants). ## Capabilities ### New Capabilities - `testing-integration-step-ca` — a `ServiceFixture` for `smallstep/step-ca` (image policy, env-bridge, health-check contract, init+configure sequence, `issueCert()` helper, error code prefix). ## Impact - **Affected code:** new package at `/mnt/s/web/genesis/packages/dev/testing-integration-step-ca/**`. - **Affected consumers:** opt-in. `drive-webrtc`, `transport-webrtc`, `transport-https`, `server-caddy-imgproxy`, and the secure variants of `mailpit` / `garage-s3` fixtures gain a path to real-chain HTTPS. Zero behaviour change for fixtures that don't depend on it. - **Catalog additions (root `package.json`):** none — `testcontainers` and `@suaveplan/testing-integration` are already pinned. - **CLAUDE.md / MANIFEST.md:** add the new package to the `Packages` section in `CLAUDE.md`, then run `bun scripts/folder-reorg/generate-manifest.ts` to refresh `MANIFEST.md`. - **Cross-cutting:** new capability spec only; no existing capability is modified. On archive the spec folds into `openspec/specs/testing-integration-step-ca/spec.md`.