Strategic naming cleanup. Earlier doc-comments + commit messages framed Rank
4 / Rank 5 / Rank 7 work as 'Rank N of the 2026-05-03 Infisical deep-research
deliverable' — the 'Infisical' qualifier was a holdover from the original
deep-research framing where Infisical (a competing secrets-management
platform) was the comparator. Keeping the comparator's name in our source
adds noise without value; an external reader sees 'Infisical' and assumes a
dependency or shared lineage rather than reading it as the competitive
context it was.
Mechanical sed across 34 files (32 source / docs + 2 follow-up Python passes
to collapse 'deep-research deep-research' duplicates that emerged where the
original phrase wrapped across lines):
s|Infisical deep-research|deep-research|g
s|infisical-deep-research-results|deep-research-results-2026-05-03|g
s|infisical-deep-research-prompt|deep-research-prompt-2026-05-03|g
s|infisical-deep-research|deep-research|g
s|Infisical|deep-research|g
s|deep-research deep-research|deep-research|g # collapse-pass
Net diff: 63 insertions / 64 deletions across cmd/, docs/, internal/,
migrations/. Pure text substitution; zero behavior change. Code path
unchanged — go vet clean, tests for TestApproval pass on both
internal/service and internal/api/handler packages.
Workspace docs (cowork/) carry the same references and will be swept
separately — they're not under certctl/ git control. The two filename
references (cowork/infisical-deep-research-results.md +
cowork/infisical-deep-research-prompt.md) get renamed alongside that sweep
to deep-research-results-2026-05-03.md /
deep-research-prompt-2026-05-03.md so cross-references in the certctl
repo doc-comments resolve cleanly.
Closes Rank 7 of the 2026-05-03 Infisical deep-research deliverable
(cowork/infisical-deep-research-results.md Part 5). Pre-fix, certctl
issued certificates unattended — every renewal-loop tick that crossed
a renewal threshold created a Job at Status=Pending which the
scheduler dispatched directly to the issuer connector. PCI-DSS Level
1, FedRAMP Moderate / High, SOC 2 Type II, and HIPAA-regulated PHI
customers all ask the same procurement question: "How do you enforce
two-person integrity on cert issuance?" Today's answer: "We don't."
After this commit chain: "Per-profile RequiresApproval=true creates a
parallel ApprovalRequest row; the renewal-loop creates the Job at
Status=AwaitingApproval; an authorized approver (different from the
requester per the same-actor RBAC check) calls
POST /api/v1/approvals/{id}/approve, transitioning the Job to
Pending; the scheduler picks it up."
This commit (4 of 4) wires the gate into the manual TriggerRenewal
entry point + main.go service construction + Config.Approval +
docs + WORKSPACE-ROADMAP follow-up entries. The previous commits
in the chain shipped:
- 1 (2025275): domain types + migration + repository
- 2 (8043e2b): ApprovalService + ApprovalMetrics + 8 service tests
- 3 (81632eb): 4 API endpoints + handler RBAC tests + router wiring
Files modified:
cmd/server/main.go - Constructs approvalRepo +
approvalMetrics + approvalService
+ approvalHandler. Wires
CertificateService via
SetApprovalService + SetProfileRepo.
Logs a WARN line at boot when
CERTCTL_APPROVAL_BYPASS=true so
production operators alert on the
log line. Adds Approvals to the
HandlerRegistry.
internal/config/config.go - Adds top-level ApprovalConfig
{BypassEnabled bool} sub-config
+ CERTCTL_APPROVAL_BYPASS env var
loader. Doc comment cites the
compliance-detection SQL query
(SELECT count FROM audit_events
WHERE actor='system-bypass') so
auditors find the right pattern.
internal/service/certificate.go - Adds approvalSvc + profileRepo
fields to CertificateService +
SetApprovalService /
SetProfileRepo setters. Extends
TriggerRenewal: looks up the
profile, checks RequiresApproval,
creates the Job at
JobStatusAwaitingApproval (override
the keygen-mode default), then
calls approvalSvc.RequestApproval
to create the parallel
ApprovalRequest row. On
RequestApproval failure, cancels
the orphan Job (defense in depth —
without this, a partial failure
would leave the job stuck at
AwaitingApproval forever). Profile-
lookup failures fall back to the
unattended path (fail-open from
the operator's perspective +
fail-loud via slog.Warn).
Files added:
docs/approval-workflow.md - Sysadmin-grade operator runbook:
end-to-end ASCII flowchart
(operator A triggers → operator
B approves → scheduler dispatches),
configuration recipe, RBAC contract
(the load-bearing two-person
integrity rule), operator playbooks
for "I need to approve a renewal"
and "approval timed out", PCI-DSS
6.4.5 / NIST 800-53 SA-15 / SOC 2
CC6.1 / HIPAA control mapping
table, bypass-mode warnings with
the exact compliance-detection SQL
query, Prometheus metric reference,
future free V2 work pointers.
Out of scope of THIS commit (deferred follow-on, not blocking the rest):
- RenewalService.CheckExpiringCertificates auto-renewal-loop gate.
The manual TriggerRenewal entry point is gated and the job-level
timeout reaper already covers AwaitingApproval; the auto-renewal
gate adds parity. Trivial to add — one block in renewal.go that
mirrors the certificate.go::TriggerRenewal gate. Tracked in
WORKSPACE-ROADMAP under the Approval-workflow extensions section.
- Scheduler reaper extension calling ApprovalService.ExpireStale.
Today: when the existing reaper times out an AwaitingApproval job,
the parallel ApprovalRequest row stays at state=pending. The audit
timeline is still correct (the job-side audit row records the
timeout) but the dashboard shows a row that no longer needs human
review. Trivial to wire — one method call in the existing
scheduler tick. Same WORKSPACE-ROADMAP follow-on.
- api/openapi.yaml extensions for the 4 new operationIds.
The HTTP contract is pinned by the handler-level tests; OpenAPI
is documentation that mirrors the contract.
- docs/connectors.md `requires_approval` row in the CertificateProfile
config table. Tracked in the same follow-on; the new
docs/approval-workflow.md is the canonical reference.
Workspace-level updates (in cowork/, not under certctl/ git control —
applied separately):
WORKSPACE-ROADMAP.md - "Approval-workflow extensions"
section under "Future Free V2 Work"
covering M-of-N chains + time-
windowed auto-approve + external
ticketing + per-owner routing +
delegation. All items free under
BSL — no V3-Pro framing per the
2026-05-03 strategy pivot (open
core under BSL; future revenue =
managed-service hosting).
Verified locally:
gofmt: clean.
go vet ./...: exit 0.
go build ./...: exit 0 — full repo links cleanly with the new
Approval wiring.
go test -short -count=1 -run TestApproval
./internal/service/... ./internal/api/handler/...:
ok 0.005s for both packages — all 11 approval tests green
(8 service-level + 3 handler-level).
Reference: cowork/rank-7-approval-workflow-primitive-prompt.md.
Commits: 2025275 → 8043e2b → 81632eb → THIS COMMIT.