Collapse CancelJobWithContext into CancelJob; eliminate 10 context.Background()
hits across the Job+Notification+Audit service cluster by threading ctx
through their handler-facing service interfaces.
Services (ctx-first):
- service/job.go: ListJobs, GetJob, CancelJob, ApproveJob, RejectJob now
accept ctx; the CancelJobWithContext wrapper is removed (handler callers
continue to invoke CancelJob, now ctx-aware).
- service/notification.go: ListNotifications, GetNotification, MarkAsRead
accept ctx.
- service/audit.go: ListAuditEvents, GetAuditEvent accept ctx.
Handlers (interface + callsites):
- handler/jobs.go, handler/notifications.go, handler/audit.go: local
service interfaces updated, r.Context() threaded at every callsite.
Tests:
- Mock services updated to match the new interfaces (ctx accepted and
ignored via '_ context.Context' first parameter; Fn closure fields
unchanged).
- job_test.go / notification_test.go callsites thread context.Background()
to match production shape.
Verification:
go build ./... ok
go vet ./... ok
go test -short ./... ok
go test -race -short ./... ok
golangci-lint run ./... 0 issues
Locked decisions from the M-2 plan:
D-1 ctx-only signatures (no dual forms)
D-4 preserve handler method names facing the router
D-5 domain types stay ctx-free
Audit complete. Commit: 855124a9d9. Sections: 12. Findings: 2/7/10/4/6.
Replace static env-var-based issuer wiring with GUI-driven dynamic
configuration stored encrypted in PostgreSQL. Operators can now
configure, test, enable/disable, and manage issuers from the dashboard
without restarting the server.
Key changes:
- AES-256-GCM encryption for sensitive issuer config at rest (PBKDF2
key derivation with 100k iterations)
- Dynamic IssuerRegistry with sync.RWMutex replacing static map
- Connector factory pattern (issuerfactory.NewFromConfig) replacing
140 lines of static wiring in main.go
- Migration 000009: encrypted_config, last_tested_at, test_status,
source columns on issuers table
- Env var seeding on first boot with ON CONFLICT DO NOTHING
- Registry Rebuild() for atomic map swap after CRUD operations
- Issuer type validation against domain constants on Create
- Audit trail for test connection results
- Conditional seeding for step-ca/OpenSSL (only when env vars set)
- GUI: source badge, connection test status on issuer detail page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Private keys never leave agent infrastructure. Agents generate ECDSA P-256
key pairs locally, store them with 0600 permissions, and submit only the CSR
(public key) to the control plane. New AwaitingCSR job state pauses
renewal/issuance jobs until the agent submits its CSR. Server-side keygen
retained behind CERTCTL_KEYGEN_MODE=server for demo/development.
Key changes:
- Dual keygen mode via CERTCTL_KEYGEN_MODE (agent default, server for demo)
- AwaitingCSR job state with CommonName/SANs in work response
- Agent ECDSA P-256 keygen, local key storage, CSR-only submission
- CompleteAgentCSRRenewal server-side flow for agent-submitted CSRs
- DeploymentRequest.KeyPEM for agent-provided keys during deployment
- Dockerfile.agent creates /var/lib/certctl/keys with correct ownership
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Service layer (63 tests): certificate, agent, audit, job, notification,
policy, and renewal services with mock repositories covering threshold
alerting, deduplication, status transitions, and job processing.
Handler layer (46 tests): certificate and agent HTTP handlers using
httptest with mock service interfaces, covering success/error paths,
pagination, JSON marshaling, and path parameter extraction.
Integration (11 subtests): end-to-end certificate lifecycle test
exercising real services and Local CA issuer through HTTP API —
create cert, trigger renewal, process jobs, register agent, heartbeat,
verify audit trail.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>