CI run #428 (job 74148571711) failed on commit c8eb3e0 with:
cmd/agent/main.go:690:44: Function `createTargetConnector` should pass
the context parameter (contextcheck)
Pre-existing on master since the Rank 5 commits (8a56a78 Azure KV,
edf6bee AWS ACM) added two `case` branches in createTargetConnector
that called `awsacm.New(context.Background(), &cfg, a.logger)` and
`azurekv.New(context.Background(), &cfg, a.logger)` instead of
threading the caller's ctx. The contextcheck linter (in .golangci.yml)
flagged the call site at line 690 because the caller — the deploy
path inside processJob — has a `ctx` in scope (used a few lines later
for `a.reportJobStatus(ctx, ...)`).
Why CI fix#15 (c8eb3e0) didn't catch this: that commit was scoped
narrowly to fix go.mod / go.sum drift after Azure SDK transitive deps
shifted; it didn't run the full lint gate locally because the sandbox
disk-pressure path falls back to gofmt + go vet + go test -short, and
contextcheck is part of golangci-lint (not vet). It surfaced once CI
ran the full lint pipeline.
Fix:
- createTargetConnector signature: prepend `ctx context.Context` as the
first parameter (matches the convention used everywhere else in the
agent — heartbeat, processJob, reportJobStatus, etc.).
- Inside the function, replace both `context.Background()` calls
(AWSACM + AzureKeyVault cases) with `ctx`. SDK credential resolution
now honors caller cancellation / deadlines.
- Update the production call site at cmd/agent/main.go:690 to pass
`ctx` (already in scope).
- Update the 6 test call sites in cmd/agent/agent_test.go to pass
`context.Background()` (test functions don't have a ctx in scope —
Background() is the conventional zero-value for unit tests).
Verified locally:
- gofmt: 0 lines diff
- go vet ./cmd/agent/...: exit 0
- go build ./cmd/agent/...: exit 0
- go test -short ./cmd/agent/...: ok 11.912s
The contextcheck linter itself wasn't re-run locally (golangci-lint
install needs ~300MB and the sandbox modcache + build cache already
filled disk). The fix matches the linter's diagnosis verbatim:
"should pass the context parameter" — call site now passes the
parameter; signature now accepts it.
Closes Rank 5 (Azure half) of the 2026-05-03 Infisical deep-research
deliverable (cowork/infisical-deep-research-results.md Part 5).
Pre-fix, certctl had no path to deploy certs to Azure-managed TLS-
termination endpoints (Application Gateway / Front Door / App Service
/ Container Apps) — operators terminating TLS at Azure had to use
manual `az keyvault certificate import` invocations or external
automation. This commit lands the SDK-driven Azure Key Vault target
connector that closes the gap, mirroring the AWS ACM target shape
shipped in commit edf6bee.
Architecture:
- internal/connector/target/azurekv/azurekv.go — Connector wraps
*azcertificates.Client behind the KeyVaultClient interface seam
(mirrors awsacm's ACMClient + awsacmpca's ACMPCAClient). Lives
in azurekv.go alongside the PFX (PKCS#12) wrapping helper that
bundles the operator-supplied PEM cert + chain + key into the
base64-PFX wire format azcertificates.ImportCertificate accepts.
- internal/connector/target/azurekv/sdk_client.go — SDK-loading
code isolated so the test path (NewWithClient) compiles without
pulling azcore + azidentity transitive deps into the test
binary. DefaultAzureCredential / ManagedIdentityCredential /
EnvironmentCredential / WorkloadIdentityCredential selected via
Config.CredentialMode (closed enum).
- Pre-deploy snapshot via GetCertificate(name, "" /* latest */) so
on-import-failure rollback restores the previous cert. Mirrors
Bundle 5+. The Azure-specific quirk: rollback creates a NEW
VERSION (Key Vault doesn't support version-restore without
soft-delete recovery, which we keep off the minimum-RBAC
surface). Operators reading audit dashboards see e.g. v1=initial,
v2=failed-renewal, v3=rollback-of-v2; the certctl-managed-by +
certctl-certificate-id provenance tags + future certctl-rollback-of
metadata tag let an operator filter rollback artifacts.
- Provenance tags identical to AWS ACM
(certctl-managed-by=certctl + certctl-certificate-id=<mc-id>),
automatically applied on every import. Key Vault carries tags
forward across versions (unlike ACM which strips on re-import),
so no separate AddTags call is required.
- DeploymentRequest.KeyPEM held in agent memory only; PFX wrapping
happens in-memory via software.sslmate.com/src/go-pkcs12. No
disk write.
Tests:
- azurekv_test.go: 13-subtest happy-path + validation matrix —
ValidateConfig (success / missing-vault-url / malformed-vault-
url / missing-cert-name / invalid-credential-mode / reserved-
tag rejection), DeployCertificate (fresh import / rollback-on-
serial-mismatch / empty-key-rejected / no-client-rejected /
SDK-error-surfaced), ValidateOnly (returns sentinel),
ValidateDeployment (serial match / mismatch).
- All tests use the NewWithClient injection seam; no real-Azure
API calls.
- go test -short -count=1 ./internal/connector/target/azurekv/...
green.
Wiring:
- internal/domain/connector.go: TargetTypeAzureKeyVault =
"AzureKeyVault".
- internal/service/target.go: validTargetTypes set extended.
- cmd/agent/main.go::createTargetConnector: AzureKeyVault case
arm mirroring the AWSACM shape exactly.
- cmd/agent/agent_test.go::TestCreateTargetConnector_AllSupported
Types: AzureKeyVault added to the type matrix + the InvalidJSON
matrix (16 supported target types now, up from 15).
go.mod / go.sum:
- github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 (direct).
- github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 (direct).
- github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/
azcertificates v1.4.0 (direct). The deprecated
/keyvault/azcertificates path appears as a transitive indirect
via Microsoft's microsoft-authentication-library-for-go; we use
the new /security/keyvault/ path exclusively.
Documentation:
- docs/connectors.md "Azure Key Vault" section: config table, RBAC
role recipe (off-the-shelf "Key Vault Certificates Officer" or
custom role with 3 data-plane actions), AKS workload-identity /
managed-identity / service-principal / default credential
recipes, atomic-rollback contract + Azure-version semantics
explanation, soft-delete caveat, App Gateway / Front Door
Terraform attachment snippet, threat model carve-outs (no disk
writes, mandatory provenance tags, no long-lived secrets in
Config), 5-bullet procurement checklist crib.
Out of scope (intentional, flagged in V3-Pro forward path):
- Azure Front Door direct-attach (UpdateRoutingConfig — different
Azure RBAC scope).
- App Gateway / App Service auto-bind (V3-Pro auto-attach).
- Soft-delete recovery (acm:RecoverDeletedCertificate-equivalent
requires extra RBAC; V2 keeps minimum-permission surface).
- GCP Certificate Manager (separate cloud, separate connector).
Verified locally:
- gofmt clean.
- go vet ./internal/connector/target/azurekv/...
./internal/domain/... ./internal/service/...
./cmd/agent/... clean.
- go test -short -count=1 ./internal/connector/target/azurekv/...
./cmd/agent/... green (all 16 supported target types
instantiate via the agent factory).
Reference: cowork/infisical-deep-research-results.md Part 5 Rank 5.
Acquisition prompt:
cowork/rank-5-aws-acm-azure-kv-target-adapters-prompt.md.
Companion commit (AWS half): edf6bee.
Closes Rank 5 (AWS half) of the 2026-05-03 Infisical deep-research
deliverable (cowork/infisical-deep-research-results.md Part 5).
Pre-fix, certctl had no path to deploy certs to AWS-managed TLS-
termination endpoints (ALB / CloudFront / API Gateway / App Runner)
— operators terminating TLS at AWS had to use Infisical secret-sync,
manual aws-cli imports, or external automation. This commit lands
the SDK-driven AWS Certificate Manager target connector that closes
the gap end-to-end.
Architecture:
- internal/connector/target/awsacm/awsacm.go — Connector wraps
*acm.Client behind the ACMClient interface seam (mirrors
awsacmpca's ACMPCAClient pattern from the issuer side).
LoadDefaultConfig handles the standard AWS credential chain
(IRSA / EC2 instance profile / SSO / env vars); no embedded
creds in connector Config.
- Pre-deploy snapshot via DescribeCertificate + GetCertificate so
on-import-failure rollback restores the previous cert. Mirrors
the Bundle 5 IIS pattern + the Bundle 7/8 WinCertStore /
JavaKeystore patterns. Surfaces rollback success/failure via
the existing certctl_deploy_rollback_total Prometheus counter
label set.
- Provenance tags: certctl-managed-by=certctl + certctl-
certificate-id=<mc-id> set automatically on every import. ACM
strips tags on re-import, so the connector calls
AddTagsToCertificate post-import to keep the provenance pair
fresh. Operators looking up a cert ARN by managed-cert ID
(Terraform data source, CloudFormation output) match against
these tags.
- DeploymentRequest.KeyPEM held in agent memory only — never
written to disk. Aligns with the pull-only deployment model
documented in CLAUDE.md.
Tests:
- awsacm_test.go: 15-subtest happy-path + validation matrix
covering ValidateConfig (success / missing-region / malformed-
region / malformed-ARN / reserved-tag rejection),
DeployCertificate (fresh import / rotate-in-place / rollback-
on-serial-mismatch / rollback-also-fails / empty-key-rejected /
no-client-rejected), ValidateOnly (returns sentinel),
ValidateDeployment (serial match / mismatch / no-ARN-yet).
- awsacm_failure_test.go: 5 per-error-class contract tests
mirroring the awsacmpca_failure_test.go shape (commit
a2a59a8) — AccessDeniedException (smithy.GenericAPIError),
ResourceNotFoundException (typed), ThrottlingException
(smithy.GenericAPIError, FaultServer preserved),
InvalidArgsException (typed, terminal), RequestInProgress
Exception (typed). All assert errors.As against the SDK type +
operator-actionable substring + connector-side wrap framing.
- Coverage on awsacm.go: 54.9% of statements (matches the K8s-
Secret + IIS connectors' 50-65% range; rollback-failure paths
contribute most of the un-covered surface — those exercise
only when the rollback's SDK call also returns an error).
- go test -race -count=10 green; no goroutine leaks.
Wiring:
- internal/domain/connector.go: TargetTypeAWSACM = "AWSACM".
- internal/service/target.go: validTargetTypes set extended.
- cmd/agent/main.go::createTargetConnector: AWSACM case arm
mirroring the KubernetesSecrets shape exactly. Calls
awsacm.New(context.Background(), &cfg, a.logger) — the
SDK-loading happens here, not lazily, so config errors
surface at agent boot.
- cmd/agent/agent_test.go::TestCreateTargetConnector_AllSupported
Types: AWSACM added to the type matrix + the InvalidJSON
matrix.
go.mod / go.sum:
- github.com/aws/aws-sdk-go-v2/service/acm v1.38.3 (direct).
aws-sdk-go-v2 + service/acmpca + smithy-go were already direct
from the awsacmpca issuer; this is the distribution-side
companion package.
Documentation:
- docs/connectors.md "AWS Certificate Manager (ACM)" section:
config table, IAM policy JSON (5 actions on
arn:aws:acm:*:*:certificate/*), IRSA / EC2 instance-profile /
SSO auth recipes, atomic-rollback contract, Terraform ALB-
attachment snippet, threat model carve-outs (no disk writes,
mandatory provenance tags, no long-lived creds in Config),
procurement checklist crib (5 bullets paste-able into a
security review).
Out of scope (intentional, flagged in V3-Pro forward path):
- CloudFront / ALB auto-attach (UpdateDistribution requires a
different IAM scope than ACM ImportCertificate).
- Cross-region ACM replication (ACM is regional; CloudFront
forces us-east-1).
- Tag-filtered ARN discovery (V2 uses operator-pinned
Config.CertificateArn after first deploy; tag-scan path
requires acm:ListTagsForCertificate which we deliberately
keep off the minimum-IAM-policy surface).
- Azure Key Vault (separate cloud, separate connector — Azure
half of Rank 5 ships in a follow-on commit).
Verified locally:
- gofmt clean.
- go vet ./internal/connector/target/awsacm/...
./internal/domain/... ./internal/service/...
./cmd/agent/... clean.
- go test -short -count=1 ./internal/connector/target/awsacm/...
./internal/domain/... ./cmd/agent/... green (15 + 5 awsacm
subtests; all 15 supported target types instantiate via the
agent factory).
- go test -race -count=10 ./internal/connector/target/awsacm/...
green.
Reference: cowork/infisical-deep-research-results.md Part 5 Rank 5.
Acquisition prompt:
cowork/rank-5-aws-acm-azure-kv-target-adapters-prompt.md.
Mechanical reformat. The new 'gofmt drift' CI step (added in
ci-pipeline-cleanup Phase 4, commit 0f205a8) surfaced 111 files
with accumulated gofmt drift across cmd/, internal/, and deploy/test/.
Each file's diff is gofmt-standard: whitespace adjustments, intra-
group import sorting (alphabetical by import path within blank-line-
separated groups), and struct-tag column alignment. No semantic
changes — verified via 'git diff --ignore-all-space' which shows only
the line-position deltas from import reordering.
The gate stays in place after this commit. Going forward it catches
gofmt drift at PR time.
Breaking change release. Plaintext HTTP listener removed. The certctl
control plane now terminates TLS 1.3 on :8443 via
http.Server.ListenAndServeTLS. No CERTCTL_TLS_ENABLED=false escape
hatch. No dual-listener mode. One-step cutover per docs/upgrade-to-tls.md.
Server
- cmd/server/tls.go: certHolder with SIGHUP hot-reload + atomic cert
swap, buildServerTLSConfig (TLS 1.3 min, GetCertificate callback),
preflightServerTLS validation
- cmd/server/main.go: ListenAndServeTLS in place of ListenAndServe,
watchSIGHUP wiring, cert/key path config threading
- tls_test.go: 418-line regression coverage of reload, preflight,
callback behavior, SAN validation
Config
- CERTCTL_TLS_CERT_PATH / CERTCTL_TLS_KEY_PATH (required)
- Plaintext rejection: agents/CLI/MCP pre-flight-fail on http://
URLs with a pointer to docs/upgrade-to-tls.md
Agents, CLI, MCP
- All three pre-flight-reject http:// URLs with fail-loud diagnostic
- CERTCTL_SERVER_CA_BUNDLE_PATH for private-CA trust
- CERTCTL_SERVER_TLS_INSECURE_SKIP_VERIFY for dev-only bypass
(loud warning on startup)
- install-agent.sh emits both vars as commented template lines
docker-compose
- certctl-tls-init sidecar generates SAN-valid self-signed cert into
deploy/test/certs/ on first boot
- All demo-stack curls pin against ca.crt with --cacert
Helm chart
- Three TLS provisioning modes, exactly one required:
- server.tls.existingSecret (operator-supplied)
- server.tls.certManager.enabled (cert-manager integration)
- server.tls.selfSigned.enabled (eval only — not for production)
- server-certificate.yaml template for cert-manager mode
- helm install without a TLS source fails at template render with
a pointer to docs/tls.md
CI
- .github/workflows/ci.yml Helm Chart Validation step renders the
chart in both existingSecret and cert-manager modes, plus an
inverse guard-regression test that asserts helm template MUST
refuse to render when no TLS source is configured. Previously
the single `helm template` invocation hit the certctl.tls.required
fail-loud guard and exit-1'd CI. Four invocations now: lint
(existingSecret), template (existingSecret), template
(cert-manager), template (no args — must fail).
Integration tests
- deploy/test/integration_test.go stands up the Compose stack over
HTTPS, extracts the CA bundle, and exercises every certctl API
over https://localhost:8443
- All 34 integration subtests green (per Phase 8 local CI-parity)
Documentation
- New: docs/tls.md (provisioning patterns, rotation, SIGHUP reload)
- New: docs/upgrade-to-tls.md (one-step cutover, no-downgrade
warnings, fleet-roll sequencing)
- CHANGELOG.md: v2.2.0 "HTTPS Everywhere — The Irony" entry
(file heading unchanged; release tag is v2.0.47)
- All curls in docs/, examples/, deploy/helm/ guides use
https://localhost:8443 --cacert
Verification
- grep -rn "ListenAndServe[^T]" cmd/ internal/ → 0 hits
- grep -rn "\"http://" cmd/ internal/ → 2 benign hits (Caddy admin
API default, SSRF doc comment) — zero certctl endpoints
- Tasks #197–#206 (Phases 0–8) all closed in the tracker
Files: 65 changed, 3489 insertions, 372 deletions (pre-CI-fix).
Close coverage gaps identified by dual-audit (qualitative + quantitative).
New test files for config (0%→98%), router (0%→100%), handler validation,
health, audit, response helpers, webhook notifier (0%→88%), email notifier,
middleware (recovery, rate limiter), domain profile, service nil-safety,
config helpers, issuer bootstrap, and server bootstrap wiring. Expanded
existing tests for ACME (34%→42%), step-ca (42%→52%), F5, SSH, agent
(43%→63%), scheduler (88%→99%), renewal service, and issuerfactory.
All tests pass: go test -short, go vet, go test -race clean.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>