4.7 KiB
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 thetesting-integration-*plugin convention fromCLAUDE.md). - Implements
ServiceFixture<StepCaConfig>from@suaveplan/testing-integrationwith imagesmallstep/step-capinned to a digest. StepCaConfigis{ 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. Passthroughstop()is a no-op. - Container mode: starts step-ca, runs an init+configure sequence equivalent to
../certs/scripts/init-ca.sh+configure-ca.shbut trimmed to the parts a test harness needs:step ca init --deployment-type standalone --name <caName> --dns <dnsNames> --address :8443 --provisioner <provName> --password-file <pwFile>- read
/home/step/certs/root_ca.crt→rootCertPem step certificate fingerprint /home/step/certs/root_ca.crt→fingerprintstep crypto jwk create(EC P-256, ES256, sig) for the JWK provisioner, password-encryptedjqinsert the JWK provisioner intoca.json- write
rootCertPemto a host tmpfile and expose its path asrootCertPath
- Health:
GET https://localhost:<caPort>/healthwith TLS verification disabled (the root isn't trusted yet on the host); injectablefetchFn. Network errors / non-ok→false. Default timeoutSTEPCA_HEALTH_TIMEOUT_MS = 60_000, pollSTEPCA_HEALTH_POLL_INTERVAL_MS = 1000. - Public helper
issueCert({ commonName, sans?, ttl? }) → { certPem, keyPem, chainPem, serial }— shellsstep ca certificateagainst the JWK provisioner inside the container via the injectedexecFn(defaultcontainer.exec). Returns leaf + chain PEMs and the serial. - Errors prefixed
INTEGRATION.STEP_CA.*via@suaveplan/error. InjectablecontainerFactory,execFn, andfetchFnfor 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 thestepCLI 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— aServiceFixtureforsmallstep/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 ofmailpit/garage-s3fixtures gain a path to real-chain HTTPS. Zero behaviour change for fixtures that don't depend on it. - Catalog additions (root
package.json): none —testcontainersand@suaveplan/testing-integrationare already pinned. - CLAUDE.md / MANIFEST.md: add the new package to the
Packagessection inCLAUDE.md, then runbun scripts/folder-reorg/generate-manifest.tsto refreshMANIFEST.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.